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.

Files changed (52) hide show
  1. unxt/__init__.py +79 -0
  2. unxt/_interop/__init__.py +18 -0
  3. unxt/_interop/optional_deps.py +13 -0
  4. unxt/_interop/unxt_interop_astropy/__init__.py +5 -0
  5. unxt/_interop/unxt_interop_astropy/custom_types.py +12 -0
  6. unxt/_interop/unxt_interop_astropy/dimensions.py +53 -0
  7. unxt/_interop/unxt_interop_astropy/quantity.py +308 -0
  8. unxt/_interop/unxt_interop_astropy/units.py +111 -0
  9. unxt/_interop/unxt_interop_gala/__init__.py +5 -0
  10. unxt/_interop/unxt_interop_gala/unitsystems.py +108 -0
  11. unxt/_interop/unxt_interop_mpl/__init__.py +81 -0
  12. unxt/_src/__init__.py +6 -0
  13. unxt/_src/dimensions.py +400 -0
  14. unxt/_src/experimental.py +234 -0
  15. unxt/_src/quantity/__init__.py +24 -0
  16. unxt/_src/quantity/angle.py +58 -0
  17. unxt/_src/quantity/base.py +1160 -0
  18. unxt/_src/quantity/base_angle.py +89 -0
  19. unxt/_src/quantity/base_parametric.py +290 -0
  20. unxt/_src/quantity/flag.py +137 -0
  21. unxt/_src/quantity/mixins.py +238 -0
  22. unxt/_src/quantity/quantity.py +123 -0
  23. unxt/_src/quantity/register_api.py +383 -0
  24. unxt/_src/quantity/register_conversions.py +95 -0
  25. unxt/_src/quantity/register_dispatches.py +246 -0
  26. unxt/_src/quantity/register_primitives.py +5075 -0
  27. unxt/_src/quantity/static_quantity.py +90 -0
  28. unxt/_src/quantity/unchecked.py +45 -0
  29. unxt/_src/quantity/value.py +289 -0
  30. unxt/_src/units.py +103 -0
  31. unxt/_src/unitsystems/__init__.py +40 -0
  32. unxt/_src/unitsystems/base.py +258 -0
  33. unxt/_src/unitsystems/builtin.py +190 -0
  34. unxt/_src/unitsystems/builtin_dimensions.py +21 -0
  35. unxt/_src/unitsystems/compare.py +13 -0
  36. unxt/_src/unitsystems/core.py +307 -0
  37. unxt/_src/unitsystems/flags.py +80 -0
  38. unxt/_src/unitsystems/realizations.py +73 -0
  39. unxt/_src/unitsystems/utils.py +95 -0
  40. unxt/_src/utils.py +112 -0
  41. unxt/_version.py +34 -0
  42. unxt/_version.pyi +2 -0
  43. unxt/dims.py +19 -0
  44. unxt/py.typed +0 -0
  45. unxt/quantity.py +172 -0
  46. unxt/setup_package.py +60 -0
  47. unxt/units.py +19 -0
  48. unxt/unitsystems.py +67 -0
  49. unxt-0.1.dev463.dist-info/METADATA +533 -0
  50. unxt-0.1.dev463.dist-info/RECORD +52 -0
  51. unxt-0.1.dev463.dist-info/WHEEL +4 -0
  52. unxt-0.1.dev463.dist-info/licenses/LICENSE +29 -0
