SymbolicML / DynamicQuantities.jl

Efficient and type-stable physical quantities in Julia
https://symbolicml.org/DynamicQuantities.jl/dev/
Apache License 2.0
120 stars 15 forks source link

WIP: Experimental support for Unitful-like statically-typed units #136

Open MilesCranmer opened 1 month ago

MilesCranmer commented 1 month ago

I understand this goes against the main point of this package, but it turned out to be extremely easy to stick in a StaticDimensions interface.

The trick is to store a regular dynamic Dimensions object (immutable struct) as one of the type parameters:

struct StaticDimensions{R,D<:AbstractDimensions{R},dim} <: AbstractStaticDimensions{R,D,dim}
    # (No fields)

    StaticDimensions(d::AbstractDimensions) = new{eltype(d),typeof(d),d}()
end

Promotion rules work as you'd expect with Unitful:

julia> using DynamicQuantities; using DynamicQuantities: StaticDimensions

julia> x = Quantity{Float64,StaticDimensions}.([1.0u"m", 1.0u"km", 10us"Constants.Mpc"])
3-element Vector{Quantity{Float64, StaticDimensions(m::Dimensions{FixedRational{Int32, 25200}})}}:
                  1.0 m
               1000.0 m
 3.085677581491367e23 m

Where you can see it correctly promotes the dimensions (even symbolic dimensions).

We can see that units can be inferred:

julia> x = Quantity{Float64,StaticDimensions}(10u"km")
10000.0 m

julia> y = Quantity{Float64,StaticDimensions}(1u"h")
3600.0 s

julia> @inferred x / y
2.7777777777777777 m s⁻¹

julia> typeof(x / y)
Quantity{Float64, StaticDimensions(m s⁻¹::Dimensions{FixedRational{Int32, 25200}})}

If anyone is interested, would love help in making this infer as well as Unitful. It then would make it really easy to switch between static and dynamic quantities – Dimensions for initial work, and then StaticDimensions whenever you are faced with optimizing a fast SIMD loop.

Any of @yingboma @chrisrackauckas @devmotion @aplavin, would you be up to help with improving this? I think the basics are ready, we would just need to ensure that inference is as good as Unitful.

github-actions[bot] commented 1 month ago

Benchmark Results

main 6afba7c2824aa4... main/6afba7c2824aa4...
Quantity/creation/Quantity(x) 2.79 ± 0.009 ns 2.79 ± 0 ns 1
Quantity/creation/Quantity(x, length=y) 3.41 ± 0.01 ns 3.41 ± 0.01 ns 1
Quantity/with_numbers/*real 3.11 ± 0.01 ns 3.1 ± 0.01 ns 1
Quantity/with_numbers/^int 8.05 ± 2.2 ns 8.05 ± 2.2 ns 1
Quantity/with_numbers/^int * real 8.37 ± 1.9 ns 8.37 ± 1.9 ns 1
Quantity/with_quantity/+y 4.04 ± 0.001 ns 4.04 ± 0.001 ns 1
Quantity/with_quantity//y 3.11 ± 0 ns 3.11 ± 0.001 ns 1
Quantity/with_self/dimension 3.11 ± 0.01 ns 3.1 ± 0.01 ns 1
Quantity/with_self/inv 3.11 ± 0.001 ns 3.11 ± 0 ns 1
Quantity/with_self/ustrip 2.79 ± 0.009 ns 2.79 ± 0.01 ns 1
QuantityArray/broadcasting/multi_array_of_quantities 0.143 ± 0.00082 ms 0.146 ± 0.001 ms 0.978
QuantityArray/broadcasting/multi_normal_array 0.0541 ± 0.003 ms 0.056 ± 0.0019 ms 0.967
QuantityArray/broadcasting/multi_quantity_array 0.155 ± 0.0015 ms 0.155 ± 0.00052 ms 1
QuantityArray/broadcasting/x^2_array_of_quantities 23.9 ± 2.3 μs 23.3 ± 2.3 μs 1.03
QuantityArray/broadcasting/x^2_normal_array 4.83 ± 0.88 μs 5.12 ± 0.91 μs 0.943
QuantityArray/broadcasting/x^2_quantity_array 6.97 ± 0.26 μs 6.98 ± 0.29 μs 0.999
QuantityArray/broadcasting/x^4_array_of_quantities 0.0815 ± 0.00058 ms 0.0816 ± 0.00055 ms 1
QuantityArray/broadcasting/x^4_normal_array 0.0499 ± 0.00017 ms 0.0498 ± 0.00018 ms 1
QuantityArray/broadcasting/x^4_quantity_array 0.053 ± 0.00022 ms 0.053 ± 0.0029 ms 1
time_to_load 0.144 ± 0.00087 s 0.151 ± 0.00055 s 0.957

Benchmark Plots

A plot of the benchmark results have been uploaded as an artifact to the workflow run for this PR. Go to "Actions"->"Benchmark a pull request"->[the most recent run]->"Artifacts" (at the bottom).