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 AbstractRealQuantity #85

Merged
merged 39 commits into from
Nov 23, 2023
Merged
Changes from 1 commit
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
f4b8112
[WIP] Trying to add RealQuantity
MilesCranmer Nov 12, 2023
af8815f
`isapprox` should throw error on mismatched dimensions
MilesCranmer Nov 19, 2023
c811845
Fix isapprox usage in tests
MilesCranmer Nov 19, 2023
54bb106
Generalize comparison operators
MilesCranmer Nov 19, 2023
adee292
Clean up more ambiguities
MilesCranmer Nov 19, 2023
d4cda51
Get simple complex operations working for RealQuantity
MilesCranmer Nov 19, 2023
9424a22
More ambiguities found by Aqua
MilesCranmer Nov 19, 2023
8f844a2
Get majority of tests working with RealQuantity
MilesCranmer Nov 19, 2023
27247a2
Fix scitypes test
MilesCranmer Nov 19, 2023
7bfbe2e
Fix conversion to base type
MilesCranmer Nov 19, 2023
0e41261
Change back to `eltype`
MilesCranmer Nov 19, 2023
4667128
Refactor some promotion rules
MilesCranmer Nov 19, 2023
745cd9e
No need to redefine `identity`
MilesCranmer Nov 21, 2023
79bfd78
Fix ambiguity in `div`
MilesCranmer Nov 21, 2023
037b6f2
Refactor promotion rules
MilesCranmer Nov 21, 2023
e90d046
Test all ambiguities
MilesCranmer Nov 21, 2023
814fa7e
Help coveralls see coverage
MilesCranmer Nov 21, 2023
d84bb6a
Reduce unnecessary methods
MilesCranmer Nov 21, 2023
664b188
Improve coverage
MilesCranmer Nov 21, 2023
c643ea6
Back to `Quantity` as default
MilesCranmer Nov 21, 2023
72d19eb
Cleanup docs
MilesCranmer Nov 21, 2023
75fa9e0
Fix coverage
MilesCranmer Nov 21, 2023
bc1a90b
Export `AbstractRealQuantity`
MilesCranmer Nov 21, 2023
2694747
Proper definition of `mod` and `rem`
MilesCranmer Nov 21, 2023
8c24478
Clean up conversion to numbers
MilesCranmer Nov 21, 2023
807bd21
Refactor div definition
MilesCranmer Nov 23, 2023
7d9aad8
Add unittest for ranges
MilesCranmer Nov 23, 2023
8eb83f7
Use `===` instead of `==`
MilesCranmer Nov 23, 2023
88ea317
Refactor disambiguities
MilesCranmer Nov 23, 2023
5b2b067
Split up `rem`/`mod` definitions
MilesCranmer Nov 23, 2023
cd339b6
Reduce invalidations
MilesCranmer Nov 23, 2023
4786b65
Expand measurements testing
MilesCranmer Nov 23, 2023
466c907
Clean up method invalidations
MilesCranmer Nov 23, 2023
b05cf1d
Delete useless `div` methods
MilesCranmer Nov 23, 2023
80cd5d3
Refactor definition of `mod`
MilesCranmer Nov 23, 2023
7999158
Improve test coverage
MilesCranmer Nov 23, 2023
50e3f2d
Refactor disambiguities
MilesCranmer Nov 23, 2023
58e6bf3
Fix complex ambiguity
MilesCranmer Nov 23, 2023
0609463
Avoid `rem` tests on earlier Julia versions
MilesCranmer Nov 23, 2023
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
Prev Previous commit
Next Next commit
Refactor promotion rules
MilesCranmer committed Nov 21, 2023

Verified

This commit was signed with the committer’s verified signature.
MilesCranmer Miles Cranmer
commit 037b6f289a3a76a2e8e38da92388cf0f6201e3c3
12 changes: 1 addition & 11 deletions src/types.jl
Original file line number Diff line number Diff line change
@@ -197,16 +197,6 @@ const ABSTRACT_QUANTITY_TYPES = (
(AbstractRealQuantity, Real, RealQuantity)
)

