From c59a17412d4fb0a21fe90ccafd59543a59f8aea7 Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Sat, 11 May 2024 17:04:19 +0100 Subject: [PATCH 01/14] feat: create `StaticDimensions` type --- src/DynamicQuantities.jl | 2 + src/static_dimensions.jl | 151 +++++++++++++++++++++++++++++++++++++++ src/types.jl | 1 + 3 files changed, 154 insertions(+) create mode 100644 src/static_dimensions.jl diff --git a/src/DynamicQuantities.jl b/src/DynamicQuantities.jl index e363bed7..a487a347 100644 --- a/src/DynamicQuantities.jl +++ b/src/DynamicQuantities.jl @@ -6,6 +6,7 @@ export Quantity, GenericQuantity, RealQuantity export FixedRational export AbstractDimensions, Dimensions, NoDims export AbstractSymbolicDimensions, SymbolicDimensions, SymbolicDimensionsSingleton +export StaticDimensions export QuantityArray export DimensionError export ustrip, dimension @@ -24,6 +25,7 @@ include("units.jl") include("constants.jl") include("uparse.jl") include("symbolic_dimensions.jl") +include("static_dimensions.jl") include("complex.jl") include("register_units.jl") include("disambiguities.jl") diff --git a/src/static_dimensions.jl b/src/static_dimensions.jl new file mode 100644 index 00000000..64c05bfe --- /dev/null +++ b/src/static_dimensions.jl @@ -0,0 +1,151 @@ +abstract type AbstractStaticDimensions{R,D,dim} <: AbstractDimensions{R} end + +""" + StaticDimensions{R,D<:AbstractDimensions{R},dim} <: AbstractStaticDimensions{R,D,dim} + +Experiment to store the dimensions as a type parameter, so that one +can have Unitful-like behavior within DynamicQuantities. +""" +struct StaticDimensions{R,D<:AbstractDimensions{R},dim} <: AbstractStaticDimensions{R,D,dim} + + StaticDimensions(d::AbstractDimensions) = new{eltype(d),typeof(d),d}() + StaticDimensions{_R}(d::AbstractDimensions) where {_R} = (d = convert(with_type_parameters(typeof(d), _R), d); StaticDimensions(d)) + StaticDimensions{_R,_D}(d::AbstractDimensions) where {_R,_D} = (d = convert(_D, d); StaticDimensions(d)) + StaticDimensions(; kws...) = StaticDimensions(Dimensions(; kws...)) +end + +Base.propertynames(::AbstractStaticDimensions{R,D,dim}) where {R,D,dim} = propertynames(dim) +Base.getproperty(::AbstractStaticDimensions{R,D,dim}, k::Symbol) where {R,D,dim} = getproperty(dim, k) +Base.getindex(::AbstractStaticDimensions{R,D,dim}, i::Symbol) where {R,D,dim} = getindex(dim, i) + +raw_dimension(::Type{<:AbstractStaticDimensions{R,D,dim}}) where {R,D,dim} = dim +dimension_names(::Type{<:AbstractStaticDimensions{R,D}}) where {R,D} = dimension_names(D) + +constructorof(::Type{<:StaticDimensions}) = StaticDimensions +with_type_parameters(::Type{StaticDimensions{Rold,D}}, ::Type{R}) where {Rold,D,R} = StaticDimensions{R,D} + +function Base.promote_rule(::Type{<:StaticDimensions{R1,D1}}, ::Type{<:StaticDimensions{R2,D2}}) where {R1,D1,R2,D2} + D = promote_type(D1, D2) + return StaticDimensions{eltype(D),D} +end +function Base.promote_rule(::Type{<:StaticDimensions{R1,D1,dim1}}, ::Type{<:StaticDimensions{R2,D2,dim2}}) where {R1,D1,dim1,R2,D2,dim2} + D = promote_type(D1, D2) + R = eltype(D) + dim1 == dim2 ? StaticDimensions{R,D,convert(D, dim1)} : StaticDimensions{R,D} +end +function Base.promote_rule(::Type{StaticDimensions{R1,D1,dim1}}, ::Type{D2}) where {R1,D1,dim1,D2<:Dimensions} + return promote_type(D1, D2) +end +function Base.promote_rule(::Type{D1}, ::Type{<:StaticDimensions{R2,D2,dim2}}) where {D1<:AbstractDimensions,R2,D2,dim2} + return promote_type(D1, D2) +end + +function Base.convert(::Type{Q1}, q::Q2) where { + T1,D1,Q1<:AbstractQuantity{T1,D1}, + dims, + Q2<:AbstractQuantity{<:Any,<:AbstractStaticDimensions{<:Any,<:Any,dims}} +} + val = ustrip(q) + # First, construct dynamic version in the input types + q_dynamic = new_quantity(Q2, val, dims) + # Then, convert that + return convert(Q1, q_dynamic) +end + +map_dimensions(::F, args::AbstractStaticDimensions...) where {F<:Function} = error("not implemented.") + +Base.:(==)(::AbstractStaticDimensions{<:Any,<:Any,dim1}, ::AbstractStaticDimensions{<:Any,<:Any,dim2}) where {dim1,dim2} = dim1 == dim2 +@generated function map_dimensions(f::F, args::AbstractStaticDimensions...) where {F<:Function} + cons = constructorof(promote_type(args...)) + dims = map(raw_dimension, args) + return :($(cons)(map_dimensions(f, $(dims...)))) +end +@generated function all_dimensions(f::F, args::AbstractStaticDimensions...) where {F<:Function} + cons = constructorof(promote_type(args...)) + dims = map(raw_dimension, args) + return :($(cons)(map_dimensions(f, $(dims...)))) +end + + + + + +################################################################################ +# Tests ######################################################################## +################################################################################ +@testitem "Static dimensions basics" begin + using DynamicQuantities + + x = Quantity(1.0, StaticDimensions(length=1)) + d = Dimensions(length=1) + @test typeof(x) === Quantity{Float64, StaticDimensions{eltype(d),typeof(d),d}} + @test sprint(show, x) == "1.0 m" + + y = Quantity(1.0, StaticDimensions(time=-1)) + @test sprint(show, y) == "1.0 s⁻¹" + + # Only promotes to concrete type if dimensions equal + @test promote_type(typeof(x), typeof(x)) === Quantity{Float64, StaticDimensions{eltype(d),typeof(d),d}} + + # Otherwise, is a union: + @test promote_type(typeof(x), typeof(y)) === Quantity{Float64, StaticDimensions{eltype(d),typeof(d)}} +end + +@testitem "Static dimensions math" begin + using DynamicQuantities + + x = Quantity(1.0, StaticDimensions(length=1)) + y = Quantity(2.0, StaticDimensions(time=-1)) + + z = x * y + @test sprint(show, z) == "2.0 m s⁻¹" + @test z isa Quantity{Float64, <:StaticDimensions} + @test z == Quantity(2.0, StaticDimensions(length=1, time=-1)) + + z2 = x / y + @test sprint(show, z2) == "0.5 m s" + @test z2 isa Quantity{Float64, <:StaticDimensions} + @test z2 == Quantity(0.5, StaticDimensions(length=1, time=1)) + + # Check if inference works + @inferred x * y + @inferred x / y +end + +@testitem "Conversion" begin + using DynamicQuantities + + x = 1.0u"m" + y = convert(Quantity{Float64,StaticDimensions}, x) + @test y isa Quantity{Float64,<:StaticDimensions} + @test sprint(show, y) == "1.0 m" + # @show typeof(y) + @test dimension(x) isa Dimensions + @test dimension(y) isa StaticDimensions + + # Should be able to convert back too: + x2 = convert(Quantity{Float64,Dimensions}, y) + @test x2 isa Quantity{Float64,<:Dimensions} + @test x == x2 + + # Should automatically convert: + @test x == y +end + +@testitem "Static dimensions arrays" begin + using DynamicQuantities + + x = [1.0u"m", 1.0u"km", 10u"Constants.Mpc"] + x = Quantity{Float64,StaticDimensions}.(x) + + # The array should automatically promote to length + d = dimension(1.0u"m") + @test eltype(x) == Quantity{Float64,StaticDimensions{eltype(d),typeof(d),d}} + + # Should be able to do vectorized operations: + x2 = x .^ 2 + d2 = dimension(1.0u"m^2") + @test eltype(x2) == Quantity{Float64,StaticDimensions{eltype(d2),typeof(d2),d2}} +end +################################################################################ + diff --git a/src/types.jl b/src/types.jl index 02423100..3375082f 100644 --- a/src/types.jl +++ b/src/types.jl @@ -238,6 +238,7 @@ end return constructorof(Qout)(val, dims) end +Base.eltype(::Union{Type{<:AbstractDimensions{R}},AbstractDimensions{R}}) where {R} = R dim_type(::Type{Q}) where {T,D<:AbstractDimensions,Q<:UnionAbstractQuantity{T,D}} = D dim_type(::Type{<:UnionAbstractQuantity}) = DEFAULT_DIM_TYPE From 8be517a7db953bd94caf8fe85561be0d862dee1f Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Sat, 11 May 2024 17:05:45 +0100 Subject: [PATCH 02/14] fix: avoid exporting StaticDimensions as not yet stable --- src/DynamicQuantities.jl | 1 - src/static_dimensions.jl | 6 ++++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/DynamicQuantities.jl b/src/DynamicQuantities.jl index a487a347..3730d3ca 100644 --- a/src/DynamicQuantities.jl +++ b/src/DynamicQuantities.jl @@ -6,7 +6,6 @@ export Quantity, GenericQuantity, RealQuantity export FixedRational export AbstractDimensions, Dimensions, NoDims export AbstractSymbolicDimensions, SymbolicDimensions, SymbolicDimensionsSingleton -export StaticDimensions export QuantityArray export DimensionError export ustrip, dimension diff --git a/src/static_dimensions.jl b/src/static_dimensions.jl index 64c05bfe..791d28cf 100644 --- a/src/static_dimensions.jl +++ b/src/static_dimensions.jl @@ -5,6 +5,8 @@ abstract type AbstractStaticDimensions{R,D,dim} <: AbstractDimensions{R} end Experiment to store the dimensions as a type parameter, so that one can have Unitful-like behavior within DynamicQuantities. + +This is not yet stable, so this type is not exported. """ struct StaticDimensions{R,D<:AbstractDimensions{R},dim} <: AbstractStaticDimensions{R,D,dim} @@ -75,6 +77,7 @@ end ################################################################################ @testitem "Static dimensions basics" begin using DynamicQuantities + using DynamicQuantities: StaticDimensions x = Quantity(1.0, StaticDimensions(length=1)) d = Dimensions(length=1) @@ -93,6 +96,7 @@ end @testitem "Static dimensions math" begin using DynamicQuantities + using DynamicQuantities: StaticDimensions x = Quantity(1.0, StaticDimensions(length=1)) y = Quantity(2.0, StaticDimensions(time=-1)) @@ -114,6 +118,7 @@ end @testitem "Conversion" begin using DynamicQuantities + using DynamicQuantities: StaticDimensions x = 1.0u"m" y = convert(Quantity{Float64,StaticDimensions}, x) @@ -134,6 +139,7 @@ end @testitem "Static dimensions arrays" begin using DynamicQuantities + using DynamicQuantities: StaticDimensions x = [1.0u"m", 1.0u"km", 10u"Constants.Mpc"] x = Quantity{Float64,StaticDimensions}.(x) From a48e270ab097197cbc4eb6e82358499886dbadc7 Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Sat, 11 May 2024 17:06:54 +0100 Subject: [PATCH 03/14] fix: ambiguous methods --- src/static_dimensions.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/static_dimensions.jl b/src/static_dimensions.jl index 791d28cf..d5c58323 100644 --- a/src/static_dimensions.jl +++ b/src/static_dimensions.jl @@ -26,11 +26,11 @@ dimension_names(::Type{<:AbstractStaticDimensions{R,D}}) where {R,D} = dimension constructorof(::Type{<:StaticDimensions}) = StaticDimensions with_type_parameters(::Type{StaticDimensions{Rold,D}}, ::Type{R}) where {Rold,D,R} = StaticDimensions{R,D} -function Base.promote_rule(::Type{<:StaticDimensions{R1,D1}}, ::Type{<:StaticDimensions{R2,D2}}) where {R1,D1,R2,D2} +function Base.promote_rule(::Type{StaticDimensions{R1,D1}}, ::Type{StaticDimensions{R2,D2}}) where {R1,D1,R2,D2} D = promote_type(D1, D2) return StaticDimensions{eltype(D),D} end -function Base.promote_rule(::Type{<:StaticDimensions{R1,D1,dim1}}, ::Type{<:StaticDimensions{R2,D2,dim2}}) where {R1,D1,dim1,R2,D2,dim2} +function Base.promote_rule(::Type{StaticDimensions{R1,D1,dim1}}, ::Type{StaticDimensions{R2,D2,dim2}}) where {R1,D1,dim1,R2,D2,dim2} D = promote_type(D1, D2) R = eltype(D) dim1 == dim2 ? StaticDimensions{R,D,convert(D, dim1)} : StaticDimensions{R,D} @@ -38,7 +38,7 @@ end function Base.promote_rule(::Type{StaticDimensions{R1,D1,dim1}}, ::Type{D2}) where {R1,D1,dim1,D2<:Dimensions} return promote_type(D1, D2) end -function Base.promote_rule(::Type{D1}, ::Type{<:StaticDimensions{R2,D2,dim2}}) where {D1<:AbstractDimensions,R2,D2,dim2} +function Base.promote_rule(::Type{D1}, ::Type{StaticDimensions{R2,D2,dim2}}) where {D1<:AbstractDimensions,R2,D2,dim2} return promote_type(D1, D2) end From 74474db6b8acb6f55be06dd056ee1edf176a8b0e Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Sat, 11 May 2024 17:13:31 +0100 Subject: [PATCH 04/14] fix: ambiguities --- src/static_dimensions.jl | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/static_dimensions.jl b/src/static_dimensions.jl index d5c58323..f469f137 100644 --- a/src/static_dimensions.jl +++ b/src/static_dimensions.jl @@ -35,11 +35,15 @@ function Base.promote_rule(::Type{StaticDimensions{R1,D1,dim1}}, ::Type{StaticDi R = eltype(D) dim1 == dim2 ? StaticDimensions{R,D,convert(D, dim1)} : StaticDimensions{R,D} end -function Base.promote_rule(::Type{StaticDimensions{R1,D1,dim1}}, ::Type{D2}) where {R1,D1,dim1,D2<:Dimensions} - return promote_type(D1, D2) -end -function Base.promote_rule(::Type{D1}, ::Type{StaticDimensions{R2,D2,dim2}}) where {D1<:AbstractDimensions,R2,D2,dim2} - return promote_type(D1, D2) +for D_type in (:SymbolicDimensions, :SymbolicDimensionsSingleton, :Dimensions) + @eval begin + function Base.promote_rule(::Type{StaticDimensions{R1,D1,dim1}}, ::Type{$D_type{R2}}) where {R1,D1,dim1,R2} + return promote_type(D1, $D_type{R2}) + end + function Base.promote_rule(::Type{$D_type{R1}}, ::Type{StaticDimensions{R2,D2,dim2}}) where {R1,R2,D2,dim2} + return promote_type($D_type{R1}, D2) + end + end end function Base.convert(::Type{Q1}, q::Q2) where { @@ -54,8 +58,6 @@ function Base.convert(::Type{Q1}, q::Q2) where { return convert(Q1, q_dynamic) end -map_dimensions(::F, args::AbstractStaticDimensions...) where {F<:Function} = error("not implemented.") - Base.:(==)(::AbstractStaticDimensions{<:Any,<:Any,dim1}, ::AbstractStaticDimensions{<:Any,<:Any,dim2}) where {dim1,dim2} = dim1 == dim2 @generated function map_dimensions(f::F, args::AbstractStaticDimensions...) where {F<:Function} cons = constructorof(promote_type(args...)) From f5e3218bcffd5177d2811a3c344220ab655fac8c Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Sat, 11 May 2024 17:42:32 +0100 Subject: [PATCH 05/14] fix: add missing constructor for older Julia --- src/static_dimensions.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/static_dimensions.jl b/src/static_dimensions.jl index f469f137..eaa473c4 100644 --- a/src/static_dimensions.jl +++ b/src/static_dimensions.jl @@ -13,6 +13,7 @@ struct StaticDimensions{R,D<:AbstractDimensions{R},dim} <: AbstractStaticDimensi StaticDimensions(d::AbstractDimensions) = new{eltype(d),typeof(d),d}() StaticDimensions{_R}(d::AbstractDimensions) where {_R} = (d = convert(with_type_parameters(typeof(d), _R), d); StaticDimensions(d)) StaticDimensions{_R,_D}(d::AbstractDimensions) where {_R,_D} = (d = convert(_D, d); StaticDimensions(d)) + StaticDimensions{_R,_D}(dims...) where {_R,_D} = StaticDimensions(constructorof(_D)(dims...)) StaticDimensions(; kws...) = StaticDimensions(Dimensions(; kws...)) end @@ -69,6 +70,7 @@ end dims = map(raw_dimension, args) return :($(cons)(map_dimensions(f, $(dims...)))) end +# TODO: Should these have Base.@constprop :aggressive? From 2b6c1dd1285570da7b810d269a6c500e95f2fa40 Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Sat, 11 May 2024 17:49:39 +0100 Subject: [PATCH 06/14] fix: constructor ambiguities --- src/static_dimensions.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/static_dimensions.jl b/src/static_dimensions.jl index eaa473c4..15531ffc 100644 --- a/src/static_dimensions.jl +++ b/src/static_dimensions.jl @@ -11,10 +11,13 @@ This is not yet stable, so this type is not exported. struct StaticDimensions{R,D<:AbstractDimensions{R},dim} <: AbstractStaticDimensions{R,D,dim} StaticDimensions(d::AbstractDimensions) = new{eltype(d),typeof(d),d}() + StaticDimensions(; kws...) = StaticDimensions(Dimensions(; kws...)) + StaticDimensions(::Type{_R}; kws...) where {_R} = StaticDimensions(Dimensions(_R; kws...)) StaticDimensions{_R}(d::AbstractDimensions) where {_R} = (d = convert(with_type_parameters(typeof(d), _R), d); StaticDimensions(d)) + StaticDimensions{_Rold}(::Type{_R}; kws...) where {_Rold,_R} = StaticDimensions(Dimensions(_R; kws...)) StaticDimensions{_R,_D}(d::AbstractDimensions) where {_R,_D} = (d = convert(_D, d); StaticDimensions(d)) StaticDimensions{_R,_D}(dims...) where {_R,_D} = StaticDimensions(constructorof(_D)(dims...)) - StaticDimensions(; kws...) = StaticDimensions(Dimensions(; kws...)) + StaticDimensions{_Rold,_D}(::Type{_R}; kws...) where {_Rold,_D,_R} = StaticDimensions(constructorof(_D)(_R; kws...)) end Base.propertynames(::AbstractStaticDimensions{R,D,dim}) where {R,D,dim} = propertynames(dim) From 64cb3ee7ea7a55b2aa5e58f3022bd3a676c66d0b Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Sat, 11 May 2024 17:52:09 +0100 Subject: [PATCH 07/14] refactor: move out disambiguities --- src/disambiguities.jl | 11 +++++++++++ src/static_dimensions.jl | 5 ----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/disambiguities.jl b/src/disambiguities.jl index 57ff677d..17aa88d8 100644 --- a/src/disambiguities.jl +++ b/src/disambiguities.jl @@ -109,3 +109,14 @@ for (type, _, _) in ABSTRACT_QUANTITY_TYPES, numeric_type in (Bool, BigFloat) end end end + + +################################################################################ +# Disambiguate StaticDimensions constructors ################################### +################################################################################ +StaticDimensions(::Type{R}; kws...) where {R} = StaticDimensions(Dimensions(R; kws...)) +StaticDimensions{Rold}(::Type{R}; kws...) where {Rold,R} = StaticDimensions(Dimensions(R; kws...)) # TODO: Is this correct? +StaticDimensions{R,D}(d::AbstractDimensions) where {R,D} = (d = convert(D, d); StaticDimensions(d)) +StaticDimensions{R,D}(dims...) where {R,D} = StaticDimensions(constructorof(D)(dims...)) +StaticDimensions{Rold,D}(::Type{R}; kws...) where {Rold,D,R} = StaticDimensions(constructorof(D)(R; kws...)) +################################################################################ diff --git a/src/static_dimensions.jl b/src/static_dimensions.jl index 15531ffc..02e2903b 100644 --- a/src/static_dimensions.jl +++ b/src/static_dimensions.jl @@ -12,12 +12,7 @@ struct StaticDimensions{R,D<:AbstractDimensions{R},dim} <: AbstractStaticDimensi StaticDimensions(d::AbstractDimensions) = new{eltype(d),typeof(d),d}() StaticDimensions(; kws...) = StaticDimensions(Dimensions(; kws...)) - StaticDimensions(::Type{_R}; kws...) where {_R} = StaticDimensions(Dimensions(_R; kws...)) StaticDimensions{_R}(d::AbstractDimensions) where {_R} = (d = convert(with_type_parameters(typeof(d), _R), d); StaticDimensions(d)) - StaticDimensions{_Rold}(::Type{_R}; kws...) where {_Rold,_R} = StaticDimensions(Dimensions(_R; kws...)) - StaticDimensions{_R,_D}(d::AbstractDimensions) where {_R,_D} = (d = convert(_D, d); StaticDimensions(d)) - StaticDimensions{_R,_D}(dims...) where {_R,_D} = StaticDimensions(constructorof(_D)(dims...)) - StaticDimensions{_Rold,_D}(::Type{_R}; kws...) where {_Rold,_D,_R} = StaticDimensions(constructorof(_D)(_R; kws...)) end Base.propertynames(::AbstractStaticDimensions{R,D,dim}) where {R,D,dim} = propertynames(dim) From c3257ca3bb18d5afa48ad2969dd02aacbb833b42 Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Sat, 11 May 2024 17:59:15 +0100 Subject: [PATCH 08/14] feat: add zero and oneunit for statically-typed versions --- src/static_dimensions.jl | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/static_dimensions.jl b/src/static_dimensions.jl index 02e2903b..7c26b22b 100644 --- a/src/static_dimensions.jl +++ b/src/static_dimensions.jl @@ -72,7 +72,13 @@ end - +# Utilities +function Base.zero(::Type{Q}) where {dim,D<:AbstractStaticDimensions{<:Any,<:Any,dim},T,Q<:AbstractQuantity{T,D}} + return new_quantity(Q, zero(T), dim) +end +function Base.oneunit(::Type{Q}) where {dim,D<:AbstractStaticDimensions{<:Any,<:Any,dim},T,Q<:AbstractQuantity{T,D}} + return new_quantity(Q, one(T), dim) +end ################################################################################ # Tests ######################################################################## @@ -155,5 +161,18 @@ end d2 = dimension(1.0u"m^2") @test eltype(x2) == Quantity{Float64,StaticDimensions{eltype(d2),typeof(d2),d2}} end + +@testitem "Using zero and oneunit now work" begin + using DynamicQuantities + using DynamicQuantities: StaticDimensions + + x = Quantity{Float64,StaticDimensions}(u"km") + @test zero(typeof(x)) == 0 * x + @test dimension(zero(typeof(x))) == dimension(x) + @test dimension(zero(x)) == dimension(x) + + @test oneunit(typeof(x)) == Quantity{Float64,StaticDimensions}(u"m") + @test dimension(oneunit(typeof(x))) == dimension(x) +end ################################################################################ From 315823d5c1da548f3047d265418b9020481b2e07 Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Sat, 11 May 2024 18:28:48 +0100 Subject: [PATCH 09/14] feat: nicer printing of type --- src/static_dimensions.jl | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/static_dimensions.jl b/src/static_dimensions.jl index 7c26b22b..842c9f83 100644 --- a/src/static_dimensions.jl +++ b/src/static_dimensions.jl @@ -50,6 +50,9 @@ function Base.convert(::Type{Q1}, q::Q2) where { dims, Q2<:AbstractQuantity{<:Any,<:AbstractStaticDimensions{<:Any,<:Any,dims}} } + if q isa Q1 + return q + end val = ustrip(q) # First, construct dynamic version in the input types q_dynamic = new_quantity(Q2, val, dims) @@ -72,13 +75,18 @@ end -# Utilities +################################################################################ +# Utilities #################################################################### +################################################################################ function Base.zero(::Type{Q}) where {dim,D<:AbstractStaticDimensions{<:Any,<:Any,dim},T,Q<:AbstractQuantity{T,D}} return new_quantity(Q, zero(T), dim) end function Base.oneunit(::Type{Q}) where {dim,D<:AbstractStaticDimensions{<:Any,<:Any,dim},T,Q<:AbstractQuantity{T,D}} return new_quantity(Q, one(T), dim) end +function Base.show(io::IO, ::Type{StaticDimensions{R,D,dim}}) where {R,D,dim} + print(io, "StaticDimensions($dim::$D)") +end ################################################################################ # Tests ######################################################################## @@ -174,5 +182,13 @@ end @test oneunit(typeof(x)) == Quantity{Float64,StaticDimensions}(u"m") @test dimension(oneunit(typeof(x))) == dimension(x) end + +@testitem "Pretty printing of type" begin + using DynamicQuantities + using DynamicQuantities: StaticDimensions + + x = Quantity{Float64,StaticDimensions}(u"m/s") + @test sprint(show, typeof(x)) == "DynamicQuantities.Quantity{Float64, StaticDimensions(m s⁻¹::DynamicQuantities.Dimensions{DynamicQuantities.FixedRational{Int32, 25200}})}" +end ################################################################################ From 427565a1b116cc9dffb4479010dbf47fef764ac6 Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Sat, 11 May 2024 18:34:48 +0100 Subject: [PATCH 10/14] test: add additional inference tests --- src/static_dimensions.jl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/static_dimensions.jl b/src/static_dimensions.jl index 842c9f83..c7550409 100644 --- a/src/static_dimensions.jl +++ b/src/static_dimensions.jl @@ -168,6 +168,14 @@ end x2 = x .^ 2 d2 = dimension(1.0u"m^2") @test eltype(x2) == Quantity{Float64,StaticDimensions{eltype(d2),typeof(d2),d2}} + + # Inference of broadcasting + g(x) = x .^ 2 + @inferred g(x) + + f(x, y) = x .* y + @inferred f(x, x) + @test f(x, x) isa Vector{<:Quantity{Float64,<:StaticDimensions}} end @testitem "Using zero and oneunit now work" begin From 910fd85e065037973977d2fe374cacd4bee40feb Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Sat, 11 May 2024 18:38:01 +0100 Subject: [PATCH 11/14] test: additional promotion tests --- src/static_dimensions.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/static_dimensions.jl b/src/static_dimensions.jl index c7550409..5007d445 100644 --- a/src/static_dimensions.jl +++ b/src/static_dimensions.jl @@ -176,6 +176,10 @@ end f(x, y) = x .* y @inferred f(x, x) @test f(x, x) isa Vector{<:Quantity{Float64,<:StaticDimensions}} + + # Should automatically promote to regular Dimensions + z = [1.0u"m", Quantity{Float64,StaticDimensions}(1.0u"m")] + @test z isa Vector{<:Quantity{Float64,<:Dimensions}} end @testitem "Using zero and oneunit now work" begin From 6afba7c2824aa466eafb83a84d27c4908cbde7c5 Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Sun, 12 May 2024 01:48:29 +0100 Subject: [PATCH 12/14] fix: type instability in `sum` --- src/static_dimensions.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/static_dimensions.jl b/src/static_dimensions.jl index 5007d445..234b43d1 100644 --- a/src/static_dimensions.jl +++ b/src/static_dimensions.jl @@ -79,10 +79,10 @@ end # Utilities #################################################################### ################################################################################ function Base.zero(::Type{Q}) where {dim,D<:AbstractStaticDimensions{<:Any,<:Any,dim},T,Q<:AbstractQuantity{T,D}} - return new_quantity(Q, zero(T), dim) + return new_quantity(Q, zero(T), StaticDimensions(dim)) end function Base.oneunit(::Type{Q}) where {dim,D<:AbstractStaticDimensions{<:Any,<:Any,dim},T,Q<:AbstractQuantity{T,D}} - return new_quantity(Q, one(T), dim) + return new_quantity(Q, one(T), StaticDimensions(dim)) end function Base.show(io::IO, ::Type{StaticDimensions{R,D,dim}}) where {R,D,dim} print(io, "StaticDimensions($dim::$D)") @@ -187,6 +187,8 @@ end using DynamicQuantities: StaticDimensions x = Quantity{Float64,StaticDimensions}(u"km") + @test zero(x) isa Quantity{Float64,<:StaticDimensions} + @test zero(typeof(x)) isa Quantity{Float64,<:StaticDimensions} @test zero(typeof(x)) == 0 * x @test dimension(zero(typeof(x))) == dimension(x) @test dimension(zero(x)) == dimension(x) From ea2d763ae7e4660b455a577d5896315276861457 Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Sat, 8 Mar 2025 21:04:31 +0000 Subject: [PATCH 13/14] fix: mark static dimension constructor unstable --- src/static_dimensions.jl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/static_dimensions.jl b/src/static_dimensions.jl index 234b43d1..0850b3a2 100644 --- a/src/static_dimensions.jl +++ b/src/static_dimensions.jl @@ -1,3 +1,5 @@ +using DispatchDoctor: @unstable + abstract type AbstractStaticDimensions{R,D,dim} <: AbstractDimensions{R} end """ @@ -10,9 +12,9 @@ This is not yet stable, so this type is not exported. """ struct StaticDimensions{R,D<:AbstractDimensions{R},dim} <: AbstractStaticDimensions{R,D,dim} - StaticDimensions(d::AbstractDimensions) = new{eltype(d),typeof(d),d}() - StaticDimensions(; kws...) = StaticDimensions(Dimensions(; kws...)) - StaticDimensions{_R}(d::AbstractDimensions) where {_R} = (d = convert(with_type_parameters(typeof(d), _R), d); StaticDimensions(d)) + @unstable StaticDimensions(d::AbstractDimensions) = new{eltype(d),typeof(d),d}() + @unstable StaticDimensions(; kws...) = StaticDimensions(Dimensions(; kws...)) + @unstable StaticDimensions{_R}(d::AbstractDimensions) where {_R} = (d = convert(with_type_parameters(typeof(d), _R), d); StaticDimensions(d)) end Base.propertynames(::AbstractStaticDimensions{R,D,dim}) where {R,D,dim} = propertynames(dim) @@ -22,7 +24,7 @@ Base.getindex(::AbstractStaticDimensions{R,D,dim}, i::Symbol) where {R,D,dim} = raw_dimension(::Type{<:AbstractStaticDimensions{R,D,dim}}) where {R,D,dim} = dim dimension_names(::Type{<:AbstractStaticDimensions{R,D}}) where {R,D} = dimension_names(D) -constructorof(::Type{<:StaticDimensions}) = StaticDimensions +@unstable constructorof(::Type{<:StaticDimensions}) = StaticDimensions with_type_parameters(::Type{StaticDimensions{Rold,D}}, ::Type{R}) where {Rold,D,R} = StaticDimensions{R,D} function Base.promote_rule(::Type{StaticDimensions{R1,D1}}, ::Type{StaticDimensions{R2,D2}}) where {R1,D1,R2,D2} From e4f45c2c127ae03d6ae2124e91dd5980f25d3f12 Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Sat, 8 Mar 2025 21:11:22 +0000 Subject: [PATCH 14/14] refactor: use accessor --- src/static_dimensions.jl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/static_dimensions.jl b/src/static_dimensions.jl index 0850b3a2..71f84d3c 100644 --- a/src/static_dimensions.jl +++ b/src/static_dimensions.jl @@ -49,20 +49,19 @@ end function Base.convert(::Type{Q1}, q::Q2) where { T1,D1,Q1<:AbstractQuantity{T1,D1}, - dims, - Q2<:AbstractQuantity{<:Any,<:AbstractStaticDimensions{<:Any,<:Any,dims}} + Q2<:AbstractQuantity{<:Any,<:AbstractStaticDimensions} } if q isa Q1 return q end val = ustrip(q) # First, construct dynamic version in the input types - q_dynamic = new_quantity(Q2, val, dims) + q_dynamic = new_quantity(Q2, val, raw_dimension(dimension(q))) # Then, convert that return convert(Q1, q_dynamic) end -Base.:(==)(::AbstractStaticDimensions{<:Any,<:Any,dim1}, ::AbstractStaticDimensions{<:Any,<:Any,dim2}) where {dim1,dim2} = dim1 == dim2 +Base.:(==)(d1::AbstractStaticDimensions, d2::AbstractStaticDimensions) = raw_dimension(d1) == raw_dimension(d2) @generated function map_dimensions(f::F, args::AbstractStaticDimensions...) where {F<:Function} cons = constructorof(promote_type(args...)) dims = map(raw_dimension, args)