@@ -0,0 +1,238 @@
1
+ """Orthogonal Mixin classes for Quantity classes."""
2
+
3
+ __all__: tuple[str, ...] = ()
4
+
5
+ from collections.abc import Callable, Sequence
6
+ from typing import TYPE_CHECKING, Any, cast
7
+
8
+ import equinox as eqx
9
+ import numpy as np
10
+ import numpy.typing as npt
11
+ from astropy.units import CompositeUnit
12
+ from jax.typing import ArrayLike
13
+ from jaxtyping import Array
14
+
15
+ from dataclassish import replace
16
+
17
+ import unxt_api as uapi
18
+ from unxt.units import AbstractUnit, unit as parse_unit
19
+
20
+ if TYPE_CHECKING:
21
+ import unxt.quantity
22
+
23
+
24
+ class AstropyQuantityCompatMixin:
25
+ """Mixin for compatibility with `astropy.units.Quantity`."""
26
+
27
+ value: eqx.AbstractVar[ArrayLike]
28
+ unit: eqx.AbstractVar[AbstractUnit]
29
+ uconvert: Callable[[Any], "unxt.quantity.AbstractQuantity"]
30
+ ustrip: Callable[[Any], ArrayLike]
31
+
32
+ def to(self, u: Any, /) -> "unxt.quantity.AbstractQuantity":
33
+ """Convert the quantity to the given units.
34
+
35
+ See `unxt.quantity.AbstractQuantity.uconvert`.
36
+
37
+ Examples
38
+ --------
39
+ >>> from unxt import Quantity
40
+
41
+ >>> q = Quantity(1, "m")
42
+ >>> q.to("cm")
43
+ Quantity(Array(100., dtype=float32, ...), unit='cm')
44
+
45
+ """
46
+ return uapi.uconvert(u, self) # redirect to the standard method
47
+
48
+ def to_value(self, u: Any, /) -> ArrayLike:
49
+ """Return the value in the given units.
50
+
51
+ See `unxt.AbstractQuantity.ustrip`.
52
+
53
+ Examples
54
+ --------
55
+ >>> from unxt import Quantity
56
+
57
+ >>> q = Quantity(1, "m")
58
+ >>> q.to_value("cm")
59
+ Array(100., dtype=float32, weak_type=True)
60
+
61
+ """
62
+ return uapi.ustrip(u, self) # redirect to the standard method
63
+
64
+ def decompose(
65
+ self, bases: Sequence[AbstractUnit | str], /
66
+ ) -> "unxt.quantity.AbstractQuantity":
67
+ """Decompose the quantity into the given bases.
68
+
69
+ Examples
70
+ --------
71
+ >>> from unxt import Quantity
72
+
73
+ >>> q = Quantity(1, "m")
74
+ >>> q.decompose(["cm", "s"])
75
+ Quantity(Array(100., dtype=float32, ...), unit='cm')
76
+
77
+ """
78
+ bases_ = [parse_unit(b) for b in bases]
79
+ du = self.unit.decompose(bases_) # decomposed units
80
+ base_units = CompositeUnit(scale=1, bases=du.bases, powers=du.powers)
81
+ return replace(self, value=self.value * du.scale, unit=base_units)
82
+
83
+
84
+ #####################################################################
85
+
86
+
87
+ SUPPORTED_IPYTHON_REPR_FORMATS: dict[str, str] = {
88
+ "text/plain": "__repr__",
89
+ "text/html": "_repr_html_",
90
+ "text/latex": "_repr_latex_",
91
+ }
92
+
93
+
94
+ class IPythonReprMixin:
95
+ """Mixin class for IPython representation of a Quantity."""
96
+
97
+ value: Array
98
+ unit: AbstractUnit
99
+
100
+ def _repr_mimebundle_(
101
+ self,
102
+ *,
103
+ include: Sequence[str] | None = None,
104
+ exclude: Sequence[str] | None = None,
105
+ ) -> dict[str, str]:
106
+ r"""Return a MIME bundle representation of the Quantity.
107
+
108
+ :param include: The set of keys to include in the MIME bundle. If not
109
+ provided, all supported formats are included.
110
+ :param exclude: The set of keys to exclude in the MIME bundle. If not
111
+ provided, all supported formats are included. 'include' has
112
+ precedence over 'exclude'.
113
+
114
+ Examples
115
+ --------
116
+ >>> from unxt import Quantity
117
+
118
+ >>> q = Quantity([1.0, 2, 3, 4], "m")
119
+ >>> q._repr_mimebundle_()
120
+ {'text/plain': "Quantity(Array([1., 2., 3., 4.], dtype=float32), unit='m')",
121
+ 'text/html': '<span>[1., 2., 3., 4.]</span> * <span>Unit("m")</span>',
122
+ 'text/latex': '$[1.,~2.,~3.,~4.] \\; \\mathrm{m}$'}
123
+
124
+ >>> q._repr_mimebundle_(include=["text/plain"])
125
+ {'text/plain': "Quantity(Array([1., 2., 3., 4.], dtype=float32), unit='m')"}
126
+
127
+ >>> q._repr_mimebundle_(exclude=["text/html", "text/latex"])
128
+ {'text/plain': "Quantity(Array([1., 2., 3., 4.], dtype=float32), unit='m')"}
129
+
130
+ """
131
+ # Determine the set of keys to include in the MIME bundle
132
+ keys: Sequence[str]
133
+ if include is None and exclude is None:
134
+ keys = tuple(SUPPORTED_IPYTHON_REPR_FORMATS)
135
+ elif include is not None:
136
+ keys = [key for key in include if key in SUPPORTED_IPYTHON_REPR_FORMATS]
137
+ else:
138
+ keys = [
139
+ k
140
+ for k in SUPPORTED_IPYTHON_REPR_FORMATS
141
+ if k not in cast("str", exclude)
142
+ ]
143
+
144
+ # Create the MIME bundle
145
+ return {
146
+ key: getattr(self, SUPPORTED_IPYTHON_REPR_FORMATS[key])() for key in keys
147
+ }
148
+
149
+ def _repr_html_(self) -> str:
150
+ """Return an HTML representation of the Quantity.
151
+
152
+ Examples
153
+ --------
154
+ >>> from unxt import Quantity
155
+
156
+ >>> q = Quantity([1.0, 2, 3, 4], "m")
157
+ >>> q._repr_html_()
158
+ '<span>[1., 2., 3., 4.]</span> * <span>Unit("m")</span>'
159
+
160
+ """
161
+ unit_repr = getattr(self.unit, "_repr_html_", self.unit.__repr__)()
162
+ value_repr = np.array2string(self.value, separator=", ") # type: ignore[call-overload]
163
+
164
+ return f"<span>{value_repr}</span> * <span>{unit_repr}</span>"
165
+
166
+ def _repr_latex_(self) -> str:
167
+ r"""Return a LaTeX representation of the Quantity.
168
+
169
+ Examples
170
+ --------
171
+ >>> from unxt import Quantity
172
+
173
+ >>> q = Quantity([1.0, 2, 3, 4], "m")
174
+ >>> q._repr_latex_()
175
+ '$[1.,~2.,~3.,~4.] \\; \\mathrm{m}$'
176
+
177
+ """
178
+ unit_repr = getattr(self.unit, "_repr_latex_", self.unit.__repr__)()
179
+ value_repr = np.array2string(self.value, separator=",~") # type: ignore[call-overload]
180
+ return f"${value_repr} \\; {unit_repr[1:-1]}$"
181
+
182
+ # TODO: implement:
183
+ # - _repr_markdown_
184
+ # - _repr_json_
185
+
186
+
187
+ #####################################################################
188
+
189
+
190
+ class NumPyCompatMixin:
191
+ """Mixin for compatibility with numpy arrays."""
192
+
193
+ unit: AbstractUnit
194
+
195
+ __array_namespace__: Callable[[], Any]
196
+
197
+ def __array__(self, *args: object, **kw: object) -> npt.NDArray[Any]:
198
+ """Return the array as a numpy array, stripping the units.
199
+
200
+ Examples
201
+ --------
202
+ >>> from unxt import Quantity
203
+ >>> import numpy as np
204
+
205
+ >>> q = Quantity(1.01, "m")
206
+ >>> np.array(q)
207
+ array(1.01, dtype=float32)
208
+
209
+ """
210
+ return np.asarray(uapi.ustrip(self.unit, self), *args, **kw)
211
+
212
+ # TODO: why doesn't `__array_namespace__` supersede this?
213
+ def __array_function__(
214
+ self,
215
+ func: Callable[..., Any],
216
+ types: Any,
217
+ args: tuple[Any, ...],
218
+ kwargs: dict[str, Any],
219
+ ) -> Any:
220
+ """Dispatch to the corresponding jax.numpy function.
221
+
222
+ Examples
223
+ --------
224
+ >>> import numpy as np
225
+ >>> import unxt as u
226
+
227
+ >>> q = u.Quantity([1.0, 2, 3, 4], "m")
228
+ >>> np.sum(q)
229
+ Quantity(Array(10., dtype=float32), unit='m')
230
+
231
+ >>> np.stack([q, q])
232
+ Quantity(Array([[1., 2., 3., 4.],
233
+ [1., 2., 3., 4.]], dtype=float32), unit='m')
234
+
235
+ """
236
+ xp = self.__array_namespace__()
237
+ xfunc = getattr(xp, func.__name__)
238
+ return xfunc(*args, **kwargs)
@@ -0,0 +1,123 @@
1
+ # pylint: disable=import-error, no-member, unsubscriptable-object
2
+ # b/c it doesn't understand dataclass fields
3
+
4
+ __all__ = ("Quantity", "Q")
5
+
6
+ from typing import ClassVar, final
7
+
8
+ import equinox as eqx
9
+ from jaxtyping import Array, Shaped
10
+ from plum import parametric
11
+
12
+ from .base_parametric import AbstractParametricQuantity
13
+ from .value import StaticValue, convert_to_quantity_value
14
+ from unxt.units import AbstractUnit, unit as parse_unit
15
+
16
+
17
+ @final
18
+ @parametric
19
+ class Quantity(AbstractParametricQuantity):
20
+ """Arrays with associated units.
21
+
22
+ This class is parametrized by the dimensions of the units.
23
+
24
+ Attributes
25
+ ----------
26
+ short_name : str
27
+ Short name 'Q' used for compact wadler-lindig printing.
28
+
29
+ Examples
30
+ --------
31
+ >>> import unxt as u
32
+
33
+ From an integer:
34
+
35
+ >>> u.Quantity(1, "m")
36
+ Quantity(Array(1, dtype=int32, ...), unit='m')
37
+
38
+ From a float:
39
+
40
+ >>> u.Q(1.0, "m")
41
+ Quantity(Array(1., dtype=float32, ...), unit='m')
42
+
43
+ From a list:
44
+
45
+ >>> u.Quantity([1, 2, 3], "m")
46
+ Quantity(Array([1, 2, 3], dtype=int32), unit='m')
47
+
48
+ From a tuple:
49
+
50
+ >>> u.Quantity((1, 2, 3), "m")
51
+ Quantity(Array([1, 2, 3], dtype=int32), unit='m')
52
+
53
+ From a `numpy.ndarray`:
54
+
55
+ >>> import numpy as np
56
+ >>> u.Quantity(np.array([1, 2, 3]), "m")
57
+ Quantity(Array([1, 2, 3], dtype=int32), unit='m')
58
+
59
+ From a `jax.Array`:
60
+
61
+ >>> import jax.numpy as jnp
62
+ >>> u.Quantity(jnp.array([1, 2, 3]), "m")
63
+ Quantity(Array([1, 2, 3], dtype=int32), unit='m')
64
+
65
+ The unit can also be given as a units object:
66
+
67
+ >>> u.Quantity(1, u.unit("m"))
68
+ Quantity(Array(1, dtype=int32, ...), unit='m')
69
+
70
+ In the previous examples, the dimension parameter was inferred from the
71
+ values. It can also be given explicitly:
72
+
73
+ >>> u.Quantity["length"](1, "m")
74
+ Quantity(Array(1, dtype=int32, ...), unit='m')
75
+
76
+ This can be used for runtime checking of the input dimension!
77
+
78
+ >>> try:
79
+ ... u.Quantity["length"](1, "s")
80
+ ... except Exception as e:
81
+ ... print(e)
82
+ Physical type mismatch.
83
+
84
+ The dimension can also be given as a dimension object:
85
+
86
+ >>> dims = u.dimension("length")
87
+ >>> dims
88
+ PhysicalType('length')
89
+ >>> u.Quantity[dims](1.0, "m")
90
+ Quantity(Array(1., dtype=float32, ...), unit='m')
91
+
92
+ Or as a unit:
93
+
94
+ >>> u.Quantity[u.unit("m")](1.0, "m")
95
+ Quantity(Array(1., dtype=float32, ...), unit='m')
96
+
97
+ Some tricky cases are when the physical type is unknown:
98
+
99
+ >>> unit = u.unit("m2 / (kg s2)")
100
+ >>> u.dimension_of(unit)
101
+ PhysicalType('unknown')
102
+
103
+ The dimension can be given as a string in all cases, but is necessary when
104
+ the physical type is unknown:
105
+
106
+ >>> print(u.Quantity["m2 kg-1 s-2"](1.0, unit)) # to show the [dim]
107
+ Quantity['m2 kg-1 s-2'](1., unit='m2 / (kg s2)')
108
+
109
+ """
110
+
111
+ short_name: ClassVar[str] = "Q"
112
+ """Short name for compact printing."""
113
+
114
+ value: Shaped[Array, "*shape"] | StaticValue = eqx.field(
115
+ converter=convert_to_quantity_value
116
+ )
117
+ """The value of the `AbstractQuantity`."""
118
+
119
+ unit: AbstractUnit = eqx.field(static=True, converter=parse_unit)
120
+ """The unit associated with this value."""
121
+
122
+
123
+ Q = Quantity # convenience alias