"""
promote_quantity(::Type{<:UnionAbstractQuantity}, t::Type{<:Any})
Find the next quantity type in the hierarchy that can accommodate the type `t`.
If the current quantity type can already accommodate `t`, then the current type is returned.
"""
promote_quantity(::Type{<:Union{GenericQuantity,Quantity,RealQuantity}}, ::Type{<:Any}) = GenericQuantity
promote_quantity(::Type{<:Union{Quantity,RealQuantity}}, ::Type{<:Number}) = Quantity
promote_quantity(::Type{<:RealQuantity}, ::Type{<:Real}) = RealQuantity
promote_quantity(T, _) = T

for (type, base_type, _) in ABSTRACT_QUANTITY_TYPES
@eval begin
@@ -230,7 +220,7 @@ const DEFAULT_QUANTITY_TYPE = RealQuantity{DEFAULT_VALUE_TYPE, DEFAULT_DIM_TYPE}
return constructorof(D)(dims...)
end
@inline function new_quantity(::Type{Q}, val, dims) where {Q<:UnionAbstractQuantity}
Qout = promote_quantity(Q, typeof(val))
Qout = promote_quantity_on_value(Q, typeof(val))
return constructorof(Qout)(val, dims)
end

71 changes: 46 additions & 25 deletions src/utils.jl
Original file line number Diff line number Diff line change
@@ -35,30 +35,51 @@ end
function Base.promote_rule(::Type{Dimensions{R1}}, ::Type{Dimensions{R2}}) where {R1,R2}
return Dimensions{promote_type(R1,R2)}
end
for (_, _, concrete_type) in ABSTRACT_QUANTITY_TYPES
@eval function Base.promote_rule(::Type{<:$concrete_type{T1,D1}}, ::Type{<:$concrete_type{T2,D2}}) where {T1,T2,D1,D2}
return $concrete_type{promote_type(T1,T2),promote_type(D1,D2)}

# Define all the quantity x quantity promotion rules
"""
promote_quantity_on_value(Q::Type, T::Type)
Find the next quantity type in the hierarchy that can accommodate the type `T`.
If the current quantity type can already accommodate `T`, then the current type is returned.
For example, `promote_quantity_on_value(Quantity, Float64)` would return `Quantity`, and
`promote_quantity_on_value(RealQuantity, String)` would return `GenericQuantity`.
The user should overload this function to define a custom type hierarchy.
Also see `promote_quantity_on_quantity`.
"""
@inline promote_quantity_on_value(::Type{<:Union{GenericQuantity,Quantity,RealQuantity}}, ::Type{<:Any}) = GenericQuantity
@inline promote_quantity_on_value(::Type{<:Union{Quantity,RealQuantity}}, ::Type{<:Number}) = Quantity
@inline promote_quantity_on_value(::Type{<:RealQuantity}, ::Type{<:Real}) = RealQuantity
@inline promote_quantity_on_value(T, _) = T

"""
promote_quantity_on_quantity(Q1, Q2)
Defines the type hierarchy for quantities, returning the most specific type
that is compatible with both input quantity types. For example,
`promote_quantity_on_quantity(Quantity, GenericQuantity)` would return `GenericQuantity`,
as it can store both `Quantity` and `GenericQuantity` values.
Similarly, `promote_quantity_on_quantity(RealQuantity, RealQuantity)` would return `RealQuantity`,
as that is the most specific type.
Also see `promote_quantity_on_value`.
"""
@inline promote_quantity_on_quantity(::Type{<:Union{GenericQuantity,Quantity,RealQuantity}}, ::Type{<:Union{GenericQuantity,Quantity,RealQuantity}}) = GenericQuantity
@inline promote_quantity_on_quantity(::Type{<:Union{Quantity,RealQuantity}}, ::Type{<:Union{Quantity,RealQuantity}}) = Quantity
@inline promote_quantity_on_quantity(::Type{<:RealQuantity}, ::Type{<:RealQuantity}) = RealQuantity
@inline promote_quantity_on_quantity(::Type{Q}, ::Type{Q}) where {Q<:UnionAbstractQuantity} = Q

