unxt 0.1.dev463__tar.gz

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 (187) hide show
  1. unxt-0.1.dev463/.git_archival.txt +4 -0
  2. unxt-0.1.dev463/.gitattributes +1 -0
  3. unxt-0.1.dev463/.github/CODEOWNERS +6 -0
  4. unxt-0.1.dev463/.github/CONTRIBUTING.md +86 -0
  5. unxt-0.1.dev463/.github/copilot-instructions.md +297 -0
  6. unxt-0.1.dev463/.github/dependabot.yml +11 -0
  7. unxt-0.1.dev463/.github/labeler.yml +40 -0
  8. unxt-0.1.dev463/.github/labels.yml +318 -0
  9. unxt-0.1.dev463/.github/matchers/pylint.json +32 -0
  10. unxt-0.1.dev463/.github/release.yml +125 -0
  11. unxt-0.1.dev463/.github/workflows/cd-unxt-api.yml +108 -0
  12. unxt-0.1.dev463/.github/workflows/cd-unxt-hypothesis.yml +108 -0
  13. unxt-0.1.dev463/.github/workflows/cd-unxt.yml +112 -0
  14. unxt-0.1.dev463/.github/workflows/ci.yml +232 -0
  15. unxt-0.1.dev463/.github/workflows/labeler.yml +68 -0
  16. unxt-0.1.dev463/.github/workflows/sync-labels.yml +22 -0
  17. unxt-0.1.dev463/.gitignore +166 -0
  18. unxt-0.1.dev463/.pre-commit-config.yaml +97 -0
  19. unxt-0.1.dev463/.prettierrc.yaml +5 -0
  20. unxt-0.1.dev463/.python-version +1 -0
  21. unxt-0.1.dev463/.readthedocs.yaml +21 -0
  22. unxt-0.1.dev463/.taplo.toml +27 -0
  23. unxt-0.1.dev463/CODE_OF_CONDUCT.md +79 -0
  24. unxt-0.1.dev463/CONTRIBUTING.md +64 -0
  25. unxt-0.1.dev463/LICENSE +29 -0
  26. unxt-0.1.dev463/PKG-INFO +533 -0
  27. unxt-0.1.dev463/README.md +437 -0
  28. unxt-0.1.dev463/RELEASING.md +357 -0
  29. unxt-0.1.dev463/SECURITY.md +9 -0
  30. unxt-0.1.dev463/conftest.py +56 -0
  31. unxt-0.1.dev463/docs/Makefile +24 -0
  32. unxt-0.1.dev463/docs/_static/custom_toc.css +12 -0
  33. unxt-0.1.dev463/docs/_static/equinox.inv +0 -0
  34. unxt-0.1.dev463/docs/_static/favicon.png +0 -0
  35. unxt-0.1.dev463/docs/_static/generate_equinox_inv.py +125 -0
  36. unxt-0.1.dev463/docs/_static/generate_jaxtyping_inv.py +146 -0
  37. unxt-0.1.dev463/docs/_static/generate_quax_blocks_inv.py +411 -0
  38. unxt-0.1.dev463/docs/_static/jaxtyping.inv +0 -0
  39. unxt-0.1.dev463/docs/_static/quax_blocks.inv +0 -0
  40. unxt-0.1.dev463/docs/_static/toggle_toc.js +16 -0
  41. unxt-0.1.dev463/docs/api/dims.md +9 -0
  42. unxt-0.1.dev463/docs/api/experimental.md +9 -0
  43. unxt-0.1.dev463/docs/api/index.md +23 -0
  44. unxt-0.1.dev463/docs/api/quantity.md +10 -0
  45. unxt-0.1.dev463/docs/api/units.md +9 -0
  46. unxt-0.1.dev463/docs/api/unitsystems.md +9 -0
  47. unxt-0.1.dev463/docs/conf.py +235 -0
  48. unxt-0.1.dev463/docs/contributing.md +64 -0
  49. unxt-0.1.dev463/docs/conventions.md +79 -0
  50. unxt-0.1.dev463/docs/dev.md +15 -0
  51. unxt-0.1.dev463/docs/glossary.md +37 -0
  52. unxt-0.1.dev463/docs/guides/dimensions.md +196 -0
  53. unxt-0.1.dev463/docs/guides/perf.md +191 -0
  54. unxt-0.1.dev463/docs/guides/quantity.md +528 -0
  55. unxt-0.1.dev463/docs/guides/sharp-bits.md +392 -0
  56. unxt-0.1.dev463/docs/guides/type-checking.md +155 -0
  57. unxt-0.1.dev463/docs/guides/units_and_systems.md +213 -0
  58. unxt-0.1.dev463/docs/index.md +625 -0
  59. unxt-0.1.dev463/docs/interop/astropy.md +157 -0
  60. unxt-0.1.dev463/docs/interop/dataclassish.md +201 -0
  61. unxt-0.1.dev463/docs/interop/gala.md +57 -0
  62. unxt-0.1.dev463/docs/interop/matplotlib.md +57 -0
  63. unxt-0.1.dev463/docs/packages/unxt-api/api.md +367 -0
  64. unxt-0.1.dev463/docs/packages/unxt-api/extending.md +567 -0
  65. unxt-0.1.dev463/docs/packages/unxt-api/index.md +221 -0
  66. unxt-0.1.dev463/docs/packages/unxt-hypothesis/api.md +11 -0
  67. unxt-0.1.dev463/docs/packages/unxt-hypothesis/index.md +622 -0
  68. unxt-0.1.dev463/docs/packages/unxt-hypothesis/testing-guide.md +881 -0
  69. unxt-0.1.dev463/noxfile.py +229 -0
  70. unxt-0.1.dev463/packages/unxt-api/README.md +111 -0
  71. unxt-0.1.dev463/packages/unxt-api/pyproject.toml +53 -0
  72. unxt-0.1.dev463/packages/unxt-api/src/unxt_api/__init__.py +30 -0
  73. unxt-0.1.dev463/packages/unxt-api/src/unxt_api/_src/__init__.py +6 -0
  74. unxt-0.1.dev463/packages/unxt-api/src/unxt_api/_src/dimensions.py +38 -0
  75. unxt-0.1.dev463/packages/unxt-api/src/unxt_api/_src/quantity.py +140 -0
  76. unxt-0.1.dev463/packages/unxt-api/src/unxt_api/_src/units.py +20 -0
  77. unxt-0.1.dev463/packages/unxt-api/src/unxt_api/_src/unitsystems.py +15 -0
  78. unxt-0.1.dev463/packages/unxt-api/src/unxt_api/py.typed +0 -0
  79. unxt-0.1.dev463/packages/unxt-api/tests/README.md +158 -0
  80. unxt-0.1.dev463/packages/unxt-api/tests/conftest.py +54 -0
  81. unxt-0.1.dev463/packages/unxt-api/tests/test_advanced.py +378 -0
  82. unxt-0.1.dev463/packages/unxt-api/tests/test_dimensions.py +134 -0
  83. unxt-0.1.dev463/packages/unxt-api/tests/test_package.py +169 -0
  84. unxt-0.1.dev463/packages/unxt-api/tests/test_quantity.py +309 -0
  85. unxt-0.1.dev463/packages/unxt-api/tests/test_units.py +123 -0
  86. unxt-0.1.dev463/packages/unxt-api/tests/test_unitsystems.py +86 -0
  87. unxt-0.1.dev463/packages/unxt-api/tests/test_usage.py +353 -0
  88. unxt-0.1.dev463/packages/unxt-hypothesis/README.md +405 -0
  89. unxt-0.1.dev463/packages/unxt-hypothesis/pyproject.toml +62 -0
  90. unxt-0.1.dev463/packages/unxt-hypothesis/src/unxt_hypothesis/__init__.py +17 -0
  91. unxt-0.1.dev463/packages/unxt-hypothesis/src/unxt_hypothesis/_src/__init__.py +3 -0
  92. unxt-0.1.dev463/packages/unxt-hypothesis/src/unxt_hypothesis/_src/dimensions.py +229 -0
  93. unxt-0.1.dev463/packages/unxt-hypothesis/src/unxt_hypothesis/_src/quantities.py +365 -0
  94. unxt-0.1.dev463/packages/unxt-hypothesis/src/unxt_hypothesis/_src/units.py +231 -0
  95. unxt-0.1.dev463/packages/unxt-hypothesis/src/unxt_hypothesis/_src/unitsystems.py +76 -0
  96. unxt-0.1.dev463/packages/unxt-hypothesis/src/unxt_hypothesis/py.typed +0 -0
  97. unxt-0.1.dev463/packages/unxt-hypothesis/tests/test_angles.py +79 -0
  98. unxt-0.1.dev463/packages/unxt-hypothesis/tests/test_from_type.py +71 -0
  99. unxt-0.1.dev463/packages/unxt-hypothesis/tests/test_quantities.py +568 -0
  100. unxt-0.1.dev463/packages/unxt-hypothesis/tests/test_strategies_dimensions.py +30 -0
  101. unxt-0.1.dev463/packages/unxt-hypothesis/tests/test_strategies_units.py +200 -0
  102. unxt-0.1.dev463/packages/unxt-hypothesis/tests/test_strategies_unitsystems.py +124 -0
  103. unxt-0.1.dev463/paper/paper.bib +117 -0
  104. unxt-0.1.dev463/paper/paper.md +62 -0
  105. unxt-0.1.dev463/paper/paper.pdf +0 -0
  106. unxt-0.1.dev463/pyproject.toml +375 -0
  107. unxt-0.1.dev463/scripts/QUICK_RELEASE.md +169 -0
  108. unxt-0.1.dev463/scripts/get_version.py +228 -0
  109. unxt-0.1.dev463/scripts/validate_tag.py +113 -0
  110. unxt-0.1.dev463/src/unxt/__init__.py +79 -0
  111. unxt-0.1.dev463/src/unxt/_interop/__init__.py +18 -0
  112. unxt-0.1.dev463/src/unxt/_interop/optional_deps.py +13 -0
  113. unxt-0.1.dev463/src/unxt/_interop/unxt_interop_astropy/__init__.py +5 -0
  114. unxt-0.1.dev463/src/unxt/_interop/unxt_interop_astropy/custom_types.py +12 -0
  115. unxt-0.1.dev463/src/unxt/_interop/unxt_interop_astropy/dimensions.py +53 -0
  116. unxt-0.1.dev463/src/unxt/_interop/unxt_interop_astropy/quantity.py +308 -0
  117. unxt-0.1.dev463/src/unxt/_interop/unxt_interop_astropy/units.py +111 -0
  118. unxt-0.1.dev463/src/unxt/_interop/unxt_interop_gala/__init__.py +5 -0
  119. unxt-0.1.dev463/src/unxt/_interop/unxt_interop_gala/unitsystems.py +108 -0
  120. unxt-0.1.dev463/src/unxt/_interop/unxt_interop_mpl/__init__.py +81 -0
  121. unxt-0.1.dev463/src/unxt/_src/__init__.py +6 -0
  122. unxt-0.1.dev463/src/unxt/_src/dimensions.py +400 -0
  123. unxt-0.1.dev463/src/unxt/_src/experimental.py +234 -0
  124. unxt-0.1.dev463/src/unxt/_src/quantity/__init__.py +24 -0
  125. unxt-0.1.dev463/src/unxt/_src/quantity/angle.py +58 -0
  126. unxt-0.1.dev463/src/unxt/_src/quantity/base.py +1160 -0
  127. unxt-0.1.dev463/src/unxt/_src/quantity/base_angle.py +89 -0
  128. unxt-0.1.dev463/src/unxt/_src/quantity/base_parametric.py +290 -0
  129. unxt-0.1.dev463/src/unxt/_src/quantity/flag.py +137 -0
  130. unxt-0.1.dev463/src/unxt/_src/quantity/mixins.py +238 -0
  131. unxt-0.1.dev463/src/unxt/_src/quantity/quantity.py +123 -0
  132. unxt-0.1.dev463/src/unxt/_src/quantity/register_api.py +383 -0
  133. unxt-0.1.dev463/src/unxt/_src/quantity/register_conversions.py +95 -0
  134. unxt-0.1.dev463/src/unxt/_src/quantity/register_dispatches.py +246 -0
  135. unxt-0.1.dev463/src/unxt/_src/quantity/register_primitives.py +5075 -0
  136. unxt-0.1.dev463/src/unxt/_src/quantity/static_quantity.py +90 -0
  137. unxt-0.1.dev463/src/unxt/_src/quantity/unchecked.py +45 -0
  138. unxt-0.1.dev463/src/unxt/_src/quantity/value.py +289 -0
  139. unxt-0.1.dev463/src/unxt/_src/units.py +103 -0
  140. unxt-0.1.dev463/src/unxt/_src/unitsystems/__init__.py +40 -0
  141. unxt-0.1.dev463/src/unxt/_src/unitsystems/base.py +258 -0
  142. unxt-0.1.dev463/src/unxt/_src/unitsystems/builtin.py +190 -0
  143. unxt-0.1.dev463/src/unxt/_src/unitsystems/builtin_dimensions.py +21 -0
  144. unxt-0.1.dev463/src/unxt/_src/unitsystems/compare.py +13 -0
  145. unxt-0.1.dev463/src/unxt/_src/unitsystems/core.py +307 -0
  146. unxt-0.1.dev463/src/unxt/_src/unitsystems/flags.py +80 -0
  147. unxt-0.1.dev463/src/unxt/_src/unitsystems/realizations.py +73 -0
  148. unxt-0.1.dev463/src/unxt/_src/unitsystems/utils.py +95 -0
  149. unxt-0.1.dev463/src/unxt/_src/utils.py +112 -0
  150. unxt-0.1.dev463/src/unxt/_version.py +34 -0
  151. unxt-0.1.dev463/src/unxt/_version.pyi +2 -0
  152. unxt-0.1.dev463/src/unxt/dims.py +19 -0
  153. unxt-0.1.dev463/src/unxt/py.typed +0 -0
  154. unxt-0.1.dev463/src/unxt/quantity.py +172 -0
  155. unxt-0.1.dev463/src/unxt/setup_package.py +60 -0
  156. unxt-0.1.dev463/src/unxt/units.py +19 -0
  157. unxt-0.1.dev463/src/unxt/unitsystems.py +67 -0
  158. unxt-0.1.dev463/tests/__init__.py +1 -0
  159. unxt-0.1.dev463/tests/benchmark/__init__.py +1 -0
  160. unxt-0.1.dev463/tests/benchmark/test_dims.py +82 -0
  161. unxt-0.1.dev463/tests/benchmark/test_quaxed.py +135 -0
  162. unxt-0.1.dev463/tests/benchmark/test_uconvert_value.py +125 -0
  163. unxt-0.1.dev463/tests/benchmark/test_units.py +77 -0
  164. unxt-0.1.dev463/tests/benchmark/test_unitsystems.py +126 -0
  165. unxt-0.1.dev463/tests/integration/__init__.py +1 -0
  166. unxt-0.1.dev463/tests/integration/equinox/__init__.py +1 -0
  167. unxt-0.1.dev463/tests/integration/equinox/test_module.py +16 -0
  168. unxt-0.1.dev463/tests/integration/matplotlib/__init__.py +1 -0
  169. unxt-0.1.dev463/tests/integration/matplotlib/baseline/test_labels_axes.png +0 -0
  170. unxt-0.1.dev463/tests/integration/matplotlib/test_interop_mpl.py +18 -0
  171. unxt-0.1.dev463/tests/integration/quax/__init__.py +1 -0
  172. unxt-0.1.dev463/tests/integration/quax/test_lora.py +21 -0
  173. unxt-0.1.dev463/tests/integration/quaxed/__init__.py +1 -0
  174. unxt-0.1.dev463/tests/integration/quaxed/test_lax.py +413 -0
  175. unxt-0.1.dev463/tests/integration/quaxed/test_numpy.py +2008 -0
  176. unxt-0.1.dev463/tests/integration/test_plum.py +29 -0
  177. unxt-0.1.dev463/tests/integration/test_uconvert_value_performance.py +107 -0
  178. unxt-0.1.dev463/tests/unit/__init__.py +1 -0
  179. unxt-0.1.dev463/tests/unit/test_astropy_interop_quantity.py +233 -0
  180. unxt-0.1.dev463/tests/unit/test_dims.py +190 -0
  181. unxt-0.1.dev463/tests/unit/test_get_version_script.py +31 -0
  182. unxt-0.1.dev463/tests/unit/test_package.py +10 -0
  183. unxt-0.1.dev463/tests/unit/test_quantity.py +1372 -0
  184. unxt-0.1.dev463/tests/unit/test_quantity_printing.py +93 -0
  185. unxt-0.1.dev463/tests/unit/test_static_quantity.py +586 -0
  186. unxt-0.1.dev463/tests/unit/test_unitsystems.py +241 -0
  187. unxt-0.1.dev463/uv.lock +4187 -0
