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.
- unxt-0.1.dev463/.git_archival.txt +4 -0
- unxt-0.1.dev463/.gitattributes +1 -0
- unxt-0.1.dev463/.github/CODEOWNERS +6 -0
- unxt-0.1.dev463/.github/CONTRIBUTING.md +86 -0
- unxt-0.1.dev463/.github/copilot-instructions.md +297 -0
- unxt-0.1.dev463/.github/dependabot.yml +11 -0
- unxt-0.1.dev463/.github/labeler.yml +40 -0
- unxt-0.1.dev463/.github/labels.yml +318 -0
- unxt-0.1.dev463/.github/matchers/pylint.json +32 -0
- unxt-0.1.dev463/.github/release.yml +125 -0
- unxt-0.1.dev463/.github/workflows/cd-unxt-api.yml +108 -0
- unxt-0.1.dev463/.github/workflows/cd-unxt-hypothesis.yml +108 -0
- unxt-0.1.dev463/.github/workflows/cd-unxt.yml +112 -0
- unxt-0.1.dev463/.github/workflows/ci.yml +232 -0
- unxt-0.1.dev463/.github/workflows/labeler.yml +68 -0
- unxt-0.1.dev463/.github/workflows/sync-labels.yml +22 -0
- unxt-0.1.dev463/.gitignore +166 -0
- unxt-0.1.dev463/.pre-commit-config.yaml +97 -0
- unxt-0.1.dev463/.prettierrc.yaml +5 -0
- unxt-0.1.dev463/.python-version +1 -0
- unxt-0.1.dev463/.readthedocs.yaml +21 -0
- unxt-0.1.dev463/.taplo.toml +27 -0
- unxt-0.1.dev463/CODE_OF_CONDUCT.md +79 -0
- unxt-0.1.dev463/CONTRIBUTING.md +64 -0
- unxt-0.1.dev463/LICENSE +29 -0
- unxt-0.1.dev463/PKG-INFO +533 -0
- unxt-0.1.dev463/README.md +437 -0
- unxt-0.1.dev463/RELEASING.md +357 -0
- unxt-0.1.dev463/SECURITY.md +9 -0
- unxt-0.1.dev463/conftest.py +56 -0
- unxt-0.1.dev463/docs/Makefile +24 -0
- unxt-0.1.dev463/docs/_static/custom_toc.css +12 -0
- unxt-0.1.dev463/docs/_static/equinox.inv +0 -0
- unxt-0.1.dev463/docs/_static/favicon.png +0 -0
- unxt-0.1.dev463/docs/_static/generate_equinox_inv.py +125 -0
- unxt-0.1.dev463/docs/_static/generate_jaxtyping_inv.py +146 -0
- unxt-0.1.dev463/docs/_static/generate_quax_blocks_inv.py +411 -0
- unxt-0.1.dev463/docs/_static/jaxtyping.inv +0 -0
- unxt-0.1.dev463/docs/_static/quax_blocks.inv +0 -0
- unxt-0.1.dev463/docs/_static/toggle_toc.js +16 -0
- unxt-0.1.dev463/docs/api/dims.md +9 -0
- unxt-0.1.dev463/docs/api/experimental.md +9 -0
- unxt-0.1.dev463/docs/api/index.md +23 -0
- unxt-0.1.dev463/docs/api/quantity.md +10 -0
- unxt-0.1.dev463/docs/api/units.md +9 -0
- unxt-0.1.dev463/docs/api/unitsystems.md +9 -0
- unxt-0.1.dev463/docs/conf.py +235 -0
- unxt-0.1.dev463/docs/contributing.md +64 -0
- unxt-0.1.dev463/docs/conventions.md +79 -0
- unxt-0.1.dev463/docs/dev.md +15 -0
- unxt-0.1.dev463/docs/glossary.md +37 -0
- unxt-0.1.dev463/docs/guides/dimensions.md +196 -0
- unxt-0.1.dev463/docs/guides/perf.md +191 -0
- unxt-0.1.dev463/docs/guides/quantity.md +528 -0
- unxt-0.1.dev463/docs/guides/sharp-bits.md +392 -0
- unxt-0.1.dev463/docs/guides/type-checking.md +155 -0
- unxt-0.1.dev463/docs/guides/units_and_systems.md +213 -0
- unxt-0.1.dev463/docs/index.md +625 -0
- unxt-0.1.dev463/docs/interop/astropy.md +157 -0
- unxt-0.1.dev463/docs/interop/dataclassish.md +201 -0
- unxt-0.1.dev463/docs/interop/gala.md +57 -0
- unxt-0.1.dev463/docs/interop/matplotlib.md +57 -0
- unxt-0.1.dev463/docs/packages/unxt-api/api.md +367 -0
- unxt-0.1.dev463/docs/packages/unxt-api/extending.md +567 -0
- unxt-0.1.dev463/docs/packages/unxt-api/index.md +221 -0
- unxt-0.1.dev463/docs/packages/unxt-hypothesis/api.md +11 -0
- unxt-0.1.dev463/docs/packages/unxt-hypothesis/index.md +622 -0
- unxt-0.1.dev463/docs/packages/unxt-hypothesis/testing-guide.md +881 -0
- unxt-0.1.dev463/noxfile.py +229 -0
- unxt-0.1.dev463/packages/unxt-api/README.md +111 -0
- unxt-0.1.dev463/packages/unxt-api/pyproject.toml +53 -0
- unxt-0.1.dev463/packages/unxt-api/src/unxt_api/__init__.py +30 -0
- unxt-0.1.dev463/packages/unxt-api/src/unxt_api/_src/__init__.py +6 -0
- unxt-0.1.dev463/packages/unxt-api/src/unxt_api/_src/dimensions.py +38 -0
- unxt-0.1.dev463/packages/unxt-api/src/unxt_api/_src/quantity.py +140 -0
- unxt-0.1.dev463/packages/unxt-api/src/unxt_api/_src/units.py +20 -0
- unxt-0.1.dev463/packages/unxt-api/src/unxt_api/_src/unitsystems.py +15 -0
- unxt-0.1.dev463/packages/unxt-api/src/unxt_api/py.typed +0 -0
- unxt-0.1.dev463/packages/unxt-api/tests/README.md +158 -0
- unxt-0.1.dev463/packages/unxt-api/tests/conftest.py +54 -0
- unxt-0.1.dev463/packages/unxt-api/tests/test_advanced.py +378 -0
- unxt-0.1.dev463/packages/unxt-api/tests/test_dimensions.py +134 -0
- unxt-0.1.dev463/packages/unxt-api/tests/test_package.py +169 -0
- unxt-0.1.dev463/packages/unxt-api/tests/test_quantity.py +309 -0
- unxt-0.1.dev463/packages/unxt-api/tests/test_units.py +123 -0
- unxt-0.1.dev463/packages/unxt-api/tests/test_unitsystems.py +86 -0
- unxt-0.1.dev463/packages/unxt-api/tests/test_usage.py +353 -0
- unxt-0.1.dev463/packages/unxt-hypothesis/README.md +405 -0
- unxt-0.1.dev463/packages/unxt-hypothesis/pyproject.toml +62 -0
- unxt-0.1.dev463/packages/unxt-hypothesis/src/unxt_hypothesis/__init__.py +17 -0
- unxt-0.1.dev463/packages/unxt-hypothesis/src/unxt_hypothesis/_src/__init__.py +3 -0
- unxt-0.1.dev463/packages/unxt-hypothesis/src/unxt_hypothesis/_src/dimensions.py +229 -0
- unxt-0.1.dev463/packages/unxt-hypothesis/src/unxt_hypothesis/_src/quantities.py +365 -0
- unxt-0.1.dev463/packages/unxt-hypothesis/src/unxt_hypothesis/_src/units.py +231 -0
- unxt-0.1.dev463/packages/unxt-hypothesis/src/unxt_hypothesis/_src/unitsystems.py +76 -0
- unxt-0.1.dev463/packages/unxt-hypothesis/src/unxt_hypothesis/py.typed +0 -0
- unxt-0.1.dev463/packages/unxt-hypothesis/tests/test_angles.py +79 -0
- unxt-0.1.dev463/packages/unxt-hypothesis/tests/test_from_type.py +71 -0
- unxt-0.1.dev463/packages/unxt-hypothesis/tests/test_quantities.py +568 -0
- unxt-0.1.dev463/packages/unxt-hypothesis/tests/test_strategies_dimensions.py +30 -0
- unxt-0.1.dev463/packages/unxt-hypothesis/tests/test_strategies_units.py +200 -0
- unxt-0.1.dev463/packages/unxt-hypothesis/tests/test_strategies_unitsystems.py +124 -0
- unxt-0.1.dev463/paper/paper.bib +117 -0
- unxt-0.1.dev463/paper/paper.md +62 -0
- unxt-0.1.dev463/paper/paper.pdf +0 -0
- unxt-0.1.dev463/pyproject.toml +375 -0
- unxt-0.1.dev463/scripts/QUICK_RELEASE.md +169 -0
- unxt-0.1.dev463/scripts/get_version.py +228 -0
- unxt-0.1.dev463/scripts/validate_tag.py +113 -0
- unxt-0.1.dev463/src/unxt/__init__.py +79 -0
- unxt-0.1.dev463/src/unxt/_interop/__init__.py +18 -0
- unxt-0.1.dev463/src/unxt/_interop/optional_deps.py +13 -0
- unxt-0.1.dev463/src/unxt/_interop/unxt_interop_astropy/__init__.py +5 -0
- unxt-0.1.dev463/src/unxt/_interop/unxt_interop_astropy/custom_types.py +12 -0
- unxt-0.1.dev463/src/unxt/_interop/unxt_interop_astropy/dimensions.py +53 -0
- unxt-0.1.dev463/src/unxt/_interop/unxt_interop_astropy/quantity.py +308 -0
- unxt-0.1.dev463/src/unxt/_interop/unxt_interop_astropy/units.py +111 -0
- unxt-0.1.dev463/src/unxt/_interop/unxt_interop_gala/__init__.py +5 -0
- unxt-0.1.dev463/src/unxt/_interop/unxt_interop_gala/unitsystems.py +108 -0
- unxt-0.1.dev463/src/unxt/_interop/unxt_interop_mpl/__init__.py +81 -0
- unxt-0.1.dev463/src/unxt/_src/__init__.py +6 -0
- unxt-0.1.dev463/src/unxt/_src/dimensions.py +400 -0
- unxt-0.1.dev463/src/unxt/_src/experimental.py +234 -0
- unxt-0.1.dev463/src/unxt/_src/quantity/__init__.py +24 -0
- unxt-0.1.dev463/src/unxt/_src/quantity/angle.py +58 -0
- unxt-0.1.dev463/src/unxt/_src/quantity/base.py +1160 -0
- unxt-0.1.dev463/src/unxt/_src/quantity/base_angle.py +89 -0
- unxt-0.1.dev463/src/unxt/_src/quantity/base_parametric.py +290 -0
- unxt-0.1.dev463/src/unxt/_src/quantity/flag.py +137 -0
- unxt-0.1.dev463/src/unxt/_src/quantity/mixins.py +238 -0
- unxt-0.1.dev463/src/unxt/_src/quantity/quantity.py +123 -0
- unxt-0.1.dev463/src/unxt/_src/quantity/register_api.py +383 -0
- unxt-0.1.dev463/src/unxt/_src/quantity/register_conversions.py +95 -0
- unxt-0.1.dev463/src/unxt/_src/quantity/register_dispatches.py +246 -0
- unxt-0.1.dev463/src/unxt/_src/quantity/register_primitives.py +5075 -0
- unxt-0.1.dev463/src/unxt/_src/quantity/static_quantity.py +90 -0
- unxt-0.1.dev463/src/unxt/_src/quantity/unchecked.py +45 -0
- unxt-0.1.dev463/src/unxt/_src/quantity/value.py +289 -0
- unxt-0.1.dev463/src/unxt/_src/units.py +103 -0
- unxt-0.1.dev463/src/unxt/_src/unitsystems/__init__.py +40 -0
- unxt-0.1.dev463/src/unxt/_src/unitsystems/base.py +258 -0
- unxt-0.1.dev463/src/unxt/_src/unitsystems/builtin.py +190 -0
- unxt-0.1.dev463/src/unxt/_src/unitsystems/builtin_dimensions.py +21 -0
- unxt-0.1.dev463/src/unxt/_src/unitsystems/compare.py +13 -0
- unxt-0.1.dev463/src/unxt/_src/unitsystems/core.py +307 -0
- unxt-0.1.dev463/src/unxt/_src/unitsystems/flags.py +80 -0
- unxt-0.1.dev463/src/unxt/_src/unitsystems/realizations.py +73 -0
- unxt-0.1.dev463/src/unxt/_src/unitsystems/utils.py +95 -0
- unxt-0.1.dev463/src/unxt/_src/utils.py +112 -0
- unxt-0.1.dev463/src/unxt/_version.py +34 -0
- unxt-0.1.dev463/src/unxt/_version.pyi +2 -0
- unxt-0.1.dev463/src/unxt/dims.py +19 -0
- unxt-0.1.dev463/src/unxt/py.typed +0 -0
- unxt-0.1.dev463/src/unxt/quantity.py +172 -0
- unxt-0.1.dev463/src/unxt/setup_package.py +60 -0
- unxt-0.1.dev463/src/unxt/units.py +19 -0
- unxt-0.1.dev463/src/unxt/unitsystems.py +67 -0
- unxt-0.1.dev463/tests/__init__.py +1 -0
- unxt-0.1.dev463/tests/benchmark/__init__.py +1 -0
- unxt-0.1.dev463/tests/benchmark/test_dims.py +82 -0
- unxt-0.1.dev463/tests/benchmark/test_quaxed.py +135 -0
- unxt-0.1.dev463/tests/benchmark/test_uconvert_value.py +125 -0
- unxt-0.1.dev463/tests/benchmark/test_units.py +77 -0
- unxt-0.1.dev463/tests/benchmark/test_unitsystems.py +126 -0
- unxt-0.1.dev463/tests/integration/__init__.py +1 -0
- unxt-0.1.dev463/tests/integration/equinox/__init__.py +1 -0
- unxt-0.1.dev463/tests/integration/equinox/test_module.py +16 -0
- unxt-0.1.dev463/tests/integration/matplotlib/__init__.py +1 -0
- unxt-0.1.dev463/tests/integration/matplotlib/baseline/test_labels_axes.png +0 -0
- unxt-0.1.dev463/tests/integration/matplotlib/test_interop_mpl.py +18 -0
- unxt-0.1.dev463/tests/integration/quax/__init__.py +1 -0
- unxt-0.1.dev463/tests/integration/quax/test_lora.py +21 -0
- unxt-0.1.dev463/tests/integration/quaxed/__init__.py +1 -0
- unxt-0.1.dev463/tests/integration/quaxed/test_lax.py +413 -0
- unxt-0.1.dev463/tests/integration/quaxed/test_numpy.py +2008 -0
- unxt-0.1.dev463/tests/integration/test_plum.py +29 -0
- unxt-0.1.dev463/tests/integration/test_uconvert_value_performance.py +107 -0
- unxt-0.1.dev463/tests/unit/__init__.py +1 -0
- unxt-0.1.dev463/tests/unit/test_astropy_interop_quantity.py +233 -0
- unxt-0.1.dev463/tests/unit/test_dims.py +190 -0
- unxt-0.1.dev463/tests/unit/test_get_version_script.py +31 -0
- unxt-0.1.dev463/tests/unit/test_package.py +10 -0
- unxt-0.1.dev463/tests/unit/test_quantity.py +1372 -0
- unxt-0.1.dev463/tests/unit/test_quantity_printing.py +93 -0
- unxt-0.1.dev463/tests/unit/test_static_quantity.py +586 -0
- unxt-0.1.dev463/tests/unit/test_unitsystems.py +241 -0
- unxt-0.1.dev463/uv.lock +4187 -0
|
@@ -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,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/**"
|