1
1
import . Units: UNIT_SYMBOLS, UNIT_MAPPING, UNIT_VALUES
2
2
import . Constants: CONSTANT_SYMBOLS, CONSTANT_MAPPING, CONSTANT_VALUES
3
- import SparseArrays as SA
4
3
5
4
const SYMBOL_CONFLICTS = intersect (UNIT_SYMBOLS, CONSTANT_SYMBOLS)
6
5
6
+ const INDEX_TYPE = UInt8
7
7
# Prefer units over constants:
8
8
# For example, this means we can't have a symbolic Planck's constant,
9
9
# as it is just "hours" (h), which is more common.
@@ -19,7 +19,7 @@ const ALL_VALUES = vcat(
19
19
if k ∉ SYMBOL_CONFLICTS
20
20
). ..
21
21
)
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)])
23
23
24
24
"""
25
25
SymbolicDimensions{R} <: AbstractDimensions{R}
@@ -35,49 +35,60 @@ to one which uses `Dimensions` as its dimensions (i.e., base SI units)
35
35
`expand_units`.
36
36
"""
37
37
struct 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}
42
40
end
43
41
44
42
static_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)
48
56
constructor_of (:: Type{<:SymbolicDimensions} ) = SymbolicDimensions
49
57
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 ))
59
63
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... )
60
70
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)
75
84
end
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 () )
78
87
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
81
92
end
82
93
return result
83
94
end
@@ -120,15 +131,122 @@ For string input, `qout` is created by parsing `ustr` as a symbolic unit, i.e. `
120
131
as_u (qout:: AbstractQuantity{<:Any, <:SymbolicDimensions} ) = Base. Fix2 (as_u, qout)
121
132
as_u (ustr:: String ) = Base. Fix2 (as_u, ustr)
122
133
123
- Base. copy (d:: SymbolicDimensions ) = SymbolicDimensions (copy (data (d)))
124
- Base.:(== )(l:: SymbolicDimensions , r:: SymbolicDimensions ) = data (l) == data (r)
125
- Base. iszero (d:: SymbolicDimensions ) = iszero (data (d))
126
- Base.:* (l:: SymbolicDimensions , r:: SymbolicDimensions ) = SymbolicDimensions (data (l) + data (r))
127
- Base.:/ (l:: SymbolicDimensions , r:: SymbolicDimensions ) = SymbolicDimensions (data (l) - data (r))
128
- Base. inv (d:: SymbolicDimensions ) = SymbolicDimensions (- data (d))
129
- Base.:^ (l:: SymbolicDimensions{R} , r:: Integer ) where {R} = SymbolicDimensions (data (l) * r)
130
- Base.:^ (l:: SymbolicDimensions{R} , r:: Number ) where {R} = SymbolicDimensions (data (l) * tryrationalize (R, r))
134
+ Base. copy (d:: SymbolicDimensions ) = SymbolicDimensions (copy (getfield (d, :nzdims )), copy (getfield (d, :nzvals )))
135
+ function Base.:(== )(l:: SymbolicDimensions , r:: SymbolicDimensions )
136
+ nzdims_l = getfield (l, :nzdims )
137
+ nzvals_l = getfield (l, :nzvals )
138
+ nzdims_r = getfield (r, :nzdims )
139
+ nzvals_r = getfield (r, :nzvals )
140
+ nl = length (nzdims_l)
141
+ nr = length (nzdims_r)
142
+ il = ir = 1
143
+ while il <= nl && ir <= nr
144
+ dim_l = nzdims_l[il]
145
+ dim_r = nzdims_r[ir]
146
+ if dim_l == dim_r
147
+ if nzvals_l[il] != nzvals_r[ir]
148
+ return false
149
+ end
150
+ il += 1
151
+ ir += 1
152
+ elseif dim_l < dim_r
153
+ if ! iszero (nzvals_l[il])
154
+ return false
155
+ end
156
+ il += 1
157
+ else
158
+ if ! iszero (nzvals_r[ir])
159
+ return false
160
+ end
161
+ ir += 1
162
+ end
163
+ end
164
+
165
+ while il <= nl
166
+ if ! iszero (nzvals_l[il])
167
+ return false
168
+ end
169
+ il += 1
170
+ end
171
+
172
+ while ir <= nr
173
+ if ! iszero (nzvals_r[ir])
174
+ return false
175
+ end
176
+ ir += 1
177
+ end
178
+
179
+ return true
180
+ end
181
+ Base. iszero (d:: SymbolicDimensions ) = iszero (getfield (d, :nzvals ))
131
182
183
+ # Defines `inv(::SymbolicDimensions)` and `^(::SymbolicDimensions, ::Number)`
184
+ function map_dimensions (op:: Function , d:: SymbolicDimensions )
185
+ return SymbolicDimensions (copy (getfield (d, :nzdims )), map (op, getfield (d, :nzvals )))
186
+ end
187
+
188
+ # Defines `*(::SymbolicDimensions, ::SymbolicDimensions)` and `/(::SymbolicDimensions, ::SymbolicDimensions)`
189
+ function map_dimensions (op:: O , l:: SymbolicDimensions{L} , r:: SymbolicDimensions{R} ) where {O<: Function ,L,R}
190
+ zero_L = zero (L)
191
+ zero_R = zero (R)
192
+ T = typeof (op (zero (L), zero (R)))
193
+ I = Vector {INDEX_TYPE} (undef, 0 )
194
+ V = Vector {T} (undef, 0 )
195
+ nzdims_l = getfield (l, :nzdims )
196
+ nzvals_l = getfield (l, :nzvals )
197
+ nzdims_r = getfield (r, :nzdims )
198
+ nzvals_r = getfield (r, :nzvals )
199
+ nl = length (nzdims_l)
200
+ nr = length (nzdims_r)
201
+ il = ir = 1
202
+ while il <= nl && ir <= nr
203
+ dim_l = nzdims_l[il]
204
+ dim_r = nzdims_r[ir]
205
+ if dim_l == dim_r
206
+ s = op (nzvals_l[il], nzvals_r[ir])
207
+ if ! iszero (s)
208
+ push! (I, dim_l)
209
+ push! (V, s)
210
+ end
211
+ il += 1
212
+ ir += 1
213
+ elseif dim_l < dim_r
214
+ s = op (nzvals_l[il], zero_R)
215
+ if ! iszero (s)
216
+ push! (I, dim_l)
217
+ push! (V, s)
218
+ end
219
+ il += 1
220
+ else
221
+ s = op (zero_L, nzvals_r[ir])
222
+ if ! iszero (s)
223
+ push! (I, dim_r)
224
+ push! (V, s)
225
+ end
226
+ ir += 1
227
+ end
228
+ end
229
+
230
+ while il <= nl
231
+ s = op (nzvals_l[il], zero_R)
232
+ if ! iszero (s)
233
+ push! (I, nzdims_l[il])
234
+ push! (V, s)
235
+ end
236
+ il += 1
237
+ end
238
+
239
+ while ir <= nr
240
+ s = op (zero_L, nzvals_r[ir])
241
+ if ! iszero (s)
242
+ push! (I, nzdims_r[ir])
243
+ push! (V, s)
244
+ end
245
+ ir += 1
246
+ end
247
+
248
+ return SymbolicDimensions (I, V)
249
+ end
132
250
133
251
"""
134
252
SymbolicUnitsParse
0 commit comments