@@ -0,0 +1,4 @@
1
+ node: $Format:%H$
2
+ node-date: $Format:%cI$
3
+ describe-name: $Format:%(describe:tags=true,match=*[0-9]*)$
4
+ ref-names: $Format:%D$
@@ -0,0 +1 @@
1
+ .git_archival.txt export-subst
@@ -0,0 +1,6 @@
1
+ # CODEOWNERS - Defines who must review changes to specific files
2
+ # See: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
3
+
4
+ # GitHub workflows and configuration files require review from repository owners
5
+ # This helps protect against unauthorized changes to CI/CD pipelines and automation
6
+ /.github/ @GalacticDynamics/unxt-maintainers
@@ -0,0 +1,86 @@
1
+ See the [Scientific Python Developer Guide][spc-dev-intro] for a detailed description of best practices for developing scientific packages.
2
+
3
+ [spc-dev-intro]: https://learn.scientific-python.org/development/
4
+
5
+ # Quick development
6
+
7
+ The fastest way to start with development is to use nox. If you don't have nox, you can use `pipx run nox` to run it without installing, or `pipx install nox`. If you don't have pipx (pip for applications), then you can install with `pip install pipx` (the only case were installing an application with regular pip is reasonable). If you use macOS, then pipx and nox are both in brew, use `brew install pipx nox`.
8
+
9
+ To use, run `nox`. This will lint and test using every installed version of Python on your system, skipping ones that are not installed. You can also run specific jobs:
10
+
11
+ ```console
12
+ $ nox -s lint # Lint only
13
+ $ nox -s tests # Python tests
14
+ $ nox -s docs -- --serve # Build and serve the docs
15
+ $ nox -s build # Make an SDist and wheel
16
+ ```
17
+
18
+ Nox handles everything for you, including setting up an temporary virtual environment for each run.
19
+
20
+ # Setting up a development environment manually
21
+
22
+ You can set up a development environment by running:
23
+
24
+ ```bash
25
+ python3 -m venv .venv
26
+ source ./.venv/bin/activate
27
+ pip install -v -e .[dev]
28
+ ```
29
+
30
+ If you have the [Python Launcher for Unix](https://github.com/brettcannon/python-launcher), you can instead do:
31
+
32
+ ```bash
33
+ py -m venv .venv
34
+ py -m install -v -e .[dev]
35
+ ```
36
+
37
+ # Post setup
38
+
39
+ You should prepare pre-commit, which will help you by checking that commits pass required checks:
40
+
41
+ ```bash
42
+ pip install pre-commit # or brew install pre-commit on macOS
43
+ pre-commit install # Will install a pre-commit hook into the git repo
44
+ ```
45
+
46
+ You can also/alternatively run `pre-commit run` (changes only) or `pre-commit run --all-files` to check even without installing the hook.
47
+
48
+ # Testing
49
+
50
+ Use pytest to run the unit checks:
51
+
52
+ ```bash
53
+ pytest
54
+ ```
55
+
56
+ # Coverage
57
+
58
+ Use pytest-cov to generate coverage reports:
59
+
60
+ ```bash
61
+ pytest --cov=unxt
62
+ ```
63
+
64
+ # Building docs
65
+
66
+ You can build the docs using:
67
+
68
+ ```bash
69
+ nox -s docs
70
+ ```
71
+
72
+ You can see a preview with:
73
+
74
+ ```bash
75
+ nox -s docs -- --serve
76
+ ```
77
+
78
+ # Pre-commit
79
+
80
+ This project uses pre-commit for all style checking. While you can run it with nox, this is such an important tool that it deserves to be installed on its own. Install pre-commit and run:
81
+
82
+ ```bash
83
+ pre-commit run -a
84
+ ```
85
+
86
+ to check all files.
@@ -0,0 +1,297 @@
1
+ # Project Overview
2
+
3
+ This is a UV workspace repository containing multiple packages:
4
+
5
+ - **unxt**: Main library for unitful quantities in JAX with support for JIT compilation, auto-differentiation, vectorization, and GPU/TPU acceleration
6
+ - **unxt-api**: Abstract dispatch API that defines the multiple-dispatch interfaces implemented by `unxt` and other packages. Minimal dependencies (only `plum-dispatch`).
7
+ - **unxt-hypothesis**: Hypothesis strategies for property-based testing with `unxt`
8
+
9
+ ## Main Package: unxt
10
+
11
+ - **Language**: Python 3.11+
12
+ - **Main API**: `Quantity`, `Angle`, unit conversion and manipulation
13
+ - `Quantity`: Main parametric class with dimension checking
14
+ - `Angle`: Specialized type with wrapping support
15
+ - `unit()`, `dimension()`, unit systems
16
+ - **Design goals**: JAX-compatible quantities, dimension checking, seamless integration with existing JAX code via Quax
17
+ - **JAX integration**: Objects are PyTrees via Equinox. Use `quaxed` for pre-quaxified JAX functions. Performant with JIT, vmap, grad.
18
+
19
+ ## Architecture & Core Components
20
+
21
+ - **Quantity types** (hierarchical):
22
+ - `AbstractQuantity`: Base class using Quax's `ArrayValue` for JAX integration
23
+ - `AbstractParametricQuantity`: Enables dimension parametrization (`Quantity["length"]`)
24
+ - `Quantity`: Main parametric class with runtime dimension checking
25
+ - `BareQuantity`: Lightweight variants without dimension checks
26
+ - `Angle`: Specialized type with wrapping support
27
+ - **Units system**: Wraps Astropy units, provides `unit()`, `unit_of()` constructors
28
+ - **Dimensions**: Physical type checking via `dimension()`, `dimension_of()`
29
+ - **Unit systems**: `AbstractUnitSystem` for consistent unit sets
30
+ - **Interop modules**: Optional integration with Astropy, Gala, Matplotlib (in `_src/_interop/`)
31
+
32
+ ## Folder Structure
33
+
34
+ ### Root Level (UV Workspace)
35
+
36
+ - `/src/unxt/`: Main package public API with re-exports
37
+ - `/packages/`: Workspace packages
38
+ - `unxt-api/`: Abstract dispatch API package
39
+ - `unxt-hypothesis/`: Hypothesis strategies package
40
+ - `/tests/`: Main package tests, organized into `unit/`, `integration/`, `benchmark/`
41
+ - `README.md`: Main package documentation, tested via Sybil (all Python code blocks are doctests)
42
+ - `conftest.py`: Pytest config, Sybil setup, optional dependency handling
43
+ - `noxfile.py`: Task automation with dependency groups
44
+ - `pyproject.toml`: Root workspace configuration with `[tool.uv.workspace]`
45
+
46
+ ### Main Package Structure (`/src/unxt/`)
47
+
48
+ - `_src/`: Private implementation code
49
+ - `quantity/`: Quantity classes and operations
50
+ - `units/`: Unit handling and wrapping
51
+ - `dimensions/`: Physical dimension types
52
+ - `unitsystems/`: Unit system definitions
53
+ - `_interop/`: Optional dependency integrations
54
+
55
+ ## Coding Style
56
+
57
+ - Always use type hints (standard typing, `jaxtyping.Array`, `ArrayLike`, shape annotations)
58
+ - Extensive use of Plum multiple dispatch - check `.methods` on any function to see all dispatches
59
+ - `@parametric` decorator enables dimension parametrization: `Quantity["length"]`
60
+ - Runtime type checking controlled by `UNXT_ENABLE_RUNTIME_TYPECHECKING` env var (defaults to `False`, set to `"beartype.beartype"` in tests)
61
+ - Immutability is a core constraint: methods return new objects, never mutate
62
+ - Keep dependencies minimal; the core dependencies are listed in `pyproject.toml`
63
+ - Docstrings should be concise and include testable usage examples
64
+ - `__all__` should always be a tuple unless it needs to be modified (e.g., with `+=`), in which case use a list - prefer immutable by default
65
+
66
+ ### Multiple Dispatch with Plum
67
+
68
+ This project heavily relies on `plum-dispatch` for multiple dispatch, which allows different implementations of the same function based on argument types. Understanding how plum works is critical for working with this codebase.
69
+
70
+ #### Multiple Dispatch Mechanism
71
+
72
+ - **Single-dispatch vs Multiple-dispatch**: Unlike single dispatch (e.g., `functools.singledispatch`), plum selects implementations based on ALL argument types, not just the first one
73
+ - **Type-based routing**: Plum examines the runtime types of all arguments and selects the most specific matching implementation
74
+ - **Dispatch decorator**: Use `@dispatch` to register multiple implementations of the same function name
75
+
76
+ Example:
77
+
78
+ ```python
79
+ from plum import dispatch
80
+
81
+
82
+ @dispatch
83
+ def process(x: int) -> str:
84
+ return f"integer: {x}"
85
+
86
+
87
+ @dispatch
88
+ def process(x: float) -> str:
89
+ return f"float: {x}"
90
+
91
+
92
+ @dispatch
93
+ def process(x: int, y: int) -> str:
94
+ return f"two integers: {x}, {y}"
95
+ ```
96
+
97
+ #### Finding All Dispatches
98
+
99
+ **CRITICAL**: When working with dispatched functions, you MUST check all registered implementations. A function may have dozens of overloads.
100
+
101
+ **Two methods to find all dispatches:**
102
+
103
+ 1. **Use `.methods` attribute** (preferred in Python REPL/notebooks):
104
+
105
+ ```python
106
+ from unxt import ustrip
107
+
108
+ print(ustrip.methods) # Shows all registered dispatch signatures
109
+ ```
110
+
111
+ 2. **Search the codebase** (preferred when coding):
112
+ - Search for `@dispatch` followed by the function name
113
+ - Look for all `def function_name(...)` definitions with `@dispatch`
114
+ - Example: searching for `@dispatch\ndef ustrip` finds all ustrip overloads
115
+
116
+ **Why this matters:**
117
+
118
+ - You might find a more specific dispatch that handles your exact case
119
+ - Prevents accidentally adding duplicate dispatches
120
+ - Reveals the complete API surface and supported type combinations
121
+ - Essential for understanding how different types interact
122
+
123
+ #### Parametric Classes
124
+
125
+ Plum's `@parametric` decorator enables type parametrization, creating distinct types for different parameters:
126
+
127
+ ```python
128
+ from plum import parametric
129
+
130
+
131
+ @parametric
132
+ class Container(type_parameter):
133
+ def __init__(self, value):
134
+ self.value = value
135
+
136
+
137
+ # Creates distinct types:
138
+ IntContainer = Container[int]
139
+ FloatContainer = Container[float]
140
+ ```
141
+
142
+ **In this codebase:**
143
+
144
+ - `Quantity` is parametric by dimension: `Quantity["length"]`, `Quantity["mass"]`
145
+ - Each parametrization creates a distinct type for dispatch
146
+ - Enables dimension-aware multiple dispatch:
147
+
148
+ ```python
149
+ @dispatch
150
+ def divide(x: Quantity["length"], y: Quantity["time"]) -> Quantity["speed"]:
151
+ ... # Returns speed dimension
152
+ ```
153
+
154
+ **Key properties:**
155
+
156
+ - Parametric types are cached (same parameters = same type object)
157
+ - Type parameters can be strings, tuples, or other hashable objects
158
+ - Use `get_type_parameter(obj)` to retrieve the parameter from an instance
159
+ - Parametric classes enable dimension checking at dispatch time
160
+
161
+ #### Type Promotion with `plum.promote`
162
+
163
+ `plum.promote` implements automatic type promotion for mixed-type operations:
164
+
165
+ ```python
166
+ from plum import dispatch, promote
167
+
168
+
169
+ @dispatch
170
+ def add(x: int, y: int) -> int:
171
+ return x + y
172
+
173
+
174
+ @dispatch
175
+ def add(x: float, y: float) -> float:
176
+ return x + y
177
+
178
+
179
+ # Without promotion:
180
+ add(1, 2.5) # Error: no dispatch for (int, float)
181
+
182
+
183
+ # With promotion (defined separately):
184
+ @dispatch
185
+ def add(x: promote(int, float), y: float) -> float:
186
+ return add(float(x), y)
187
+ ```
188
+
189
+ **In this codebase:**
190
+
191
+ - `plum.promote` is used to convert between quantity types
192
+ - Common pattern: promote bare numbers to quantities
193
+ - Enables natural operations like `quantity + 5.0`
194
+
195
+ **Usage pattern:**
196
+
197
+ 1. Define core implementations for specific types
198
+ 2. Add promotion dispatches to handle mixed types
199
+ 3. Promotion dispatches typically convert arguments and redispatch
200
+
201
+ **Important notes:**
202
+
203
+ - Promotion order matters: `promote(int, float)` != `promote(float, int)`
204
+ - Keep promotion logic explicit and minimal
205
+ - Prefer concrete dispatches over heavy promotion use
206
+ - Document promotion behavior when it's non-obvious
207
+
208
+ ### JAX Integration via Quax
209
+
210
+ - Quantities are `ArrayValue` subclasses (Quax protocol)
211
+ - PyTree registration handled automatically via Equinox
212
+ - Use `quaxed` library (pre-quaxified JAX) for convenience, or manually apply `quax.quaxify` decorator
213
+ - Mixins from `quax-blocks` provide operator overloading (`NumpyBinaryOpsMixin`, etc.)
214
+
215
+ ### Immutability
216
+
217
+ - All quantity operations return new instances
218
+ - Use `dataclassish.replace()` for attribute updates
219
+ - Follow Equinox patterns for JAX compatibility
220
+
221
+ ### Import Hook
222
+
223
+ - `setup_package.py` installs jaxtyping import hook for runtime checking
224
+ - Not required for normal usage but enables beartype integration during tests
225
+
226
+ ## Tooling
227
+
228
+ - This repo uses `uv` for dependency and environment management
229
+ - This repo uses `nox` for all development tasks
230
+ - Before committing, run full checks:
231
+ ```bash
232
+ uv run nox -s all
233
+ ```
234
+ - Common sessions:
235
+ - `nox -s lint`: pre-commit + pylint
236
+ - `nox -s test`: pytest suite
237
+ - `nox -s docs`: build documentation (add `--serve` to preview)
238
+ - `nox -s pytest_benchmark`: run CodSpeed benchmarks
239
+
240
+ **IMPORTANT**: Never write temporary files outside the repository (e.g., to `/tmp/` or other system directories). Always use paths within the repository for any file operations, including temporary or scratch files.
241
+
242
+ ## Testing
243
+
244
+ - Use `pytest` for all test suites with Sybil for doctests in code and markdown
245
+ - Add unit tests for every new function or class
246
+ - Test organization: `unit/`, `integration/`, `benchmark/`
247
+ - **All tests must actually test something**: Every test function must include `assert` statements or return values that pytest can validate. Empty test bodies or tests that only call functions without verification are not valid.
248
+ - Optional dependencies handled via `optional_dependencies.OptionalDependencyEnum`
249
+ - Tests requiring optional deps auto-skip if not installed
250
+ - `conftest.py` manages `collect_ignore_glob` for missing deps
251
+ - For JAX-related behavior:
252
+ - Confirm PyTree registration works correctly (flatten/unflatten)
253
+ - Verify compatibility with transformations like `jit`, `vmap`, and `grad`
254
+ - Test numerical accuracy where applicable
255
+ - Tests should run on CPU by default; no accelerators required
256
+ - Hypothesis for property-based testing of quantity laws
257
+
258
+ ## Optional Dependencies
259
+
260
+ Three optional interop groups:
261
+
262
+ - `backend-astropy`: Enhanced Astropy integration
263
+ - `interop-gala`: Galactic dynamics library support
264
+ - `interop-mpl`: Matplotlib quantity plotting
265
+
266
+ Install with: `uv add unxt --extra all` or specific groups
267
+
268
+ ## Workspace Packages
269
+
270
+ This repository uses a UV workspace structure with multiple packages (e.g., `unxt`, `unxt-api`, `unxt-hypothesis`). When creating new workspace packages, use this versioning setup pattern:
271
+
272
+ ```toml
273
+ [build-system]
274
+ build-backend = "hatchling.build"
275
+ requires = ["hatch-vcs", "hatchling"]
276
+
277
+ [tool.hatch.version]
278
+ raw-options = { root = "../..", search_parent_directories = true, git_describe_command = "git describe --dirty --tags --long --match '<package-name>-v*'", local_scheme = "no-local-version" }
279
+ source = "vcs"
280
+
281
+ [tool.hatch.build.hooks.vcs]
282
+ version-file = "src/<package_name>/_version.py"
283
+ version-file-template = """\
284
+ version: str = {version!r}
285
+ version_tuple: tuple[int, int, int] | tuple[int, int, int, str, str]
286
+ version_tuple = {version_tuple!r}
287
+ """
288
+
289
+ [tool.uv.sources]
290
+ unxt = { workspace = true }
291
+ ```
292
+
293
+ Replace `<package-name>` with the actual package name (e.g., `unxt-hypothesis-v*`) and `<package_name>` with the Python module name (e.g., `unxt_hypothesis`). This enables automatic versioning from git tags.
294
+
295
+ ## Final Notes
296
+
297
+ Preserve JAX compatibility and immutability above all. When extending quantity operations, ensure dimension checking is correct and test with JAX transformations. Follow Equinox/Quax patterns for custom array types. Documentation examples must be executable (they're tested).
@@ -0,0 +1,11 @@
1
+ version: 2
2
+ updates:
3
+ # Maintain dependencies for GitHub Actions
4
+ - package-ecosystem: "github-actions"
5
+ directory: "/"
6
+ schedule:
7
+ interval: "quarterly"
8
+ groups:
9
+ actions:
10
+ patterns:
11
+ - "*"
@@ -0,0 +1,40 @@
1
+ # .github/labeler.yml
2
+ # Automatically apply labels based on file paths
3
+
4
+ "🙈 Add / update .gitignore":
5
+ - changed-files:
6
+ - any-glob-to-any-file: "**/.gitignore"
7
+
8
+ "👷 Add / update CI build system":
9
+ - changed-files:
10
+ - any-glob-to-any-file: ".github/workflows/**"
11
+
12
+ "🔧 Add / update configuration":
13
+ - changed-files:
14
+ - any-glob-to-any-file:
15
+ - "pyproject.toml"
16
+ - "noxfile.py"
17
+ - ".github/labels.yml"
18
+ - ".github/labeler.yml"
19
+ - ".github/dependabot.yml"
20
+ - ".github/release.yml"
21
+ - "conftest.py"
22
+
23
+ "📝 Add / update documentation":
24
+ - changed-files:
25
+ - any-glob-to-any-file:
26
+ - "README.md"
27
+ - "CONTRIBUTING.md"
28
+ - "docs/**"
29
+
30
+ "📄 Add / update license":
31
+ - changed-files:
32
+ - any-glob-to-any-file: "LICENSE"
33
+
34
+ "✅ Add / update / pass tests":
35
+ - changed-files:
36
+ - any-glob-to-any-file: "tests/**"
37
+
38
+ "⏱️ Run benchmarks":
39
+ - changed-files:
40
+ - any-glob-to-any-file: "tests/benchmark/**"