-
Notifications
You must be signed in to change notification settings - Fork 20
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
Make AbstractQuantity
and AbstractDimensions
#24
Conversation
What subtypes are envisaged? I keep wondering whether |
I could see someone wanting to use fewer fields in using DynamicQuantities
import DynamicQuantities: new_dimensions, new_quantity, dimension_name
struct MyDimensions{R} <: AbstractDimensions{R}
length::R
mass::R
time::R
MyDimensions(args::_R...) where {_R} = new{_R}(args...)
end
struct MyQuantity{T,R} <: AbstractQuantity{T,R}
value::T
dimensions::MyDimensions{R}
MyQuantity(x, d::MyDimensions{_R}) where {_R} = new{typeof(x), _R}(x, d)
end
new_dimensions(::Type{<:MyDimensions}, l...) = MyDimensions(l...)
new_quantity(::Type{<:MyQuantity}, v, d) = MyQuantity(v, d)
new_quantity(::Type{<:MyDimensions}, v, d) = MyQuantity(v, d)
dimension_name(::MyDimensions, k::Symbol) = (length="m", mass="kg", time="s")[k] which seems to work: julia> q1 = MyQuantity(0.5, MyDimensions(0, 3, 2))
0.5 kg³ s²
julia> q2 = MyQuantity(0.3, MyDimensions(-1, 5, 2))
0.3 m⁻¹ kg⁵ s²
julia> q1 * q2
0.15 m⁻¹ kg⁸ s⁴ |
Yeah, this part is tricky... |
Yes. I missed that this PR did that too, was only thinking about The existing |
Just to clarify - the existing But yeah I agree it could be useful to have other types of |
I already posted this in one of the other discussions:
|
Benchmark Results
Benchmark PlotsA plot of the benchmark results have been uploaded as an artifact to the workflow run for this PR. |
I think this sounds feasible to support. Does the current abstract type interface work for implementing this? Or is there something else that is needed to get it working? |
I changed the functions a user needs to overload, to make things simpler. Now it's just these ones: dimension_name(::Dimensions, k::Symbol) = (length="m", mass="kg", time="s", current="A", temperature="K", luminosity="cd", amount="mol")[k]
quantity_constructor(::Type{<:Union{Quantity,Dimensions}}) = Quantity
dimension_constructor(::Type{<:Union{Quantity,Dimensions}}) = Dimensions With the I'm using edit: I suppose we could somehow get this constructor info automatically... by looking at the field types? |
1c574d8
to
68941b8
Compare
Yes - I checked this. Not yet sure where I will land that code, though. |
Okay, let me know if there’s something missing in the abstract interface. Also I think there might be a way to make these required functions completely optional. |
Sorry for the spam. Now I'm wondering if instead of an abstract type, With SimpleTraits.jl, this would look like: @traitfn Base.:+(x::::AbstractQuantity) = ... where we would have @traitimpl AbstractQuantity{X} <- quantity_like(X) where Then we can just create a @traitimpl AbstractQuantity{QuantityArray} What do you think @mcabbott? |
From the thread where this was proposed on Discourse, there was discussion of using the If supporting this statically-inferable idea is part of the intention, it's not clear to me that a single type parameter in |
Ah, apologies for the confusion – that comment on discourse was just an idea, but is unrelated to the changes implemented by this PR. Indeed, statically-inferrable dimensions are not possible with the current types (and probably better left to Unitful). This PR is for if you would like to use custom dimension spaces, but with otherwise the same types with variable dimensions. |
cc @trulsf, x-ref trulsf/UnitJuMP.jl#18 |
Do you have a specific branch I could follow along? I am interested in making the abstract interface compatible with your use case. What would your ideal API be? e.g., in this example: julia> using DynamicQuantities, JuMP, HiGHS
julia> m = Model(HiGHS.Optimizer);
julia> set_silent(m)
julia> @variable(m, x >= 0, u"m/s")
x [1.0 m s⁻¹]
julia> @variable(m, y >= 0, u"km/h")
y [0.2777777777777778 m s⁻¹]
julia> @variable(m, z, Bin, Dimensions())
z [1.0 ]
julia> max_speed = 4u"km/s"
4000.0 m s⁻¹
julia> @constraint(m, x + y <= max_speed * z)
x + 0.2777777777777778 y - 4000 z <= 0 [m s⁻¹]
julia> obj = @objective(m, Max, x + 3y)
0.8333333333333334 y + x [m s⁻¹]
julia> optimize!(m)
julia> println("obj = $(value(obj))")
obj = 12000.0 m s⁻¹
julia> println("x = $(value(x))")
x = 0.0 m s⁻¹
julia> println("y = $(value(y))")
y = 4000.0 m s⁻¹
julia> println("z = $(value(z))")
z = 1.0 what would the preferred outputs be? |
Also - I tried defining an For example, @traitfn Base.eltype(::Type{D}) where {D; IsQuantity{D}} = ... would actually generate a function So we have to stick to abstract types until Julia starts allowing traits or multiple inheritance. |
If there are no other comments on this abstract type system, I may try to merge things later this week. |
One quick update: I also just refactored all quantities types to be parametrically typed based on their entire dimension type, rather than the element type of the dimensions. I think this is a much more general option, and does not cost us anything because the space of dimensions is still fixed throughout any calculation. So, for example, struct Quantity{T,D<:AbstractDimensions} <: AbstractQuantity{T,D}
value::T
dimensions::D
end whereas before it was the more rigid struct Quantity{T,R} <: AbstractQuantity{T,R}
value::T
dimensions::Dimensions{R}
end This means that you could use the same The downside is that you can no longer automatically create quantities by multiplying a dimension object, like: 0.5 * Dimensions(length=1) But I think this is in opposition to the abstract interface anyways (how do you safely associate a quantity type from a dimensions type?), so it's better kept out. |
64233bf
to
a365da0
Compare
Thanks everyone for the feedback on this PR! Merging now. |
[Diff since v0.4.0](v0.4.0...v0.5.0) **Closed issues:** - Use `Int64 / C` as default (#20) **Merged pull requests:** - Make `AbstractQuantity` and `AbstractDimensions` (#24) (@MilesCranmer)
This PR adds abstract super types for quantities and dimensions. Almost every function is now defined for the abstract version, even with the constructors set up automagically
and with an
AbstractQuantity
(which we can pass our new dimensions type too)The individual dimensions are by default the
FixedRational
type, but you can override this with, e.gor by conversion:
Old version: