Skip to content
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
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 <[email protected]>"]
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
Loading