-
Notifications
You must be signed in to change notification settings - Fork 18
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
Additional utilities for identity functions, +
, and -
#23
Conversation
Benchmark Results
Benchmark PlotsA plot of the benchmark results have been uploaded as an artifact to the workflow run for this PR. |
Essentially, this PR seems to hit in the same direction as #18, but just to remark: this does not define an universal BTW I found an intersting paper here: https://link.springer.com/article/10.1023/B:JOMC.0000034933.47311.93 And two other papers citing it concerned with programmable formalization: Up to what I could understand, these don't work with "universal zero" as well. |
Thanks, I see. Do you want to take a look at this too? I don't know what you mean by DynamicQuantities.jl/src/utils.jl Lines 58 to 61 in 38ef94a
As is stated, it can only be defined for (Only in Unitful.jl is the dimension information accessible from the type itself; whereas here it is stored in the value - and therefore constrains the |
Btw, note that with this PR, you can now add numbers to dimensionless quantities: julia> x = Quantity(0.5)
0.5
julia> x + 1.0
1.5
julia> typeof(x + 1.0)
Quantity{Float64, DynamicQuantities.FixedRational{Int32, 25200}} |
38ef94a
to
0257746
Compare
src/utils.jl
Outdated
Base.one(d::Dimensions) = one(typeof(d)) | ||
|
||
# Additive identities: | ||
Base.zero(q::Q) where {T,Q<:Quantity{T}} = Quantity(zero(ustrip(q)), dimension(q)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a style thing, but you could shorten many lines by deleing unused type parameters:
Base.zero(q::Q) where {T,Q<:Quantity{T}} = Quantity(zero(ustrip(q)), dimension(q)) | |
Base.zero(q::Quantity) = Quantity(zero(ustrip(q)), dimension(q)) |
Base.:*(l::Quantity, r) = Quantity(l.value * r, l.dimensions) | ||
Base.:*(l, r::Quantity) = Quantity(l * r.value, r.dimensions) | ||
Base.:*(l::Dimensions, r) = Quantity(r, l) | ||
Base.:*(l, r::Dimensions) = Quantity(l, r) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that both Dimensions and Quantity are given many number-like methods. Might it be simpler to only do this for Quantity, and use Quantity(true, Dimension(...))
in place of a naked Dimension?
Or some special One if true
is too narrow. But I'm not so sure how much it's intended to support Quantity([1,2,3], time=1)
. It would seem simplest to me to restrict Quantity to real numbers, but maybe you have other plans.
Actually, when would methods like *(::Number, ::Dimension)
be used at all? #22 defines const m = Quantity(1.0, length=1)
. It could instead use this method and define m::Dimension
, but instead seems to focus on numbers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that both Dimensions and Quantity are given many number-like methods. Might it be simpler to only do this for Quantity, and use
Quantity(true, Dimension(...))
in place of a naked Dimension?
I'm not sure if this is possible because Quantity.value
and Dimensions
are different number systems: .value
is a regular number, while Dimensions.(field)
are powers. So, for example, :*(::Dimensions, ::Dimensions)
actually adds fields, rather than multiplies them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, when would methods like
*(::Number, ::Dimension)
be used at all? #22 definesconst m = Quantity(1.0, length=1)
. It could instead use this method and definem::Dimension
, but instead seems to focus on numbers.
This is just a convenience thing that felt more intuitive than using Quantity()
directly. For example:
x = Quantity(1.0, mass=3, length=1)
y = 0.5 * dimension(x)
which puts y
in the units of x
.
You could do Quantity(0.5, dimension(x))
, but I think after #22 merges, the Quantity()
constructor will be used less often, so this multiplication strategy seems useful. Wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Sorry, just realized all my messages were listed as pending because they are submitted as a batch when I hit "Review changes"...)
Ok, now found a way to make an MWE for what I am asking for:
In order to make
If not for
So with these settings we get
If I find time I als will try to make an example with mixed quantities in the matrix and the RHS. Besides of defining these methods in this package, with AbstractQuantities, it would be possible to define a |
I'm not sure we should add help?> zero
search: zero zeros iszero set_zero_subnormals get_zero_subnormals count_zeros RoundToZero leading_zeros trailing_zeros RoundFromZero finalizer
zero(x)
zero(::Type)
Get the additive identity element for the type of x (x can also specify the type itself).
See also iszero, one, oneunit, oftype. Meaning that Here is the docstring for help?> oneunit
search: oneunit DimensionlessQuantity
oneunit(x::T)
oneunit(T::Type)
Return T(one(x)), where T is either the type of the argument or (if a type is passed) the argument. This differs from one for dimensionful quantities:
one is dimensionless (a multiplicative identity) while oneunit is dimensionful (of the same type as x, or of type T). so it needs to know about dimensions - hence it requires
We should be very hesitant to use a different algebra than standard unit packages unless there is a very good reason to do so. Both Astropy: [ins] In [2]: abs(1.5*u.km/u.s)
Out[2]: <Quantity 1.5 km / s> Unitful: julia> abs(1.5u"km/s")
1.5 km s⁻¹
We can definitely add this. And I guess should raise
See #24 for a first attempt at implementing |
For the specific example, it's probably better to just do: julia> Quantity(ustrip.(A) \ ustrip.(b), dimension(first(b)) / dimension(first(A))) I think by overloading Edit: also note that this doesn't work for Unitful.jl as well: julia> A \ b
ERROR: MethodError: no method matching (Quantity{Float64})(::Float64) |
This is one path, arrays of Quantity. In Unitful.jl, arrays of uniform unit are in principle free, and can be re-interpreted, like PainterQubits/Unitful.jl#437 . But here, they involve storing many Dimensions objects, and can't be re-interpreted to feed to BLAS. The other possible path is Edit:
This seems essential, |
Well, arrays of quantity are certainly interesting for heterogeneous physical systems. I am interested in "multiphysics" PDE systems in this context. With the switch to With
this seems to get me through dense Now |
Base.:*(l::Quantity, r) = Quantity(l.value * r, l.dimensions) | ||
Base.:*(l, r::Quantity) = Quantity(l * r.value, r.dimensions) | ||
Base.:*(l::Dimensions, r) = Quantity(r, l) | ||
Base.:*(l, r::Dimensions) = Quantity(l, r) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that both Dimensions and Quantity are given many number-like methods. Might it be simpler to only do this for Quantity, and use
Quantity(true, Dimension(...))
in place of a naked Dimension?
I'm not sure if this is possible because Quantity.value
and Dimensions
are different number systems: .value
is a regular number, while Dimensions.(field)
are powers. So, for example, :*(::Dimensions, ::Dimensions)
actually adds fields, rather than multiplies them.
Base.:*(l::Quantity, r) = Quantity(l.value * r, l.dimensions) | ||
Base.:*(l, r::Quantity) = Quantity(l * r.value, r.dimensions) | ||
Base.:*(l::Dimensions, r) = Quantity(r, l) | ||
Base.:*(l, r::Dimensions) = Quantity(l, r) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, when would methods like
*(::Number, ::Dimension)
be used at all? #22 definesconst m = Quantity(1.0, length=1)
. It could instead use this method and definem::Dimension
, but instead seems to focus on numbers.
This is just a convenience thing that felt more intuitive than using Quantity()
directly. For example:
x = Quantity(1.0, mass=3, length=1)
y = 0.5 * dimension(x)
which puts y
in the units of x
.
You could do Quantity(0.5, dimension(x))
, but I think after #22 merges, the Quantity()
constructor will be used less often, so this multiplication strategy seems useful. Wdyt?
Base.:*(l::Quantity, r) = Quantity(l.value * r, l.dimensions) | ||
Base.:*(l, r::Quantity) = Quantity(l * r.value, r.dimensions) | ||
Base.:*(l::Dimensions, r) = Quantity(r, l) | ||
Base.:*(l, r::Dimensions) = Quantity(l, r) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Sorry, just realized all my messages were listed as pending because they are submitted as a batch when I hit "Review changes"...)
Going to merge this now so we can have the fixed |
This fixes the behavior of
one
,zero
,oneunit
as pointed out by @mcabbott in #6.This also extends
+
and-
for::Quantity
to::Number
, given the quantity is dimensionless.Could you take a look @mcabbott?