Skip to content

Commit

Permalink
Merge pull request #1 from CliMA/ck/add_impl
Browse files Browse the repository at this point in the history
Add implementation
  • Loading branch information
charleskawczynski authored Jan 31, 2025
2 parents 830f7fc + 4dacc6e commit d35d52a
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 18 deletions.
12 changes: 10 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
name = "AbsentTypes"
uuid = "806a83c1-d09f-449d-ab1d-4e49ad4cc0e9"
uuid = "0d71be07-595a-4f89-9529-4065a4ab43a6"
authors = ["CliMA Contributors <clima-software@caltech.edu>"]
version = "0.1.0"

[compat]
Aqua = "0.8"
Test = "1"
LazyBroadcast = "1"
julia = "1.8"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
LazyBroadcast = "9dccce8e-a116-406d-9fcc-a88ed4f510c8"

[targets]
test = ["Test"]
test = ["Test", "Aqua", "LazyBroadcast"]
2 changes: 1 addition & 1 deletion docs/Project.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[deps]
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
DocumenterCitations = "daee34ce-89f3-4625-b898-19384cb65244"
AbsentTypes = "806a83c1-d09f-449d-ab1d-4e49ad4cc0e9"
AbsentTypes = "0d71be07-595a-4f89-9529-4065a4ab43a6"
13 changes: 2 additions & 11 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,7 @@ import AbsentTypes
bib = DocumenterCitations.CitationBibliography(joinpath(@__DIR__, "refs.bib"))

mathengine = Documenter.MathJax(
Dict(
:TeX => Dict(
:equationNumbers => Dict(:autoNumber => "AMS"),
:Macros => Dict(),
),
),
Dict(:TeX => Dict(:equationNumbers => Dict(:autoNumber => "AMS"), :Macros => Dict())),
)

