Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add operator_matrix to MatrixFields, along with tests and docs #1399

Merged
merged 1 commit into from
Aug 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .buildkite/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,17 @@ steps:
slurm_gpus: 1
slurm_mem: 40GB

- label: "Unit: operator matrices (CPU)"
key: unit_operator_matrices_cpu
command: "julia --color=yes --check-bounds=yes --project=test test/MatrixFields/operator_matrices.jl"

- label: "Unit: operator matrices (GPU)"
key: unit_operator_matrices_gpu
command: "julia --color=yes --project=test test/MatrixFields/operator_matrices.jl"
agents:
slurm_gpus: 1
slurm_mem: 40GB

- group: "Unit: Hypsography"
steps:

Expand Down
8 changes: 8 additions & 0 deletions docs/src/matrix_fields.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ BandMatrixRow
MultiplyColumnwiseBandMatrixField
```

## Operator Matrices

```@docs
operator_matrix
```

## Internals

```@docs
Expand All @@ -31,6 +37,8 @@ mul_return_type
rmul_return_type
matrix_shape
column_axes
AbstractLazyOperator
replace_lazy_operator
```

## Utilities
Expand Down
6 changes: 6 additions & 0 deletions src/MatrixFields/MatrixFields.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,16 @@ export DiagonalMatrixRow,
QuaddiagonalMatrixRow,
PentadiagonalMatrixRow

# Types that are teated as single values when using matrix fields.
const SingleValue =
Union{Number, Geometry.AxisTensor, Geometry.AdjointAxisTensor}

include("band_matrix_row.jl")
include("rmul_with_projection.jl")
include("matrix_shape.jl")
include("matrix_multiplication.jl")
include("lazy_operators.jl")
include("operator_matrices.jl")
include("field2arrays.jl")

const ColumnwiseBandMatrixField{V, S} = Fields.Field{
Expand Down
4 changes: 2 additions & 2 deletions src/MatrixFields/band_matrix_row.jl
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,9 @@ Base.:-(row1::BandMatrixRow, row2::UniformScaling) =
Base.:-(row1::UniformScaling, row2::BandMatrixRow) =
map(rsub, promote(row1, row2)...)

Base.:*(row::BandMatrixRow, value::Number) =
Base.:*(row::BandMatrixRow, value::SingleValue) =
map(entry -> rmul(entry, value), row)
Base.:*(value::Number, row::BandMatrixRow) =
Base.:*(value::SingleValue, row::BandMatrixRow) =
map(entry -> rmul(value, entry), row)

Base.:/(row::BandMatrixRow, value::Number) =
Expand Down
121 changes: 121 additions & 0 deletions src/MatrixFields/lazy_operators.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
"""
AbstractLazyOperator

Supertype for "lazy operators", i.e., operators that can be called without any
arguments by users, as long as they appear in broadcast expressions that contain
at least one `Field`. If `lazy_op` is an `AbstractLazyOperator`, the expression
`lazy_op.()` will internally be translated to `non_lazy_op.(fields...)`, as long
as it appears in a broadcast expression with at least one `Field`. This
translation is done by the function `replace_lazy_operator(space, lazy_op)`,
which must be implemented by every subtype of `AbstractLazyOperator`.
"""
abstract type AbstractLazyOperator end

struct LazyOperatorStyle <: Base.Broadcast.BroadcastStyle end

Base.Broadcast.broadcasted(op::AbstractLazyOperator) =
Base.Broadcast.broadcasted(LazyOperatorStyle(), op)

# Broadcasting over an AbstractLazyOperator and either a Ref, a Tuple, a Field,
# an Operator, or another AbstractLazyOperator involves using LazyOperatorStyle.
Base.Broadcast.BroadcastStyle(
::LazyOperatorStyle,
::Union{
Base.Broadcast.AbstractArrayStyle{0},
Base.Broadcast.Style{Tuple},
Fields.AbstractFieldStyle,
LazyOperatorStyle,
},
) = LazyOperatorStyle()

struct LazyOperatorBroadcasted{F, A} <:
Operators.OperatorBroadcasted{LazyOperatorStyle}
f::F
args::A
end

# TODO: This definition of Base.Broadcast.broadcasted results in 2 additional
# method invalidations when using Julia 1.8.5. However, if we were to delete it,
# we would also need to replace the following specializations on
# LazyOperatorBroadcasted with specializations on Base.Broadcast.Broadcasted.
# Specifically, we would need to modify Base.Broadcast.materialize so that it
# specializes on Base.Broadcast.Broadcasted{LazyOperatorStyle}, and this would
# result in 11 invalidations instead of 2.
Base.Broadcast.broadcasted(::LazyOperatorStyle, f::F, args...) where {F} =
LazyOperatorBroadcasted(f, args)

function Base.Broadcast.materialize(bc::LazyOperatorBroadcasted)
space = largest_space(bc)
isnothing(space) && error("Cannot materialize broadcast expression with \
AbstractLazyOperator because it does not contain any Fields")
return Base.Broadcast.materialize(replace_lazy_operators(space, bc))
end

Base.Broadcast.materialize!(dest::Fields.Field, bc::LazyOperatorBroadcasted) =
Base.Broadcast.materialize!(dest, replace_lazy_operators(axes(dest), bc))

replace_lazy_operators(_, arg) = arg
replace_lazy_operators(space, bc::LazyOperatorBroadcasted) =
bc.f isa AbstractLazyOperator ? replace_lazy_operator(space, bc.f) :
Base.Broadcast.broadcasted(
bc.f,
replace_lazy_operators_args(space, bc.args...)...,
)

replace_lazy_operators_args(_) = ()
replace_lazy_operators_args(space, arg, args...) = (
replace_lazy_operators(space, arg),
replace_lazy_operators_args(space, args...)...,
)

"""
replace_lazy_operator(space, lazy_op)

Generates an instance of `Base.AbstractBroadcasted` that corresponds to the
expression `lazy_op.()`, where the broadcast in which this expression appears is
being evaluated on the given `space`. Note that the staggering (`CellCenter` or
`CellFace`) of this `space` depends on the specifics of the broadcast and is not
predetermined.
"""
replace_lazy_operator(_, ::AbstractLazyOperator) =
error("Every subtype of AbstractLazyOperator must implement a method for
replace_lazy_operator(space, lazy_op)")

largest_space(_) = nothing
largest_space(field::Fields.Field) = axes(field)
largest_space(bc::Base.AbstractBroadcasted) = largest_space_args(bc.args...)

largest_space_args() = nothing
largest_space_args(arg, args...) =
larger_space(largest_space(arg), largest_space_args(args...))

larger_space(::Nothing, ::Nothing) = nothing
larger_space(space1, ::Nothing) = space1
larger_space(::Nothing, space2) = space2
larger_space(space1::S, ::S) where {S} = space1 # Neither space is larger.
larger_space(
space1::Spaces.FiniteDifferenceSpace,
::Spaces.FiniteDifferenceSpace,
) = space1 # The staggering does not matter here, so neither space is larger.
larger_space(
space1::Spaces.ExtrudedFiniteDifferenceSpace,
::Spaces.ExtrudedFiniteDifferenceSpace,
) = space1 # The staggering does not matter here, so neither space is larger.
larger_space(
space1::Spaces.ExtrudedFiniteDifferenceSpace,
::Spaces.FiniteDifferenceSpace,
) = space1 # The types indicate that space2 is a subspace of space1.
larger_space(
::Spaces.FiniteDifferenceSpace,
space2::Spaces.ExtrudedFiniteDifferenceSpace,
) = space2 # The types indicate that space1 is a subspace of space2.
larger_space(
space1::Spaces.ExtrudedFiniteDifferenceSpace,
::Spaces.AbstractSpectralElementSpace,
) = space1 # The types indicate that space2 is a subspace of space1.
larger_space(
::Spaces.AbstractSpectralElementSpace,
space2::Spaces.ExtrudedFiniteDifferenceSpace,
) = space2 # The types indicate that space1 is a subspace of space2.
larger_space(::S1, ::S2) where {S1, S2} =
error("Mismatched spaces ($(S1.name.name) and $(S2.name.name))")
Loading