ucon 0.5.0__tar.gz → 0.5.2__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.
Files changed (53) hide show
  1. {ucon-0.5.0 → ucon-0.5.2}/PKG-INFO +105 -3
  2. {ucon-0.5.0 → ucon-0.5.2}/README.md +104 -2
  3. {ucon-0.5.0 → ucon-0.5.2}/ROADMAP.md +51 -40
  4. ucon-0.5.2/tests/ucon/test_basis_transform.py +521 -0
  5. ucon-0.5.2/tests/ucon/test_graph_basis_transform.py +263 -0
  6. ucon-0.5.2/tests/ucon/test_rebased_unit.py +184 -0
  7. ucon-0.5.2/tests/ucon/test_uncertainty.py +264 -0
  8. ucon-0.5.2/tests/ucon/test_unit_system.py +174 -0
  9. ucon-0.5.2/tests/ucon/test_vector_fraction.py +185 -0
  10. {ucon-0.5.0 → ucon-0.5.2}/ucon/__init__.py +20 -2
  11. {ucon-0.5.0 → ucon-0.5.2}/ucon/algebra.py +36 -14
  12. {ucon-0.5.0 → ucon-0.5.2}/ucon/core.py +558 -12
  13. {ucon-0.5.0 → ucon-0.5.2}/ucon/graph.py +167 -10
  14. {ucon-0.5.0 → ucon-0.5.2}/ucon/maps.py +22 -1
  15. {ucon-0.5.0 → ucon-0.5.2}/ucon/units.py +28 -1
  16. {ucon-0.5.0 → ucon-0.5.2}/ucon.egg-info/PKG-INFO +105 -3
  17. {ucon-0.5.0 → ucon-0.5.2}/ucon.egg-info/SOURCES.txt +6 -0
  18. {ucon-0.5.0 → ucon-0.5.2}/.github/workflows/publish.yaml +0 -0
  19. {ucon-0.5.0 → ucon-0.5.2}/.github/workflows/tests.yaml +0 -0
  20. {ucon-0.5.0 → ucon-0.5.2}/.gitignore +0 -0
  21. {ucon-0.5.0 → ucon-0.5.2}/LICENSE +0 -0
  22. {ucon-0.5.0 → ucon-0.5.2}/NOTICE +0 -0
  23. {ucon-0.5.0 → ucon-0.5.2}/docs/decisions/composable-unit-algebra.md +0 -0
  24. {ucon-0.5.0 → ucon-0.5.2}/docs/decisions/composite-units.md +0 -0
  25. {ucon-0.5.0 → ucon-0.5.2}/docs/decisions/pseudo-dimension-tuple-values.md +0 -0
  26. {ucon-0.5.0 → ucon-0.5.2}/docs/decisions/unit-algebra-naming.md +0 -0
  27. {ucon-0.5.0 → ucon-0.5.2}/docs/decisions/unity-distance-metric-for-nearest-scale.md +0 -0
  28. {ucon-0.5.0 → ucon-0.5.2}/docs/explainers/exponent-scale-relationship.md +0 -0
  29. {ucon-0.5.0 → ucon-0.5.2}/docs/explainers/type-operation-matrix.md +0 -0
  30. {ucon-0.5.0 → ucon-0.5.2}/docs/explainers/why-algebraic-closure-matters.md +0 -0
  31. {ucon-0.5.0 → ucon-0.5.2}/docs/explainers/why-type-safety-matters.md +0 -0
  32. {ucon-0.5.0 → ucon-0.5.2}/docs/proposals/interface-unifying-the-value-layer.md +0 -0
  33. {ucon-0.5.0 → ucon-0.5.2}/docs/proposals/project_unified-algebraic-core.md +0 -0
  34. {ucon-0.5.0 → ucon-0.5.2}/docs/proposals/support-for-fractional-exponents.md +0 -0
  35. {ucon-0.5.0 → ucon-0.5.2}/docs/proposals/unified-unit-presentation.md +0 -0
  36. {ucon-0.5.0 → ucon-0.5.2}/noxfile.py +0 -0
  37. {ucon-0.5.0 → ucon-0.5.2}/requirements.txt +0 -0
  38. {ucon-0.5.0 → ucon-0.5.2}/setup.cfg +0 -0
  39. {ucon-0.5.0 → ucon-0.5.2}/setup.py +0 -0
  40. {ucon-0.5.0 → ucon-0.5.2}/tests/__init__.py +0 -0
  41. {ucon-0.5.0 → ucon-0.5.2}/tests/ucon/__init__.py +0 -0
  42. {ucon-0.5.0 → ucon-0.5.2}/tests/ucon/conversion/__init__.py +0 -0
  43. {ucon-0.5.0 → ucon-0.5.2}/tests/ucon/conversion/test_graph.py +0 -0
  44. {ucon-0.5.0 → ucon-0.5.2}/tests/ucon/conversion/test_map.py +0 -0
  45. {ucon-0.5.0 → ucon-0.5.2}/tests/ucon/test_algebra.py +0 -0
  46. {ucon-0.5.0 → ucon-0.5.2}/tests/ucon/test_core.py +0 -0
  47. {ucon-0.5.0 → ucon-0.5.2}/tests/ucon/test_default_graph_conversions.py +0 -0
  48. {ucon-0.5.0 → ucon-0.5.2}/tests/ucon/test_dimensionless_units.py +0 -0
  49. {ucon-0.5.0 → ucon-0.5.2}/tests/ucon/test_quantity.py +0 -0
  50. {ucon-0.5.0 → ucon-0.5.2}/tests/ucon/test_units.py +0 -0
  51. {ucon-0.5.0 → ucon-0.5.2}/ucon/quantity.py +0 -0
  52. {ucon-0.5.0 → ucon-0.5.2}/ucon.egg-info/dependency_links.txt +0 -0
  53. {ucon-0.5.0 → ucon-0.5.2}/ucon.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ucon