format = Documenter.HTML(
Expand All @@ -26,11 +21,7 @@ Documenter.makedocs(;
clean = true,
doctest = true,
modules = [AbsentTypes],
pages = Any[
"Home" => "index.md",
"API" => "api.md",
"References" => "references.md",
],
pages = Any["Home"=>"index.md", "API"=>"api.md", "References"=>"references.md"],
)

Documenter.deploydocs(
Expand Down
4 changes: 4 additions & 0 deletions docs/src/references.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# References

```@bibliography
```
73 changes: 72 additions & 1 deletion src/AbsentTypes.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,76 @@
module AbsentTypes

greet() = print("Hello World!")
"""
Absent()
A `Base.AbstractBroadcasted` that represents arithmetic object.
An `Absent()` can be added to, subtracted from, or multiplied by any value in a
broadcast expression without incurring a runtime performance penalty.
For example, the following rules hold when broadcasting instances of `Absent`:
```
1 + Absent() == 1
Absent() + 1 == 1
1 - Absent() == 1
1 * Absent() == Absent()
1 / Absent() == Absent()
```
"""
struct Absent <: Base.AbstractBroadcasted end
Base.broadcastable(x::Absent) = x

struct AbsentStyle <: Base.BroadcastStyle end
Base.BroadcastStyle(::Type{<:Absent}) = Absent()

# Specialize on AbstractArrayStyle to avoid ambiguities with AbstractBroadcasted.
Base.BroadcastStyle(::Absent, ::Base.Broadcast.AbstractArrayStyle) = Absent()
Base.BroadcastStyle(::Base.Broadcast.AbstractArrayStyle, ::Absent) = Absent()

# Add another method to avoid ambiguity between the previous two.
Base.BroadcastStyle(::Absent, ::Absent) = Absent()

broadcasted_sum(args) =
if isempty(args)
Absent()
elseif length(args) == 1
args[1]
else
Base.broadcasted(+, args...)
end
Base.broadcasted(::Absent, ::typeof(+), args...) =
broadcasted_sum(filter(arg -> !(arg isa Absent), args))

Base.broadcasted(op::typeof(-), ::Absent, arg) = Base.broadcasted(op, arg)
Base.broadcasted(op::typeof(-), arg, ::Absent) = Base.broadcasted(Base.identity, arg)
Base.broadcasted(op::typeof(-), a::Absent) = Absent()
Base.broadcasted(op::typeof(-), a::Absent, ::Absent) = Base.broadcasted(op, a)

Base.broadcasted(op::typeof(+), ::Absent, args...) = Base.broadcasted(op, args...)
Base.broadcasted(op::typeof(+), arg, ::Absent) = Base.broadcasted(op, arg)
Base.broadcasted(op::typeof(+), a::Absent, ::Absent) = Base.broadcasted(op, a)

Base.broadcasted(op::typeof(*), ::Absent, args...) = Absent()
Base.broadcasted(op::typeof(*), arg, ::Absent) = Absent()
Base.broadcasted(op::typeof(*), ::Absent, ::Absent) = Absent()
Base.broadcasted(op::typeof(/), ::Absent, args...) = Absent()
Base.broadcasted(op::typeof(/), arg, ::Absent) = Absent()
Base.broadcasted(op::typeof(/), ::Absent, ::Absent) = Absent()

function skip_materialize(dest, bc::Base.Broadcast.Broadcasted)
if typeof(bc.f) <: typeof(+) || typeof(bc.f) <: typeof(-)
if length(bc.args) == 2 &&
bc.args[1] === dest &&
bc.args[2] === Base.Broadcast.Broadcasted(Absent, ())
return true
else
return false
end
else
return false
end
end

Base.Broadcast.instantiate(bc::Base.Broadcast.Broadcasted{AbsentStyle}) = x

end # module AbsentTypes
62 changes: 59 additions & 3 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,11 +1,67 @@
#=
julia --project
using Revise; using TestEnv; TestEnv.activat(); include("test/runtests.jl")
=#
using Test
using AbsentTypes
using AbsentTypes: Absent
using Aqua
using LazyBroadcast: lazy
import Base.Broadcast: instantiate, materialize, Broadcasted, DefaultArrayStyle

@testset "AbsentTypes" begin
@test 1 == 1
@testset "Absent" begin
x = [1]
a = Absent()
@test typeof(lazy.(x .+ a)) <: Broadcasted{
DefaultArrayStyle{1},
Tuple{Base.OneTo{Int64}},
typeof(+),
Tuple{Vector{Int64}},
}
@test typeof(lazy.(a .+ x)) <: Broadcasted{
DefaultArrayStyle{1},
Tuple{Base.OneTo{Int64}},
typeof(+),
Tuple{Vector{Int64}},
}
@test lazy.(a .* x) isa Absent
@test lazy.(a ./ x) isa Absent

# +
@test materialize(lazy.(a .+ x .+ 1)) == [2]
@test materialize(lazy.(a .+ 1 .+ x)) == [2]
@test materialize(lazy.(1 .+ a .+ x)) == [2]
@test materialize(lazy.(1 .+ x .+ a)) == [2]

# -
@test materialize(lazy.(a .- x .- 1)) == [-2]
@test materialize(lazy.(a .- 1 .- x)) == [-2]
@test materialize(lazy.(1 .- a .- x)) == [0]
@test materialize(lazy.(1 .- x .- a)) == [0]
@test materialize(lazy.(a .- a)) == Absent()
@test materialize(lazy.(1 .- 1 .+ a .- a)) == 0
@test materialize(lazy.(x .- x .+ a .- a)) == [0]

# *
@test materialize(lazy.(a .* x .* 1)) == Absent()
@test materialize(lazy.(a .* 1 .* x)) == Absent()
@test materialize(lazy.(1 .* a .* x)) == Absent()
@test materialize(lazy.(1 .* x .* a)) == Absent()

# /
@test materialize(lazy.(a ./ x ./ 1)) == Absent()
@test materialize(lazy.(a ./ 1 ./ x)) == Absent()
@test materialize(lazy.(1 ./ a ./ x)) == Absent()
@test materialize(lazy.(1 ./ x ./ a)) == Absent()

@test_throws MethodError Absent() + 1
@test_throws MethodError Absent() - 1
@test_throws MethodError Absent() * 1
@test_throws MethodError Absent() / 1

@test materialize(Absent()) isa Absent
end

@testset "Aqua" begin
@test Aqua.test_all(AbsentTypes)
Aqua.test_all(AbsentTypes)
end

0 comments on commit d35d52a

Please sign in to comment.