11import . Units: UNIT_SYMBOLS, UNIT_MAPPING, UNIT_VALUES
22import . Constants: CONSTANT_SYMBOLS, CONSTANT_MAPPING, CONSTANT_VALUES
3- import SparseArrays as SA
43
54const SYMBOL_CONFLICTS = intersect (UNIT_SYMBOLS, CONSTANT_SYMBOLS)
65
6+ const INDEX_TYPE = UInt8
77# Prefer units over constants:
88# For example, this means we can't have a symbolic Planck's constant,
99# as it is just "hours" (h), which is more common.
@@ -19,7 +19,7 @@ const ALL_VALUES = vcat(
1919 if k ∉ SYMBOL_CONFLICTS
2020 ). ..
2121)
22- const ALL_MAPPING = NamedTuple ([s => i for (i, s) in enumerate (ALL_SYMBOLS)])
22+ const ALL_MAPPING = NamedTuple ([s => INDEX_TYPE (i) for (i, s) in enumerate (ALL_SYMBOLS)])
2323
2424"""
2525 SymbolicDimensions{R} <: AbstractDimensions{R}
@@ -35,49 +35,60 @@ to one which uses `Dimensions` as its dimensions (i.e., base SI units)
3535`expand_units`.
3636"""
3737struct SymbolicDimensions{R} <: AbstractDimensions{R}
38- _data:: SA.SparseVector{R}
39-
40- SymbolicDimensions (data:: SA.SparseVector ) = new {eltype(data)} (data)
41- SymbolicDimensions {_R} (data:: SA.SparseVector ) where {_R} = new {_R} (data)
38+ nzdims:: Vector{INDEX_TYPE}
39+ nzvals:: Vector{R}
4240end
4341
4442static_fieldnames (:: Type{<:SymbolicDimensions} ) = ALL_SYMBOLS
45- data (d:: SymbolicDimensions ) = getfield (d, :_data )
46- Base. getproperty (d:: SymbolicDimensions{R} , s:: Symbol ) where {R} = data (d)[ALL_MAPPING[s]]
47- Base. getindex (d:: SymbolicDimensions{R} , k:: Symbol ) where {R} = getproperty (d, k)
43+ function Base. getproperty (d:: SymbolicDimensions{R} , s:: Symbol ) where {R}
44+ nzdims = getfield (d, :nzdims )
45+ i = get (ALL_MAPPING, s, INDEX_TYPE (0 ))
46+ iszero (i) && error (" $s is not available as a symbol in SymbolicDimensions. Symbols available: $(ALL_SYMBOLS) ." )
47+ ii = searchsortedfirst (nzdims, i)
48+ if ii <= length (nzdims) && nzdims[ii] == i
49+ return getfield (d, :nzvals )[ii]
50+ else
51+ return zero (R)
52+ end
53+ end
54+ Base. propertynames (:: SymbolicDimensions ) = ALL_SYMBOLS
55+ Base. getindex (d:: SymbolicDimensions , k:: Symbol ) = getproperty (d, k)
4856constructor_of (:: Type{<:SymbolicDimensions} ) = SymbolicDimensions
4957
50- SymbolicDimensions {R} (d:: SymbolicDimensions ) where {R} = SymbolicDimensions {R} (data (d))
51- (:: Type{D} )(; kws... ) where {D<: SymbolicDimensions } = D (DEFAULT_DIM_BASE_TYPE; kws... )
52- (:: Type{D} )(:: Type{R} ; kws... ) where {R,D<: SymbolicDimensions } =
53- let constructor= constructor_of (D){R}
54- length (kws) == 0 && return constructor (SA. spzeros (R, length (ALL_SYMBOLS)))
55- I = [ALL_MAPPING[s] for s in keys (kws)]
56- V = [tryrationalize (R, v) for v in values (kws)]
57- data = SA. sparsevec (I, V, length (ALL_SYMBOLS))
58- return constructor (data)
58+ SymbolicDimensions {R} (d:: SymbolicDimensions ) where {R} = SymbolicDimensions {R} (getfield (d, :nzdims ), convert (Vector{R}, getfield (d, :nzvals )))
59+ SymbolicDimensions (; kws... ) = SymbolicDimensions {DEFAULT_DIM_BASE_TYPE} (; kws... )
60+ function SymbolicDimensions {R} (; kws... ) where {R}
61+ if isempty (kws)
62+ return SymbolicDimensions {R} (Vector {INDEX_TYPE} (undef, 0 ), Vector {R} (undef, 0 ))
5963 end
64+ I = INDEX_TYPE[ALL_MAPPING[s] for s in keys (kws)]
65+ p = sortperm (I)
66+ V = R[tryrationalize (R, kws[i]) for i in p]
67+ return SymbolicDimensions {R} (permute! (I, p), V)
68+ end
69+ (:: Type{<:SymbolicDimensions} )(:: Type{R} ; kws... ) where {R} = SymbolicDimensions {R} (; kws... )
6070
61- function Base. convert (:: Type{Qout} , q:: Quantity{<:Any,<:Dimensions} ) where {T,D<: SymbolicDimensions ,Qout<: Quantity{T,D} }
62- output = Qout (
63- convert (T, ustrip (q)),
64- D;
65- m= ulength (q),
66- kg= umass (q),
67- s= utime (q),
68- A= ucurrent (q),
69- K= utemperature (q),
70- cd= uluminosity (q),
71- mol= uamount (q),
72- )
73- SA. dropzeros! (data (dimension (output)))
74- return output
71+ function Base. convert (:: Type{Quantity{T,SymbolicDimensions}} , q:: Quantity{<:Any,<:Dimensions} ) where {T}
72+ return convert (Quantity{T,SymbolicDimensions{DEFAULT_DIM_BASE_TYPE}}, q)
73+ end
74+ function Base. convert (:: Type{Quantity{T,SymbolicDimensions{R}}} , q:: Quantity{<:Any,<:Dimensions} ) where {T,R}
75+ syms = (:m , :kg , :s , :A , :K , :cd , :mol )
76+ vals = (ulength (q), umass (q), utime (q), ucurrent (q), utemperature (q), uluminosity (q), uamount (q))
77+ I = INDEX_TYPE[ALL_MAPPING[s] for (s, v) in zip (syms, vals) if ! iszero (v)]
78+ V = R[tryrationalize (R, v) for v in vals if ! iszero (v)]
79+ p = sortperm (I)
80+ permute! (I, p)
81+ permute! (V, p)
82+ dims = SymbolicDimensions {R} (I, V)
83+ return Quantity (convert (T, ustrip (q)), dims)
7584end
76- function Base. convert (:: Type{Q} , q:: Quantity{<:Any,<:SymbolicDimensions} ) where {T,D<: Dimensions ,Q <: Quantity{T,D} }
77- result = one (Q) * ustrip (q)
85+ function Base. convert (:: Type{Quantity{T,D}} , q:: Quantity{<:Any,<:SymbolicDimensions} ) where {T,D<: Dimensions }
86+ result = Quantity ( T ( ustrip (q)), D () )
7887 d = dimension (q)
79- for (idx, value) in zip (SA. findnz (data (d))... )
80- result = result * convert (Q, ALL_VALUES[idx]) ^ value
88+ for (idx, value) in zip (getfield (d, :nzdims ), getfield (d, :nzvals ))
89+ if ! iszero (value)
90+ result = result * convert (Quantity{T,D}, ALL_VALUES[idx]) ^ value
91+ end
8192 end
8293 return result
8394end
@@ -95,15 +106,122 @@ end
95106expand_units (q:: QuantityArray ) = expand_units .(q)
96107
97108
98- Base. copy (d:: SymbolicDimensions ) = SymbolicDimensions (copy (data (d)))
99- Base.:(== )(l:: SymbolicDimensions , r:: SymbolicDimensions ) = data (l) == data (r)
100- Base. iszero (d:: SymbolicDimensions ) = iszero (data (d))
101- Base.:* (l:: SymbolicDimensions , r:: SymbolicDimensions ) = SymbolicDimensions (data (l) + data (r))
102- Base.:/ (l:: SymbolicDimensions , r:: SymbolicDimensions ) = SymbolicDimensions (data (l) - data (r))
103- Base. inv (d:: SymbolicDimensions ) = SymbolicDimensions (- data (d))
104- Base.:^ (l:: SymbolicDimensions{R} , r:: Integer ) where {R} = SymbolicDimensions (data (l) * r)
105- Base.:^ (l:: SymbolicDimensions{R} , r:: Number ) where {R} = SymbolicDimensions (data (l) * tryrationalize (R, r))
109+ Base. copy (d:: SymbolicDimensions ) = SymbolicDimensions (copy (getfield (d, :nzdims )), copy (getfield (d, :nzvals )))
110+ function Base.:(== )(l:: SymbolicDimensions , r:: SymbolicDimensions )
111+ nzdims_l = getfield (l, :nzdims )
112+ nzvals_l = getfield (l, :nzvals )
113+ nzdims_r = getfield (r, :nzdims )
114+ nzvals_r = getfield (r, :nzvals )
115+ nl = length (nzdims_l)
116+ nr = length (nzdims_r)
117+ il = ir = 1
118+ while il <= nl && ir <= nr
119+ dim_l = nzdims_l[il]
120+ dim_r = nzdims_r[ir]
121+ if dim_l == dim_r
122+ if nzvals_l[il] != nzvals_r[ir]
123+ return false
124+ end
125+ il += 1
126+ ir += 1
127+ elseif dim_l < dim_r
128+ if ! iszero (nzvals_l[il])
129+ return false
130+ end
131+ il += 1
132+ else
133+ if ! iszero (nzvals_r[ir])
134+ return false
135+ end
136+ ir += 1
137+ end
138+ end
139+
140+ while il <= nl
141+ if ! iszero (nzvals_l[il])
142+ return false
143+ end
144+ il += 1
145+ end
146+
147+ while ir <= nr
148+ if ! iszero (nzvals_r[ir])
149+ return false
150+ end
151+ ir += 1
152+ end
153+
154+ return true
155+ end
156+ Base. iszero (d:: SymbolicDimensions ) = iszero (getfield (d, :nzvals ))
157+
158+ # Defines `inv(::SymbolicDimensions)` and `^(::SymbolicDimensions, ::Number)`
159+ function map_dimensions (op:: Function , d:: SymbolicDimensions )
160+ return SymbolicDimensions (copy (getfield (d, :nzdims )), map (op, getfield (d, :nzvals )))
161+ end
106162
163+ # Defines `*(::SymbolicDimensions, ::SymbolicDimensions)` and `/(::SymbolicDimensions, ::SymbolicDimensions)`
164+ function map_dimensions (op:: O , l:: SymbolicDimensions{L} , r:: SymbolicDimensions{R} ) where {O<: Function ,L,R}
165+ zero_L = zero (L)
166+ zero_R = zero (R)
167+ T = typeof (op (zero (L), zero (R)))
168+ I = Vector {INDEX_TYPE} (undef, 0 )
169+ V = Vector {T} (undef, 0 )
170+ nzdims_l = getfield (l, :nzdims )
171+ nzvals_l = getfield (l, :nzvals )
172+ nzdims_r = getfield (r, :nzdims )
173+ nzvals_r = getfield (r, :nzvals )
174+ nl = length (nzdims_l)
175+ nr = length (nzdims_r)
176+ il = ir = 1
177+ while il <= nl && ir <= nr
178+ dim_l = nzdims_l[il]
179+ dim_r = nzdims_r[ir]
180+ if dim_l == dim_r
181+ s = op (nzvals_l[il], nzvals_r[ir])
182+ if ! iszero (s)
183+ push! (I, dim_l)
184+ push! (V, s)
185+ end
186+ il += 1
187+ ir += 1
188+ elseif dim_l < dim_r
189+ s = op (nzvals_l[il], zero_R)
190+ if ! iszero (s)
191+ push! (I, dim_l)
192+ push! (V, s)
193+ end
194+ il += 1
195+ else
196+ s = op (zero_L, nzvals_r[ir])
197+ if ! iszero (s)
198+ push! (I, dim_r)
199+ push! (V, s)
200+ end
201+ ir += 1
202+ end
203+ end
204+
205+ while il <= nl
206+ s = op (nzvals_l[il], zero_R)
207+ if ! iszero (s)
208+ push! (I, nzdims_l[il])
209+ push! (V, s)
210+ end
211+ il += 1
212+ end
213+
214+ while ir <= nr
215+ s = op (zero_L, nzvals_r[ir])
216+ if ! iszero (s)
217+ push! (I, nzdims_r[ir])
218+ push! (V, s)
219+ end
220+ ir += 1
221+ end
222+
223+ return SymbolicDimensions (I, V)
224+ end
107225
108226"""
109227 SymbolicUnitsParse
0 commit comments