3
- Version: 0.5.0
3
+ Version: 0.5.2
4
4
  Summary: a tool for dimensional analysis: a "Unit CONverter"
5
5
  Home-page: https://github.com/withtwoemms/ucon
6
6
  Author: Emmanuel I. Obi
@@ -67,6 +67,7 @@ It combines **units**, **scales**, and **dimensions** into a composable algebra
67
67
  - Scale-aware arithmetic via `UnitFactor` and `UnitProduct`
68
68
  - Metric and binary prefixes (`kilo`, `kibi`, `micro`, `mebi`, etc.)
69
69
  - Pseudo-dimensions for angles, solid angles, and ratios with semantic isolation
70
+ - Uncertainty propagation through arithmetic and conversions
70
71
  - A clean foundation for physics, chemistry, data modeling, and beyond
71
72
 
72
73
  Think of it as **`decimal.Decimal` for the physical world** — precise, predictable, and type-safe.
@@ -91,6 +92,9 @@ To best answer this question, we turn to an age-old technique ([dimensional anal
91
92
  | **`Ratio`** | `ucon.core` | Represents the division of two `Number` objects; captures relationships between quantities. | Expressing rates, densities, efficiencies (e.g., energy / time = power, length / time = velocity). |
92
93
  | **`Map`** hierarchy | `ucon.maps` | Composable conversion morphisms: `LinearMap`, `AffineMap`, `ComposedMap`. | Defining conversion functions between units (e.g., meter→foot, celsius→kelvin). |
93
94
  | **`ConversionGraph`** | `ucon.graph` | Registry of unit conversion edges with BFS path composition. | Converting between units via `Number.to(target)`; managing default and custom graphs. |
95
+ | **`UnitSystem`** | `ucon.core` | Named mapping from dimensions to base units (e.g., SI, Imperial). | Defining coherent unit systems; grouping base units by dimension. |
96
+ | **`BasisTransform`** | `ucon.core` | Matrix-based transformation between dimensional exponent spaces. | Converting between incompatible dimensional structures; exact arithmetic with `Fraction`. |
97
+ | **`RebasedUnit`** | `ucon.core` | A unit rebased to another system's dimension, preserving provenance. | Cross-basis conversions; tracking original unit through basis changes. |
94
98
  | **`units` module** | `ucon.units` | Defines canonical unit instances (SI, imperial, information, and derived units). | Quick access to standard physical units (`units.meter`, `units.foot`, `units.byte`, etc.). |
95
99
 
96
100
  ### Under the Hood
@@ -215,6 +219,102 @@ print(ratio.to(units.ppm)) # <500000.0 ppm>
215
219
  units.radian(1).to(units.percent) # raises ConversionNotFound
216
220
  ```
217
221
 
222
+ Uncertainty propagates through arithmetic and conversions:
223
+ ```python
224
+ from ucon import units
225
+
226
+ # Measurements with uncertainty
227
+ length = units.meter(1.234, uncertainty=0.005)
228
+ width = units.meter(0.567, uncertainty=0.003)
229
+
230
+ print(length) # <1.234 ± 0.005 m>
231
+
232
+ # Uncertainty propagates through arithmetic (quadrature)
233
+ area = length * width
234
+ print(area) # <0.699678 ± 0.00424... m²>
235
+
236
+ # Uncertainty propagates through conversion
237
+ length_ft = length.to(units.foot)
238
+ print(length_ft) # <4.048... ± 0.0164... ft>
239
+ ```
240
+
241
+ Unit systems and basis transforms enable conversions between incompatible dimensional structures.
242
+ This goes beyond simple unit conversion (meter → foot) into structural transformation:
243
+
244
+ ```python
245
+ from fractions import Fraction
246
+ from ucon import BasisTransform, Dimension, Unit, UnitSystem, units
247
+ from ucon.graph import ConversionGraph
248
+ from ucon.maps import LinearMap
249
+
250
+ # The realm of Valdris has three fundamental dimensions:
251
+ # - Aether (A): magical energy substrate
252
+ # - Resonance (R): vibrational frequency of magic
253
+ # - Substance (S): physical matter
254
+ #
255
+ # These combine into SI dimensions via a transformation matrix:
256
+ #
257
+ # | L | | 2 0 0 | | A |
258
+ # | M | = | 1 0 1 | × | R |
259
+ # | T | |-2 -1 0 | | S |
260
+ #
261
+ # Reading the columns:
262
+ # - 1 aether contributes: L², M, T⁻² (energy-like)
263
+ # - 1 resonance contributes: T⁻¹ (frequency-like)
264
+ # - 1 substance contributes: M (mass-like)
265
+
266
+ # Fantasy base units
267
+ mote = Unit(name='mote', dimension=Dimension.energy, aliases=('mt',))
268
+ chime = Unit(name='chime', dimension=Dimension.frequency, aliases=('ch',))
269
+ ite = Unit(name='ite', dimension=Dimension.mass, aliases=('it',))
270
+
271
+ valdris = UnitSystem(
272
+ name="Valdris",
273
+ bases={
274
+ Dimension.energy: mote,
275
+ Dimension.frequency: chime,
276
+ Dimension.mass: ite,
277
+ }
278
+ )
279
+
280
+ # The basis transform encodes how Valdris dimensions compose into SI
281
+ valdris_to_si = BasisTransform(
282
+ src=valdris,
283
+ dst=units.si,
284
+ src_dimensions=(Dimension.energy, Dimension.frequency, Dimension.mass),
285
+ dst_dimensions=(Dimension.energy, Dimension.frequency, Dimension.mass),
286
+ matrix=(
287
+ (2, 0, 0), # energy: 2 × aether
288
+ (1, 0, 1), # frequency: aether + substance
289
+ (-2, -1, 0), # mass: -2×aether - resonance
290
+ ),
291
+ )
292
+
293
+ # Physical calibration: how many SI units per fantasy unit
294
+ graph = ConversionGraph()
295
+ graph.connect_systems(
296
+ basis_transform=valdris_to_si,
297
+ edges={
298
+ (mote, units.joule): LinearMap(42), # 1 mote = 42 J
299
+ (chime, units.hertz): LinearMap(7), # 1 chime = 7 Hz
300
+ (ite, units.kilogram): LinearMap(Fraction(1, 2)), # 1 ite = 0.5 kg
301
+ }
302
+ )
303
+
304
+ # Game engine converts between physics systems
305
+ energy_map = graph.convert(src=mote, dst=units.joule)
306
+ energy_map(10) # 420 joules from 10 motes
307
+
308
+ # Inverse: display real-world values in game units
309
+ joule_to_mote = graph.convert(src=units.joule, dst=mote)
310
+ joule_to_mote(420) # 10 motes
311
+
312
+ # The transform is invertible with exact Fraction arithmetic
313
+ valdris_to_si.is_invertible # True
314
+ ```
315
+
316
+ This enables fantasy game physics, or any field where the dimensional structure differs from SI.
317
+
218
318
  ---
219
319
 
220
320
  ## Roadmap Highlights
@@ -224,8 +324,10 @@ units.radian(1).to(units.percent) # raises ConversionNotFound
224
324
  | **0.3.x** | Dimensional Algebra | Unit/Scale separation, `UnitFactor`, `UnitProduct` | ✅ Complete |
225
325
  | **0.4.x** | Conversion System | `ConversionGraph`, `Number.to()`, callable units | ✅ Complete |
226
326
  | **0.5.0** | Dimensionless Units | Pseudo-dimensions for angle, solid angle, ratio | ✅ Complete |
227
- | **0.5.x** | Metrology | Uncertainty propagation, `UnitSystem` | 🚧 In Progress |
228
- | **0.7.x** | Pydantic Integration | Type-safe quantity validation | Planned |
327
+ | **0.5.x** | Uncertainty | Propagation through arithmetic and conversions | Complete |
328
+ | **0.5.x** | Unit Systems | `BasisTransform`, `UnitSystem`, cross-basis conversion | Complete |
329
+ | **0.6.x** | Pydantic Integration | Type-safe quantity validation | ⏳ Planned |
330
+ | **0.7.x** | NumPy Arrays | Vectorized conversion and arithmetic | ⏳ Planned |
229
331
 
230
332
  See full roadmap: [ROADMAP.md](./ROADMAP.md)
231
333
 
@@ -30,6 +30,7 @@ It combines **units**, **scales**, and **dimensions** into a composable algebra
30
30
  - Scale-aware arithmetic via `UnitFactor` and `UnitProduct`
31
31
  - Metric and binary prefixes (`kilo`, `kibi`, `micro`, `mebi`, etc.)
32
32
  - Pseudo-dimensions for angles, solid angles, and ratios with semantic isolation
33
+ - Uncertainty propagation through arithmetic and conversions
33
34
  - A clean foundation for physics, chemistry, data modeling, and beyond
34
35
 
35
36
  Think of it as **`decimal.Decimal` for the physical world** — precise, predictable, and type-safe.
@@ -54,6 +55,9 @@ To best answer this question, we turn to an age-old technique ([dimensional anal
54
55
  | **`Ratio`** | `ucon.core` | Represents the division of two `Number` objects; captures relationships between quantities. | Expressing rates, densities, efficiencies (e.g., energy / time = power, length / time = velocity). |
55
56
  | **`Map`** hierarchy | `ucon.maps` | Composable conversion morphisms: `LinearMap`, `AffineMap`, `ComposedMap`. | Defining conversion functions between units (e.g., meter→foot, celsius→kelvin). |
56
57
  | **`ConversionGraph`** | `ucon.graph` | Registry of unit conversion edges with BFS path composition. | Converting between units via `Number.to(target)`; managing default and custom graphs. |
58
+ | **`UnitSystem`** | `ucon.core` | Named mapping from dimensions to base units (e.g., SI, Imperial). | Defining coherent unit systems; grouping base units by dimension. |
59
+ | **`BasisTransform`** | `ucon.core` | Matrix-based transformation between dimensional exponent spaces. | Converting between incompatible dimensional structures; exact arithmetic with `Fraction`. |
60
+ | **`RebasedUnit`** | `ucon.core` | A unit rebased to another system's dimension, preserving provenance. | Cross-basis conversions; tracking original unit through basis changes. |
57
61
  | **`units` module** | `ucon.units` | Defines canonical unit instances (SI, imperial, information, and derived units). | Quick access to standard physical units (`units.meter`, `units.foot`, `units.byte`, etc.). |
58
62
 
59
63
  ### Under the Hood
@@ -178,6 +182,102 @@ print(ratio.to(units.ppm)) # <500000.0 ppm>
178
182
  units.radian(1).to(units.percent) # raises ConversionNotFound
179
183
  ```
180
184
 
185
+ Uncertainty propagates through arithmetic and conversions:
186
+ ```python
187
+ from ucon import units
188
+
189
+ # Measurements with uncertainty
190
+ length = units.meter(1.234, uncertainty=0.005)
191
+ width = units.meter(0.567, uncertainty=0.003)
192
+
193
+ print(length) # <1.234 ± 0.005 m>
194
+
195
+ # Uncertainty propagates through arithmetic (quadrature)
196
+ area = length * width
197
+ print(area) # <0.699678 ± 0.00424... m²>
198
+
199
+ # Uncertainty propagates through conversion
200
+ length_ft = length.to(units.foot)
201
+ print(length_ft) # <4.048... ± 0.0164... ft>
202
+ ```
203
+
204
+ Unit systems and basis transforms enable conversions between incompatible dimensional structures.
205
+ This goes beyond simple unit conversion (meter → foot) into structural transformation:
206
+
207
+ ```python
208
+ from fractions import Fraction
209
+ from ucon import BasisTransform, Dimension, Unit, UnitSystem, units
210
+ from ucon.graph import ConversionGraph
211
+ from ucon.maps import LinearMap
212
+
213
+ # The realm of Valdris has three fundamental dimensions:
214
+ # - Aether (A): magical energy substrate
215
+ # - Resonance (R): vibrational frequency of magic
216
+ # - Substance (S): physical matter
217
+ #
218
+ # These combine into SI dimensions via a transformation matrix:
219
+ #
220
+ # | L | | 2 0 0 | | A |
221
+ # | M | = | 1 0 1 | × | R |
222
+ # | T | |-2 -1 0 | | S |
223
+ #
224
+ # Reading the columns:
225
+ # - 1 aether contributes: L², M, T⁻² (energy-like)
226
+ # - 1 resonance contributes: T⁻¹ (frequency-like)
227
+ # - 1 substance contributes: M (mass-like)
228
+
229
+ # Fantasy base units
230
+ mote = Unit(name='mote', dimension=Dimension.energy, aliases=('mt',))
231
+ chime = Unit(name='chime', dimension=Dimension.frequency, aliases=('ch',))
232
+ ite = Unit(name='ite', dimension=Dimension.mass, aliases=('it',))
233
+
234
+ valdris = UnitSystem(
235
+ name="Valdris",
236
+ bases={
237
+ Dimension.energy: mote,
238
+ Dimension.frequency: chime,
239
+ Dimension.mass: ite,
240
+ }
241
+ )
242
+
243
+ # The basis transform encodes how Valdris dimensions compose into SI
244
+ valdris_to_si = BasisTransform(
245
+ src=valdris,
246
+ dst=units.si,
247
+ src_dimensions=(Dimension.energy, Dimension.frequency, Dimension.mass),
248
+ dst_dimensions=(Dimension.energy, Dimension.frequency, Dimension.mass),
249
+ matrix=(
250
+ (2, 0, 0), # energy: 2 × aether
251
+ (1, 0, 1), # frequency: aether + substance
252
+ (-2, -1, 0), # mass: -2×aether - resonance
253
+ ),
254
+ )
255
+
256
+ # Physical calibration: how many SI units per fantasy unit
257
+ graph = ConversionGraph()
258
+ graph.connect_systems(
259
+ basis_transform=valdris_to_si,
260
+ edges={
261
+ (mote, units.joule): LinearMap(42), # 1 mote = 42 J
262
+ (chime, units.hertz): LinearMap(7), # 1 chime = 7 Hz
263
+ (ite, units.kilogram): LinearMap(Fraction(1, 2)), # 1 ite = 0.5 kg
264
+ }
265
+ )
266
+
267
+ # Game engine converts between physics systems
268
+ energy_map = graph.convert(src=mote, dst=units.joule)
269
+ energy_map(10) # 420 joules from 10 motes
270
+
271
+ # Inverse: display real-world values in game units
272
+ joule_to_mote = graph.convert(src=units.joule, dst=mote)
273
+ joule_to_mote(420) # 10 motes
274
+
275
+ # The transform is invertible with exact Fraction arithmetic
276
+ valdris_to_si.is_invertible # True
277
+ ```
278
+
279
+ This enables fantasy game physics, or any field where the dimensional structure differs from SI.
280
+
181
281
  ---
182
282
 
183
283
  ## Roadmap Highlights
@@ -187,8 +287,10 @@ units.radian(1).to(units.percent) # raises ConversionNotFound
187
287
  | **0.3.x** | Dimensional Algebra | Unit/Scale separation, `UnitFactor`, `UnitProduct` | ✅ Complete |
188
288
  | **0.4.x** | Conversion System | `ConversionGraph`, `Number.to()`, callable units | ✅ Complete |
189
289
  | **0.5.0** | Dimensionless Units | Pseudo-dimensions for angle, solid angle, ratio | ✅ Complete |
190
- | **0.5.x** | Metrology | Uncertainty propagation, `UnitSystem` | 🚧 In Progress |
191
- | **0.7.x** | Pydantic Integration | Type-safe quantity validation | Planned |
290
+ | **0.5.x** | Uncertainty | Propagation through arithmetic and conversions | Complete |
291
+ | **0.5.x** | Unit Systems | `BasisTransform`, `UnitSystem`, cross-basis conversion | Complete |
292
+ | **0.6.x** | Pydantic Integration | Type-safe quantity validation | ⏳ Planned |
293
+ | **0.7.x** | NumPy Arrays | Vectorized conversion and arithmetic | ⏳ Planned |
192
294
 
193
295
  See full roadmap: [ROADMAP.md](./ROADMAP.md)
194
296
 
@@ -23,10 +23,10 @@ ucon is a dimensional analysis library for engineers building systems where unit
23
23
  | v0.3.x | Dimensional Algebra | Complete |
24
24
  | v0.4.x | Core Conversion + Information | Complete |
25
25
  | v0.5.0 | Dimensionless Units | Complete |
26
- | v0.5.x | Uncertainty Propagation | Planned |
27
- | v0.5.x | BasisMap + UnitSystem | Planned |
28
- | v0.6.0 | NumPy Array Support | Planned |
29
- | v0.7.0 | Pydantic + Serialization | Planned |
26
+ | v0.5.x | Uncertainty Propagation | Complete |
27
+ | v0.5.x | BasisTransform + UnitSystem | Complete |
28
+ | v0.6.0 | Pydantic + Serialization | Planned |
29
+ | v0.7.0 | NumPy Array Support | Planned |
30
30
  | v0.8.0 | String Parsing | Planned |
31
31
  | v0.9.0 | Constants + Logarithmic Units | Planned |
32
32
  | v0.10.0 | DataFrame Integration | Planned |
@@ -34,17 +34,21 @@ ucon is a dimensional analysis library for engineers building systems where unit
34
34
 
35
35
  ---
36
36
 
37
- ## Current Version: **v0.5.x** (in progress)
37
+ ## Current Version: **v0.5.x** (complete)
38
38
 
39
39
  Building on v0.5.0 baseline:
40
- - `ucon.core` (`Dimension`, `Scale`, `Unit`, `UnitFactor`, `UnitProduct`, `Number`, `Ratio`)
41
- - `ucon.maps` (`Map`, `LinearMap`, `AffineMap`, `ComposedMap`)
42
- - `ucon.graph` (`ConversionGraph`, default graph, `get_default_graph()`, `using_graph()`)
43
- - `ucon.units` (SI + imperial + information + angle + ratio units, callable syntax)
40
+ - `ucon.core` (`Dimension`, `Scale`, `Unit`, `UnitFactor`, `UnitProduct`, `Number`, `Ratio`, `UnitSystem`, `BasisTransform`, `RebasedUnit`)
41
+ - `ucon.maps` (`Map`, `LinearMap`, `AffineMap`, `ComposedMap` with `derivative()`)
42
+ - `ucon.graph` (`ConversionGraph`, default graph, `get_default_graph()`, `using_graph()`, cross-basis conversion)
43
+ - `ucon.units` (SI + imperial + information + angle + ratio units, callable syntax, `si` and `imperial` systems)
44
+ - `ucon.algebra` (`Vector` with `Fraction` exponents, `Exponent`)
44
45
  - Callable unit API: `meter(5)`, `(mile / hour)(60)`
45
46
  - `Number.simplify()` for base-scale normalization
46
47
  - `Dimension.information` with `units.bit`, `units.byte`
47
48
  - Pseudo-dimensions: `angle`, `solid_angle`, `ratio` with semantic isolation
49
+ - Uncertainty propagation: `meter(1.234, uncertainty=0.005)` with quadrature arithmetic
50
+ - `BasisTransform` for cross-system dimensional mapping with exact matrix arithmetic
51
+ - `UnitSystem` for named dimension-to-unit groupings
48
52
 
49
53
  ---
50
54
 
@@ -116,15 +120,15 @@ Building on v0.5.0 baseline:
116
120
 
117
121
  ---
118
122
 
119
- ## v0.5.x — Uncertainty Propagation
123
+ ## v0.5.x — Uncertainty Propagation (Complete)
120
124
 
121
125
  **Theme:** Metrology foundation.
122
126
 
123
- - [ ] `Number.uncertainty: float | None`
124
- - [ ] Propagation through arithmetic (uncorrelated, quadrature)
125
- - [ ] Propagation through conversion via `Map.derivative()`
126
- - [ ] Construction: `meter(1.234, uncertainty=0.005)`
127
- - [ ] Display: `1.234 ± 0.005 meter`
127
+ - [x] `Number.uncertainty: float | None`
128
+ - [x] Propagation through arithmetic (uncorrelated, quadrature)
129
+ - [x] Propagation through conversion via `Map.derivative()`
130
+ - [x] Construction: `meter(1.234, uncertainty=0.005)`
131
+ - [x] Display: `1.234 ± 0.005 meter`
128
132
 
129
133
  **Outcomes:**
130
134
  - First-class uncertainty support for scientific and engineering workflows
@@ -133,40 +137,31 @@ Building on v0.5.0 baseline:
133
137
 
134
138
  ---
135
139
 
136
- ## v0.5.x — BasisMap + UnitSystem
140
+ ## v0.5.x — BasisTransform + UnitSystem (Complete)
137
141
 
138
142
  **Theme:** Cross-system architecture.
139
143
 
140
- - [ ] `UnitSystem` class (named grouping of base units)
141
- - [ ] `BasisMap` class (structural equivalence between systems)
142
- - [ ] Prebuilt systems: `si`, `imperial`, `cgs`
143
- - [ ] `graph.connect_systems()` for bulk edge creation
144
- - [ ] Support for custom domain dimensions
144
+ - [x] `Vector` with `Fraction` exponents for exact arithmetic
145
+ - [x] `UnitSystem` class (named dimension-to-unit mapping)
146
+ - [x] `BasisTransform` class (matrix-based dimensional basis transformation)
147
+ - [x] `RebasedUnit` class (provenance-preserving cross-basis unit)
148
+ - [x] `NonInvertibleTransform` exception for surjective transforms
149
+ - [x] Prebuilt systems: `units.si`, `units.imperial`
150
+ - [x] `graph.add_edge()` with `basis_transform` parameter
151
+ - [x] `graph.connect_systems()` for bulk edge creation
152
+ - [x] Cross-basis conversion via rebased paths
153
+ - [x] Introspection: `list_transforms()`, `list_rebased_units()`, `edges_for_transform()`
145
154
 
146
155
  **Outcomes:**
147
- - `BasisMap` enables system-aware "express in base units" functionality
156
+ - `BasisTransform` enables conversions between incompatible dimensional structures
157
+ - Matrix operations with exact `Fraction` arithmetic (no floating-point drift)
158
+ - Invertibility detection with clear error messages for surjective transforms
148
159
  - Named unit systems for domain-specific workflows
149
- - Foundation for plugin-style system extensions
160
+ - Foundation for custom dimension domains
150
161
 
151
162
  ---
152
163
 
153
- ## v0.6.0 — NumPy Array Support
154
-
155
- **Theme:** Scientific computing integration.
156
-
157
- - [ ] `Number` wraps `np.ndarray` values
158
- - [ ] Vectorized conversion
159
- - [ ] Vectorized arithmetic with uncertainty propagation
160
- - [ ] Performance benchmarks
161
-
162
- **Outcomes:**
163
- - Seamless integration with NumPy-based scientific workflows
164
- - Efficient batch conversions for large datasets
165
- - Performance characteristics documented and optimized
166
-
167
- ---
168
-
169
- ## v0.7.0 — Pydantic + Serialization
164
+ ## v0.6.0 — Pydantic + Serialization
170
165
 
171
166
  **Theme:** API and persistence integration.
172
167
 
@@ -182,6 +177,22 @@ Building on v0.5.0 baseline:
182
177
 
183
178
  ---
184
179
 
180
+ ## v0.7.0 — NumPy Array Support
181
+
182
+ **Theme:** Scientific computing integration.
183
+
184
+ - [ ] `Number` wraps `np.ndarray` values
185
+ - [ ] Vectorized conversion
186
+ - [ ] Vectorized arithmetic with uncertainty propagation
187
+ - [ ] Performance benchmarks
188
+
189
+ **Outcomes:**
190
+ - Seamless integration with NumPy-based scientific workflows
191
+ - Efficient batch conversions for large datasets
192
+ - Performance characteristics documented and optimized
193
+
194
+ ---
195
+
185
196
  ## v0.8.0 — String Parsing
186
197
 
187
198
  **Theme:** Ergonomic input.