unxt 0.1.dev463__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of unxt might be problematic. Click here for more details.
- unxt/__init__.py +79 -0
- unxt/_interop/__init__.py +18 -0
- unxt/_interop/optional_deps.py +13 -0
- unxt/_interop/unxt_interop_astropy/__init__.py +5 -0
- unxt/_interop/unxt_interop_astropy/custom_types.py +12 -0
- unxt/_interop/unxt_interop_astropy/dimensions.py +53 -0
- unxt/_interop/unxt_interop_astropy/quantity.py +308 -0
- unxt/_interop/unxt_interop_astropy/units.py +111 -0
- unxt/_interop/unxt_interop_gala/__init__.py +5 -0
- unxt/_interop/unxt_interop_gala/unitsystems.py +108 -0
- unxt/_interop/unxt_interop_mpl/__init__.py +81 -0
- unxt/_src/__init__.py +6 -0
- unxt/_src/dimensions.py +400 -0
- unxt/_src/experimental.py +234 -0
- unxt/_src/quantity/__init__.py +24 -0
- unxt/_src/quantity/angle.py +58 -0
- unxt/_src/quantity/base.py +1160 -0
- unxt/_src/quantity/base_angle.py +89 -0
- unxt/_src/quantity/base_parametric.py +290 -0
- unxt/_src/quantity/flag.py +137 -0
- unxt/_src/quantity/mixins.py +238 -0
- unxt/_src/quantity/quantity.py +123 -0
- unxt/_src/quantity/register_api.py +383 -0
- unxt/_src/quantity/register_conversions.py +95 -0
- unxt/_src/quantity/register_dispatches.py +246 -0
- unxt/_src/quantity/register_primitives.py +5075 -0
- unxt/_src/quantity/static_quantity.py +90 -0
- unxt/_src/quantity/unchecked.py +45 -0
- unxt/_src/quantity/value.py +289 -0
- unxt/_src/units.py +103 -0
- unxt/_src/unitsystems/__init__.py +40 -0
- unxt/_src/unitsystems/base.py +258 -0
- unxt/_src/unitsystems/builtin.py +190 -0
- unxt/_src/unitsystems/builtin_dimensions.py +21 -0
- unxt/_src/unitsystems/compare.py +13 -0
- unxt/_src/unitsystems/core.py +307 -0
- unxt/_src/unitsystems/flags.py +80 -0
- unxt/_src/unitsystems/realizations.py +73 -0
- unxt/_src/unitsystems/utils.py +95 -0
- unxt/_src/utils.py +112 -0
- unxt/_version.py +34 -0
- unxt/_version.pyi +2 -0
- unxt/dims.py +19 -0
- unxt/py.typed +0 -0
- unxt/quantity.py +172 -0
- unxt/setup_package.py +60 -0
- unxt/units.py +19 -0
- unxt/unitsystems.py +67 -0
- unxt-0.1.dev463.dist-info/METADATA +533 -0
- unxt-0.1.dev463.dist-info/RECORD +52 -0
- unxt-0.1.dev463.dist-info/WHEEL +4 -0
- unxt-0.1.dev463.dist-info/licenses/LICENSE +29 -0
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
"""Functional approach to Quantities."""
|
|
2
|
+
|
|
3
|
+
__all__: tuple[str, ...] = ()
|
|
4
|
+
|
|
5
|
+
from dataclasses import replace
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import equinox as eqx
|
|
9
|
+
import numpy as np
|
|
10
|
+
from astropy.units import UnitConversionError
|
|
11
|
+
from jaxtyping import Array, ArrayLike
|
|
12
|
+
from plum import dispatch
|
|
13
|
+
|
|
14
|
+
import unxt_api as uapi
|
|
15
|
+
from .base import AbstractQuantity
|
|
16
|
+
from .base_angle import AbstractAngle
|
|
17
|
+
from .base_parametric import AbstractParametricQuantity
|
|
18
|
+
from .static_quantity import StaticQuantity
|
|
19
|
+
from .value import StaticValue
|
|
20
|
+
from unxt.dims import AbstractDimension
|
|
21
|
+
from unxt.units import AbstractUnit
|
|
22
|
+
from unxt.unitsystems import AbstractUnitSystem
|
|
23
|
+
|
|
24
|
+
# ===================================================================
|
|
25
|
+
# Get dimensions
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dispatch
|
|
29
|
+
def dimension_of(obj: AbstractQuantity, /) -> AbstractDimension:
|
|
30
|
+
"""Return the dimension of a quantity.
|
|
31
|
+
|
|
32
|
+
Examples
|
|
33
|
+
--------
|
|
34
|
+
>>> import unxt as u
|
|
35
|
+
>>> q = u.Q(1, "m")
|
|
36
|
+
>>> u.dimension_of(q)
|
|
37
|
+
PhysicalType('length')
|
|
38
|
+
|
|
39
|
+
"""
|
|
40
|
+
return uapi.dimension_of(obj.unit)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dispatch
|
|
44
|
+
def dimension_of(obj: type[AbstractParametricQuantity], /) -> AbstractDimension:
|
|
45
|
+
"""Return the dimension of a quantity.
|
|
46
|
+
|
|
47
|
+
Examples
|
|
48
|
+
--------
|
|
49
|
+
>>> import unxt as u
|
|
50
|
+
|
|
51
|
+
>>> try:
|
|
52
|
+
... u.dimension_of(u.Quantity)
|
|
53
|
+
... except Exception as e:
|
|
54
|
+
... print(e)
|
|
55
|
+
can only get dimensions from parametrized Quantity -- Quantity[dim].
|
|
56
|
+
|
|
57
|
+
>>> u.dimension_of(u.Quantity["length"])
|
|
58
|
+
PhysicalType('length')
|
|
59
|
+
|
|
60
|
+
"""
|
|
61
|
+
obj = eqx.error_if(
|
|
62
|
+
obj,
|
|
63
|
+
not hasattr(obj, "_type_parameter"),
|
|
64
|
+
f"can only get dimensions from parametrized {obj.__name__} "
|
|
65
|
+
f"-- {obj.__name__}[dim].",
|
|
66
|
+
)
|
|
67
|
+
return obj._type_parameter # noqa: SLF001
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@dispatch
|
|
71
|
+
def dimension_of(obj: type[AbstractAngle], /) -> AbstractDimension:
|
|
72
|
+
"""Get the dimension of an angle class.
|
|
73
|
+
|
|
74
|
+
Examples
|
|
75
|
+
--------
|
|
76
|
+
>>> import unxt as u
|
|
77
|
+
|
|
78
|
+
>>> u.dimension_of(u.Angle)
|
|
79
|
+
PhysicalType('angle')
|
|
80
|
+
|
|
81
|
+
"""
|
|
82
|
+
return uapi.dimension("angle")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# ===================================================================
|
|
86
|
+
# Get units
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@dispatch
|
|
90
|
+
def unit_of(obj: AbstractQuantity, /) -> AbstractUnit:
|
|
91
|
+
"""Return the units of an object.
|
|
92
|
+
|
|
93
|
+
Examples
|
|
94
|
+
--------
|
|
95
|
+
>>> from unxt import unit_of, Quantity
|
|
96
|
+
>>> q = Quantity(1, "m")
|
|
97
|
+
>>> unit_of(q)
|
|
98
|
+
Unit("m")
|
|
99
|
+
|
|
100
|
+
"""
|
|
101
|
+
return obj.unit
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# ===================================================================
|
|
105
|
+
# Convert units for values
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@dispatch
|
|
109
|
+
def uconvert_value(
|
|
110
|
+
tousys: AbstractUnitSystem, ufrom: AbstractUnit, x: ArrayLike, /
|
|
111
|
+
) -> ArrayLike:
|
|
112
|
+
"""Convert the value from units to a unitsystem's preferred units.
|
|
113
|
+
|
|
114
|
+
Examples
|
|
115
|
+
--------
|
|
116
|
+
>>> import unxt as u
|
|
117
|
+
>>> u.uconvert_value(u.unitsystems.galactic, u.unit("km"), 1e17) # kpc
|
|
118
|
+
3.2407792894443648
|
|
119
|
+
|
|
120
|
+
>>> u.unitsystems.galactic[u.dimension("length")] # checking the units
|
|
121
|
+
Unit("kpc")
|
|
122
|
+
|
|
123
|
+
"""
|
|
124
|
+
return uapi.uconvert_value(tousys[uapi.dimension_of(ufrom)], ufrom, x)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@dispatch
|
|
128
|
+
def uconvert_value(
|
|
129
|
+
tousys: AbstractUnitSystem, ufrom: str, x: ArrayLike, /
|
|
130
|
+
) -> ArrayLike:
|
|
131
|
+
"""Convert the value from units to a unitsystem's preferred units.
|
|
132
|
+
|
|
133
|
+
Examples
|
|
134
|
+
--------
|
|
135
|
+
>>> import unxt as u
|
|
136
|
+
>>> u.uconvert_value(u.unitsystems.galactic, "km", 1e17) # in kpc
|
|
137
|
+
3.2407792894443648
|
|
138
|
+
|
|
139
|
+
>>> u.unitsystems.galactic[u.dimension("length")] # checking the units
|
|
140
|
+
Unit("kpc")
|
|
141
|
+
|
|
142
|
+
"""
|
|
143
|
+
return uapi.uconvert_value(tousys, uapi.unit(ufrom), x)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@dispatch
|
|
147
|
+
def uconvert_value(uto: str, ufrom: str, x: ArrayLike, /) -> ArrayLike:
|
|
148
|
+
"""Convert the value to the specified units.
|
|
149
|
+
|
|
150
|
+
Examples
|
|
151
|
+
--------
|
|
152
|
+
>>> import unxt as u
|
|
153
|
+
|
|
154
|
+
>>> u.uconvert_value("m", "km", 1)
|
|
155
|
+
1000.0
|
|
156
|
+
|
|
157
|
+
"""
|
|
158
|
+
return uapi.uconvert_value(uapi.unit(uto), uapi.unit(ufrom), x)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
# **NOTE:** we also add convenience dispatches for `uconvert_value` so that
|
|
162
|
+
# users can use the lower-level function with Quantity and not break their code.
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@dispatch
|
|
166
|
+
def uconvert_value(uto: Any, ufrom: Any, x: AbstractQuantity, /) -> AbstractQuantity:
|
|
167
|
+
"""Convert the quantity to the specified units.
|
|
168
|
+
|
|
169
|
+
This is a convenience dispatch so that users can use the lower-level
|
|
170
|
+
function with Quantity and not break their code.
|
|
171
|
+
This dispatch simply calls `uconvert`, checking first that the units are
|
|
172
|
+
convertible.
|
|
173
|
+
|
|
174
|
+
Examples
|
|
175
|
+
--------
|
|
176
|
+
>>> import unxt as u
|
|
177
|
+
|
|
178
|
+
>>> q = u.Q(1, "km")
|
|
179
|
+
>>> u.uconvert_value("m", "km", q)
|
|
180
|
+
Quantity(Array(1000., dtype=float32, ...), unit='m')
|
|
181
|
+
|
|
182
|
+
"""
|
|
183
|
+
x = eqx.error_if(
|
|
184
|
+
x,
|
|
185
|
+
not uapi.is_unit_convertible(ufrom, x.unit),
|
|
186
|
+
f"Cannot convert from {x.unit} to {ufrom}",
|
|
187
|
+
)
|
|
188
|
+
return uapi.uconvert(uto, x)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
# ===================================================================
|
|
192
|
+
# Convert units for quantities
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@dispatch
|
|
196
|
+
def uconvert(ustr: str, x: AbstractQuantity, /) -> AbstractQuantity:
|
|
197
|
+
"""Convert the quantity to the specified units.
|
|
198
|
+
|
|
199
|
+
Examples
|
|
200
|
+
--------
|
|
201
|
+
>>> from unxt import Quantity, units
|
|
202
|
+
|
|
203
|
+
>>> x = Quantity(1000, "m")
|
|
204
|
+
>>> uconvert("km", x)
|
|
205
|
+
Quantity(Array(1., dtype=float32, ...), unit='km')
|
|
206
|
+
|
|
207
|
+
"""
|
|
208
|
+
return uconvert(uapi.unit(ustr), x)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@dispatch
|
|
212
|
+
def uconvert(usys: AbstractUnitSystem, x: AbstractQuantity, /) -> AbstractQuantity:
|
|
213
|
+
"""Convert the quantity to the specified units.
|
|
214
|
+
|
|
215
|
+
Examples
|
|
216
|
+
--------
|
|
217
|
+
>>> from unxt import Quantity, units
|
|
218
|
+
>>> from unxt.unitsystems import galactic
|
|
219
|
+
|
|
220
|
+
>>> q = Quantity(1e17, "km")
|
|
221
|
+
>>> uconvert(galactic, q)
|
|
222
|
+
Quantity(Array(3.2407792, dtype=float32, ...), unit='kpc')
|
|
223
|
+
|
|
224
|
+
"""
|
|
225
|
+
return uconvert(usys[uapi.dimension_of(x)], x)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
# ===================================================================
|
|
229
|
+
# Strip units
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
@dispatch
|
|
233
|
+
def ustrip(x: StaticQuantity, /) -> np.ndarray:
|
|
234
|
+
"""Strip the units from a static quantity."""
|
|
235
|
+
return x.value.array
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
@dispatch
|
|
239
|
+
def ustrip(x: AbstractQuantity, /) -> Array | np.ndarray:
|
|
240
|
+
"""Strip the units from the quantity.
|
|
241
|
+
|
|
242
|
+
Examples
|
|
243
|
+
--------
|
|
244
|
+
>>> import unxt as u
|
|
245
|
+
|
|
246
|
+
>>> q = u.Q(1000, "m")
|
|
247
|
+
>>> u.ustrip(q)
|
|
248
|
+
Array(1000, dtype=int32, weak_type=True)
|
|
249
|
+
|
|
250
|
+
>>> u.ustrip(q) is q.value
|
|
251
|
+
True
|
|
252
|
+
|
|
253
|
+
"""
|
|
254
|
+
v = x.value
|
|
255
|
+
return v.array if isinstance(v, StaticValue) else v
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
@dispatch
|
|
259
|
+
def ustrip(u: AbstractUnit, x: AbstractQuantity, /) -> Array | np.ndarray:
|
|
260
|
+
"""Strip the units from the quantity.
|
|
261
|
+
|
|
262
|
+
Examples
|
|
263
|
+
--------
|
|
264
|
+
>>> import unxt as u
|
|
265
|
+
|
|
266
|
+
>>> q = u.Q(1000, "m")
|
|
267
|
+
>>> u.ustrip(u.unit("km"), q)
|
|
268
|
+
Array(1., dtype=float32, ...)
|
|
269
|
+
|
|
270
|
+
"""
|
|
271
|
+
return uapi.ustrip(uapi.uconvert(u, x))
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
@dispatch
|
|
275
|
+
def ustrip(u: str, x: AbstractQuantity, /) -> Array | np.ndarray:
|
|
276
|
+
"""Strip the units from the quantity.
|
|
277
|
+
|
|
278
|
+
Examples
|
|
279
|
+
--------
|
|
280
|
+
>>> import unxt as u
|
|
281
|
+
|
|
282
|
+
>>> q = u.Q(1000, "m")
|
|
283
|
+
>>> u.ustrip("km", q)
|
|
284
|
+
Array(1., dtype=float32, ...)
|
|
285
|
+
|
|
286
|
+
"""
|
|
287
|
+
return uapi.ustrip(uapi.unit(u), x)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
@dispatch
|
|
291
|
+
def ustrip(u: AbstractUnitSystem, x: AbstractQuantity, /) -> Array | np.ndarray:
|
|
292
|
+
"""Strip the units from the quantity.
|
|
293
|
+
|
|
294
|
+
Examples
|
|
295
|
+
--------
|
|
296
|
+
>>> import unxt as u
|
|
297
|
+
>>> from unxt.unitsystems import galactic
|
|
298
|
+
|
|
299
|
+
>>> q = u.Q(1e17, "km")
|
|
300
|
+
>>> u.ustrip(galactic, q)
|
|
301
|
+
Array(3.2407792, dtype=float32, weak_type=True)
|
|
302
|
+
|
|
303
|
+
"""
|
|
304
|
+
return uapi.ustrip(u[uapi.dimension_of(x)], x)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
# ============================================================================
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
@dispatch
|
|
311
|
+
def is_unit_convertible(to_unit: Any, from_: Any, /) -> bool:
|
|
312
|
+
"""Check if a unit can be converted to another unit.
|
|
313
|
+
|
|
314
|
+
Parameters
|
|
315
|
+
----------
|
|
316
|
+
to_unit : Any
|
|
317
|
+
The unit to convert to. Converted to a unit object using `unxt.unit`.
|
|
318
|
+
from_ : Any
|
|
319
|
+
The unit to convert from. Converted to a unit object using `unxt.unit`,
|
|
320
|
+
Note this means it also support `Quantity` objects and many others.
|
|
321
|
+
|
|
322
|
+
Examples
|
|
323
|
+
--------
|
|
324
|
+
>>> from unxt import is_unit_convertible
|
|
325
|
+
>>> is_unit_convertible("cm", "m")
|
|
326
|
+
True
|
|
327
|
+
|
|
328
|
+
>>> is_unit_convertible("m", "Gyr")
|
|
329
|
+
False
|
|
330
|
+
|
|
331
|
+
"""
|
|
332
|
+
to_u = uapi.unit(to_unit)
|
|
333
|
+
from_u = uapi.unit(from_)
|
|
334
|
+
try:
|
|
335
|
+
from_u.to(to_u)
|
|
336
|
+
except UnitConversionError:
|
|
337
|
+
return False
|
|
338
|
+
return True
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
@dispatch
|
|
342
|
+
def is_unit_convertible(to_unit: Any, from_: AbstractQuantity, /) -> bool:
|
|
343
|
+
"""Check if a Quantity can be converted to another unit.
|
|
344
|
+
|
|
345
|
+
Examples
|
|
346
|
+
--------
|
|
347
|
+
>>> from unxt import Quantity, is_unit_convertible
|
|
348
|
+
>>> q = Quantity(1, "m")
|
|
349
|
+
|
|
350
|
+
>>> is_unit_convertible("cm", q)
|
|
351
|
+
True
|
|
352
|
+
|
|
353
|
+
>>> is_unit_convertible("Gyr", q)
|
|
354
|
+
False
|
|
355
|
+
|
|
356
|
+
"""
|
|
357
|
+
return is_unit_convertible(to_unit, from_.unit)
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
# ============================================================================
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
@dispatch
|
|
364
|
+
def wrap_to(
|
|
365
|
+
angle: AbstractQuantity,
|
|
366
|
+
min: AbstractQuantity,
|
|
367
|
+
max: AbstractQuantity,
|
|
368
|
+
/,
|
|
369
|
+
) -> AbstractQuantity:
|
|
370
|
+
"""Wrap to the range [min, max).
|
|
371
|
+
|
|
372
|
+
Examples
|
|
373
|
+
--------
|
|
374
|
+
>>> import unxt as u
|
|
375
|
+
|
|
376
|
+
>>> angle = u.Angle(370, "deg")
|
|
377
|
+
>>> u.quantity.wrap_to(angle, min=u.Q(0, "deg"), max=u.Q(360, "deg"))
|
|
378
|
+
Angle(Array(10, dtype=int32, ...), unit='deg')
|
|
379
|
+
|
|
380
|
+
"""
|
|
381
|
+
minv, maxv = min.ustrip(angle.unit), max.ustrip(angle.unit)
|
|
382
|
+
value = ((angle.value - minv) % (maxv - minv)) + minv
|
|
383
|
+
return replace(angle, value=value)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Compatibility for Quantity."""
|
|
2
|
+
|
|
3
|
+
__all__: tuple[str, ...] = ()
|
|
4
|
+
|
|
5
|
+
from plum import conversion_method
|
|
6
|
+
|
|
7
|
+
from .angle import Angle
|
|
8
|
+
from .base import AbstractQuantity
|
|
9
|
+
from .quantity import Quantity
|
|
10
|
+
from .unchecked import BareQuantity
|
|
11
|
+
from unxt_api import unit_of, ustrip
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@conversion_method(type_from=AbstractQuantity, type_to=BareQuantity) # type: ignore[arg-type]
|
|
15
|
+
def quantity_to_unchecked(q: AbstractQuantity, /) -> BareQuantity:
|
|
16
|
+
"""Convert any quantity to an unchecked quantity.
|
|
17
|
+
|
|
18
|
+
Examples
|
|
19
|
+
--------
|
|
20
|
+
>>> from plum import convert
|
|
21
|
+
>>> from unxt.quantity import Quantity, BareQuantity
|
|
22
|
+
|
|
23
|
+
>>> q = Quantity(1, "m")
|
|
24
|
+
>>> convert(q, BareQuantity)
|
|
25
|
+
BareQuantity(Array(1, dtype=int32, weak_type=True), unit='m')
|
|
26
|
+
|
|
27
|
+
The self-conversion doesn't copy the object:
|
|
28
|
+
|
|
29
|
+
>>> q = BareQuantity(1, "m")
|
|
30
|
+
>>> convert(q, BareQuantity) is q
|
|
31
|
+
True
|
|
32
|
+
|
|
33
|
+
"""
|
|
34
|
+
if isinstance(q, BareQuantity):
|
|
35
|
+
return q
|
|
36
|
+
u = unit_of(q)
|
|
37
|
+
return BareQuantity(ustrip(u, q), u)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@conversion_method(type_from=AbstractQuantity, type_to=Quantity) # type: ignore[arg-type]
|
|
41
|
+
def quantity_to_checked(q: AbstractQuantity, /) -> Quantity:
|
|
42
|
+
"""Convert any quantity to a checked quantity.
|
|
43
|
+
|
|
44
|
+
Examples
|
|
45
|
+
--------
|
|
46
|
+
>>> from plum import convert
|
|
47
|
+
>>> from unxt.quantity import Quantity, BareQuantity
|
|
48
|
+
|
|
49
|
+
>>> q = BareQuantity(1, "m")
|
|
50
|
+
>>> q
|
|
51
|
+
BareQuantity(Array(1, dtype=int32, ...), unit='m')
|
|
52
|
+
|
|
53
|
+
>>> convert(q, Quantity)
|
|
54
|
+
Quantity(Array(1, dtype=int32, ...), unit='m')
|
|
55
|
+
|
|
56
|
+
The self-conversion doesn't copy the object:
|
|
57
|
+
|
|
58
|
+
>>> q = Quantity(1, "m")
|
|
59
|
+
>>> convert(q, Quantity) is q
|
|
60
|
+
True
|
|
61
|
+
|
|
62
|
+
"""
|
|
63
|
+
if isinstance(q, Quantity):
|
|
64
|
+
return q
|
|
65
|
+
u = unit_of(q)
|
|
66
|
+
return Quantity(ustrip(u, q), u)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@conversion_method(type_from=AbstractQuantity, type_to=Angle) # type: ignore[arg-type]
|
|
70
|
+
def convert_quantity_to_angle(q: AbstractQuantity, /) -> Angle:
|
|
71
|
+
"""Convert any quantity to an Angle.
|
|
72
|
+
|
|
73
|
+
Examples
|
|
74
|
+
--------
|
|
75
|
+
>>> from plum import convert
|
|
76
|
+
>>> from unxt.quantity import Angle, BareQuantity
|
|
77
|
+
>>> q = BareQuantity(1, "rad")
|
|
78
|
+
>>> q
|
|
79
|
+
BareQuantity(Array(1, dtype=int32, ...), unit='rad')
|
|
80
|
+
|
|
81
|
+
>>> convert(q, Angle)
|
|
82
|
+
Angle(Array(1, dtype=int32, ...), unit='rad')
|
|
83
|
+
|
|
84
|
+
The self-conversion doesn't copy the object:
|
|
85
|
+
|
|
86
|
+
>>> q = Angle(1, "rad")
|
|
87
|
+
>>> convert(q, Angle) is q
|
|
88
|
+
True
|
|
89
|
+
|
|
90
|
+
"""
|
|
91
|
+
if isinstance(q, Angle):
|
|
92
|
+
return q
|
|
93
|
+
|
|
94
|
+
unit = unit_of(q)
|
|
95
|
+
return Angle(ustrip(unit, q), unit)
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"""Register dispatches for `quaxed.numpy`."""
|
|
2
|
+
# pylint: disable=import-error
|
|
3
|
+
|
|
4
|
+
__all__: tuple[str, ...] = ()
|
|
5
|
+
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import jax
|
|
9
|
+
import jax.numpy as jax_xp
|
|
10
|
+
from jaxtyping import ArrayLike
|
|
11
|
+
from plum import dispatch, type_unparametrized as type_np
|
|
12
|
+
|
|
13
|
+
from .base import AbstractQuantity
|
|
14
|
+
from .quantity import Quantity
|
|
15
|
+
from unxt_api import ustrip
|
|
16
|
+
|
|
17
|
+
# -----------------------------------------------
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dispatch
|
|
21
|
+
def arange(
|
|
22
|
+
start: AbstractQuantity,
|
|
23
|
+
stop: AbstractQuantity | None = None,
|
|
24
|
+
step: AbstractQuantity | None = None,
|
|
25
|
+
**kwargs: Any,
|
|
26
|
+
) -> AbstractQuantity:
|
|
27
|
+
"""Return evenly spaced values within a given interval.
|
|
28
|
+
|
|
29
|
+
Examples
|
|
30
|
+
--------
|
|
31
|
+
>>> import quaxed.numpy as jnp
|
|
32
|
+
>>> from unxt import Quantity
|
|
33
|
+
|
|
34
|
+
>>> jnp.arange(Quantity(5, "m"))
|
|
35
|
+
Quantity(Array([0, 1, 2, 3, 4], dtype=int32), unit='m')
|
|
36
|
+
|
|
37
|
+
>>> jnp.arange(Quantity(5, "m"), Quantity(10, "m"))
|
|
38
|
+
Quantity(Array([5, 6, 7, 8, 9], dtype=int32), unit='m')
|
|
39
|
+
|
|
40
|
+
>>> jnp.arange(Quantity(5, "m"), Quantity(10, "m"), Quantity(2, "m"))
|
|
41
|
+
Quantity(Array([5, 7, 9], dtype=int32), unit='m')
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
unit = start.unit
|
|
45
|
+
return type_np(start)(
|
|
46
|
+
jax_xp.arange(
|
|
47
|
+
start.value,
|
|
48
|
+
stop=ustrip(unit, stop) if stop is not None else None,
|
|
49
|
+
step=ustrip(unit, step) if step is not None else None,
|
|
50
|
+
**kwargs,
|
|
51
|
+
),
|
|
52
|
+
unit=unit,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# -----------------------------------------------
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dispatch
|
|
60
|
+
def empty_like(
|
|
61
|
+
x: AbstractQuantity, /, *, device: Any = None, **kwargs: Any
|
|
62
|
+
) -> AbstractQuantity:
|
|
63
|
+
"""Return a new array with the same shape and type as a given array.
|
|
64
|
+
|
|
65
|
+
Examples
|
|
66
|
+
--------
|
|
67
|
+
>>> import quaxed.numpy as jnp
|
|
68
|
+
>>> from unxt import Quantity
|
|
69
|
+
|
|
70
|
+
>>> jnp.empty_like(Quantity(5, "m"))
|
|
71
|
+
Quantity(Array(0, dtype=int32, ...), unit='m')
|
|
72
|
+
|
|
73
|
+
"""
|
|
74
|
+
out = type_np(x)(jax_xp.empty_like(x.value, **kwargs), unit=x.unit)
|
|
75
|
+
return jax.device_put(out, device=device)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# -----------------------------------------------
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@dispatch
|
|
82
|
+
def full(
|
|
83
|
+
shape: Any, fill_value: AbstractQuantity, /, **kwargs: Any
|
|
84
|
+
) -> AbstractQuantity:
|
|
85
|
+
"""Return a new array of given shape and type, filled with `fill_value`.
|
|
86
|
+
|
|
87
|
+
Examples
|
|
88
|
+
--------
|
|
89
|
+
>>> import quaxed.numpy as jnp
|
|
90
|
+
>>> from unxt import Quantity
|
|
91
|
+
|
|
92
|
+
>>> jnp.full((2, 2), Quantity(5, "m"))
|
|
93
|
+
Quantity(Array([[5, 5], [5, 5]], dtype=int32, ...), unit='m')
|
|
94
|
+
|
|
95
|
+
"""
|
|
96
|
+
fill_val = ustrip(fill_value.unit, fill_value)
|
|
97
|
+
return Quantity(jax_xp.full(shape, fill_val, **kwargs), unit=fill_value.unit)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# -----------------------------------------------
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@dispatch
|
|
104
|
+
def full_like(
|
|
105
|
+
x: AbstractQuantity, /, *, fill_value: Any, **kwargs: Any
|
|
106
|
+
) -> AbstractQuantity:
|
|
107
|
+
"""Return a new array with the same shape and type as a given array.
|
|
108
|
+
|
|
109
|
+
Examples
|
|
110
|
+
--------
|
|
111
|
+
>>> import quaxed.numpy as jnp
|
|
112
|
+
>>> from unxt import Quantity
|
|
113
|
+
|
|
114
|
+
>>> jnp.full_like(Quantity(5, "m"), fill_value=Quantity(10, "m"))
|
|
115
|
+
Quantity(Array(10, dtype=int32, ...), unit='m')
|
|
116
|
+
|
|
117
|
+
"""
|
|
118
|
+
# re-dispatch to the correct implementation
|
|
119
|
+
return full_like(x, fill_value, **kwargs)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@dispatch
|
|
123
|
+
def full_like(
|
|
124
|
+
x: AbstractQuantity, fill_value: ArrayLike, /, **kwargs: Any
|
|
125
|
+
) -> AbstractQuantity:
|
|
126
|
+
"""Return a new array with the same shape and type as a given array.
|
|
127
|
+
|
|
128
|
+
Examples
|
|
129
|
+
--------
|
|
130
|
+
>>> import quaxed.numpy as jnp
|
|
131
|
+
>>> from unxt import Quantity
|
|
132
|
+
|
|
133
|
+
>>> jnp.full_like(Quantity(5, "m"), 100.0)
|
|
134
|
+
Quantity(Array(100, dtype=int32, ...), unit='m')
|
|
135
|
+
|
|
136
|
+
"""
|
|
137
|
+
return type_np(x)(jax_xp.full_like(x.value, fill_value, **kwargs), unit=x.unit)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@dispatch
|
|
141
|
+
def full_like(
|
|
142
|
+
x: AbstractQuantity, fill_value: AbstractQuantity, /, **kwargs: Any
|
|
143
|
+
) -> AbstractQuantity:
|
|
144
|
+
"""Return a new array with the same shape and type as a given array.
|
|
145
|
+
|
|
146
|
+
Examples
|
|
147
|
+
--------
|
|
148
|
+
>>> import quaxed.numpy as jnp
|
|
149
|
+
>>> from unxt import Quantity
|
|
150
|
+
|
|
151
|
+
>>> jnp.full_like(Quantity(5, "m"), Quantity(10, "m"))
|
|
152
|
+
Quantity(Array(10, dtype=int32, ...), unit='m')
|
|
153
|
+
|
|
154
|
+
"""
|
|
155
|
+
fill_val = ustrip(x.unit, fill_value)
|
|
156
|
+
return type_np(x)(jax_xp.full_like(x.value, fill_val, **kwargs), unit=x.unit)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
# -----------------------------------------------
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
@dispatch
|
|
163
|
+
def linspace(
|
|
164
|
+
start: AbstractQuantity, stop: AbstractQuantity, num: Any, /, **kwargs: Any
|
|
165
|
+
) -> AbstractQuantity:
|
|
166
|
+
"""Return evenly spaced values within a given interval.
|
|
167
|
+
|
|
168
|
+
Examples
|
|
169
|
+
--------
|
|
170
|
+
>>> import quaxed.numpy as jnp
|
|
171
|
+
>>> from unxt import Quantity
|
|
172
|
+
|
|
173
|
+
>>> jnp.linspace(Quantity(0, "m"), Quantity(10, "m"), 5)
|
|
174
|
+
Quantity(Array([ 0. , 2.5, 5. , 7.5, 10. ], dtype=float32), unit='m')
|
|
175
|
+
|
|
176
|
+
"""
|
|
177
|
+
unit = start.unit
|
|
178
|
+
return type_np(start)(
|
|
179
|
+
jax_xp.linspace(ustrip(unit, start), ustrip(unit, stop), num, **kwargs),
|
|
180
|
+
unit=unit,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@dispatch
|
|
185
|
+
def linspace(
|
|
186
|
+
start: AbstractQuantity, stop: AbstractQuantity, /, **kwargs: Any
|
|
187
|
+
) -> AbstractQuantity:
|
|
188
|
+
"""Return evenly spaced values within a given interval.
|
|
189
|
+
|
|
190
|
+
Examples
|
|
191
|
+
--------
|
|
192
|
+
>>> import quaxed.numpy as jnp
|
|
193
|
+
>>> from unxt import Quantity
|
|
194
|
+
|
|
195
|
+
>>> jnp.linspace(Quantity(0, "m"), Quantity(10, "m"), num=5)
|
|
196
|
+
Quantity(Array([ 0. , 2.5, 5. , 7.5, 10. ], dtype=float32), unit='m')
|
|
197
|
+
|
|
198
|
+
"""
|
|
199
|
+
unit = start.unit
|
|
200
|
+
return type_np(start)(
|
|
201
|
+
jax_xp.linspace(ustrip(unit, start), ustrip(unit, stop), **kwargs), unit=unit
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
# -----------------------------------------------
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@dispatch
|
|
209
|
+
def ones_like(
|
|
210
|
+
x: AbstractQuantity, /, *, device: Any = None, **kwargs: Any
|
|
211
|
+
) -> AbstractQuantity:
|
|
212
|
+
"""Return a new array with the same shape and type as a given array.
|
|
213
|
+
|
|
214
|
+
Examples
|
|
215
|
+
--------
|
|
216
|
+
>>> import quaxed.numpy as jnp
|
|
217
|
+
>>> from unxt import Quantity
|
|
218
|
+
|
|
219
|
+
>>> jnp.ones_like(Quantity(5, "m"))
|
|
220
|
+
Quantity(Array(1, dtype=int32, ...), unit='m')
|
|
221
|
+
|
|
222
|
+
"""
|
|
223
|
+
out = type_np(x)(jax_xp.ones_like(x.value, **kwargs), unit=x.unit)
|
|
224
|
+
return jax.device_put(out, device=device)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
# -----------------------------------------------
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
@dispatch
|
|
231
|
+
def zeros_like(
|
|
232
|
+
x: AbstractQuantity, /, *, device: Any = None, **kwargs: Any
|
|
233
|
+
) -> AbstractQuantity:
|
|
234
|
+
"""Return a new array with the same shape and type as a given array.
|
|
235
|
+
|
|
236
|
+
Examples
|
|
237
|
+
--------
|
|
238
|
+
>>> import quaxed.numpy as jnp
|
|
239
|
+
>>> from unxt import Quantity
|
|
240
|
+
|
|
241
|
+
>>> jnp.zeros_like(Quantity(5, "m"))
|
|
242
|
+
Quantity(Array(0, dtype=int32, ...), unit='m')
|
|
243
|
+
|
|
244
|
+
"""
|
|
245
|
+
out = type_np(x)(jax_xp.zeros_like(x.value, **kwargs), unit=x.unit)
|
|
246
|
+
return jax.device_put(out, device=device)
|