for (type1, _, _) in ABSTRACT_QUANTITY_TYPES, (type2, _, _) in ABSTRACT_QUANTITY_TYPES
@eval function Base.promote_rule(::Type{Q1}, ::Type{Q2}) where {T1,T2,D1,D2,Q1<:$type1{T1,D1},Q2<:$type2{T2,D2}}
return with_type_parameters(
promote_quantity_on_quantity(Q1, Q2),
promote_type(T1, T2),
promote_type(D1, D2),
)
end
end

function Base.promote_rule(::Type{<:Quantity{T1,D1}}, ::Type{<:GenericQuantity{T2,D2}}) where {T1,T2,D1,D2}
return GenericQuantity{promote_type(T1,T2),promote_type(D1,D2)}
end
function Base.promote_rule(::Type{<:GenericQuantity{T1,D1}}, ::Type{<:Quantity{T2,D2}}) where {T1,T2,D1,D2}
return GenericQuantity{promote_type(T1,T2),promote_type(D1,D2)}
end
function Base.promote_rule(::Type{<:GenericQuantity{T1,D1}}, ::Type{<:RealQuantity{T2,D2}}) where {T1,T2,D1,D2}
return GenericQuantity{promote_type(T1,T2),promote_type(D1,D2)}
end
function Base.promote_rule(::Type{<:RealQuantity{T1,D1}}, ::Type{<:GenericQuantity{T2,D2}}) where {T1,T2,D1,D2}
return GenericQuantity{promote_type(T1,T2),promote_type(D1,D2)}
end
function Base.promote_rule(::Type{<:Quantity{T1,D1}}, ::Type{<:RealQuantity{T2,D2}}) where {T1,T2,D1,D2}
return Quantity{promote_type(T1,T2),promote_type(D1,D2)}
end
function Base.promote_rule(::Type{<:RealQuantity{T1,D1}}, ::Type{<:Quantity{T2,D2}}) where {T1,T2,D1,D2}
return Quantity{promote_type(T1,T2),promote_type(D1,D2)}
end

# Define promotion rules for all basic numeric types, individually.
# We don't want to define an opinionated promotion on <:Number,
@@ -84,10 +105,10 @@ for (type, _, _) in ABSTRACT_QUANTITY_TYPES
return new_quantity(Q, convert(T, x), D())
end
function Base.promote_rule(::Type{Q}, ::Type{T2}) where {T,D,Q<:$type{T,D},T2<:BASE_NUMERIC_TYPES}
return with_type_parameters(promote_quantity(Q, T2), promote_type(T, T2), D)
return with_type_parameters(promote_quantity_on_value(Q, T2), promote_type(T, T2), D)
end
function Base.promote_rule(::Type{T2}, ::Type{Q}) where {T,D,Q<:$type{T,D},T2<:BASE_NUMERIC_TYPES}
return with_type_parameters(promote_quantity(Q, T2), promote_type(T, T2), D)
return with_type_parameters(promote_quantity_on_value(Q, T2), promote_type(T, T2), D)
end
end
for numeric_type in AMBIGUOUS_NUMERIC_TYPES
@@ -96,10 +117,10 @@ for (type, _, _) in ABSTRACT_QUANTITY_TYPES
return new_quantity(Q, convert(T, x), D())
end
function Base.promote_rule(::Type{Q}, ::Type{$numeric_type}) where {T,D,Q<:$type{T,D}}
return with_type_parameters(promote_quantity(Q, $numeric_type), promote_type(T, $numeric_type), D)
return with_type_parameters(promote_quantity_on_value(Q, $numeric_type), promote_type(T, $numeric_type), D)
end
function Base.promote_rule(::Type{$numeric_type}, ::Type{Q}) where {T,D,Q<:$type{T,D}}
return with_type_parameters(promote_quantity(Q, $numeric_type), promote_type(T, $numeric_type), D)
return with_type_parameters(promote_quantity_on_value(Q, $numeric_type), promote_type(T, $numeric_type), D)
end
end
end