ucon 0.3.5rc1__tar.gz → 0.4.0__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 (46) hide show
  1. {ucon-0.3.5rc1 → ucon-0.4.0}/PKG-INFO +58 -30
  2. {ucon-0.3.5rc1 → ucon-0.4.0}/README.md +57 -29
  3. ucon-0.4.0/ROADMAP.md +244 -0
  4. ucon-0.4.0/tests/ucon/conversion/__init__.py +0 -0
  5. ucon-0.4.0/tests/ucon/conversion/test_graph.py +175 -0
  6. ucon-0.4.0/tests/ucon/conversion/test_map.py +163 -0
  7. {ucon-0.3.5rc1 → ucon-0.4.0}/tests/ucon/test_algebra.py +34 -34
  8. {ucon-0.3.5rc1 → ucon-0.4.0}/tests/ucon/test_core.py +20 -20
  9. {ucon-0.3.5rc1 → ucon-0.4.0}/tests/ucon/test_quantity.py +205 -14
  10. {ucon-0.3.5rc1 → ucon-0.4.0}/ucon/__init__.py +6 -2
  11. {ucon-0.3.5rc1 → ucon-0.4.0}/ucon/algebra.py +9 -5
  12. {ucon-0.3.5rc1 → ucon-0.4.0}/ucon/core.py +388 -68
  13. ucon-0.4.0/ucon/graph.py +415 -0
  14. ucon-0.4.0/ucon/maps.py +161 -0
  15. ucon-0.4.0/ucon/quantity.py +17 -0
  16. ucon-0.4.0/ucon/units.py +130 -0
  17. {ucon-0.3.5rc1 → ucon-0.4.0}/ucon.egg-info/PKG-INFO +58 -30
  18. {ucon-0.3.5rc1 → ucon-0.4.0}/ucon.egg-info/SOURCES.txt +5 -0
  19. ucon-0.3.5rc1/ROADMAP.md +0 -222
  20. ucon-0.3.5rc1/ucon/quantity.py +0 -196
  21. ucon-0.3.5rc1/ucon/units.py +0 -87
  22. {ucon-0.3.5rc1 → ucon-0.4.0}/.github/workflows/publish.yaml +0 -0
  23. {ucon-0.3.5rc1 → ucon-0.4.0}/.github/workflows/tests.yaml +0 -0
  24. {ucon-0.3.5rc1 → ucon-0.4.0}/.gitignore +0 -0
  25. {ucon-0.3.5rc1 → ucon-0.4.0}/LICENSE +0 -0
  26. {ucon-0.3.5rc1 → ucon-0.4.0}/NOTICE +0 -0
  27. {ucon-0.3.5rc1 → ucon-0.4.0}/docs/decisions/composable-unit-algebra.md +0 -0
  28. {ucon-0.3.5rc1 → ucon-0.4.0}/docs/decisions/composite-units.md +0 -0
  29. {ucon-0.3.5rc1 → ucon-0.4.0}/docs/decisions/unit-algebra-naming.md +0 -0
  30. {ucon-0.3.5rc1 → ucon-0.4.0}/docs/decisions/unity-distance-metric-for-nearest-scale.md +0 -0
  31. {ucon-0.3.5rc1 → ucon-0.4.0}/docs/explainers/type-operation-matrix.md +0 -0
  32. {ucon-0.3.5rc1 → ucon-0.4.0}/docs/explainers/why-algebraic-closure-matters.md +0 -0
  33. {ucon-0.3.5rc1 → ucon-0.4.0}/docs/explainers/why-type-safety-matters.md +0 -0
  34. {ucon-0.3.5rc1 → ucon-0.4.0}/docs/proposals/interface-unifying-the-value-layer.md +0 -0
  35. {ucon-0.3.5rc1 → ucon-0.4.0}/docs/proposals/project_unified-algebraic-core.md +0 -0
  36. {ucon-0.3.5rc1 → ucon-0.4.0}/docs/proposals/support-for-fractional-exponents.md +0 -0
  37. {ucon-0.3.5rc1 → ucon-0.4.0}/docs/proposals/unified-unit-presentation.md +0 -0
  38. {ucon-0.3.5rc1 → ucon-0.4.0}/noxfile.py +0 -0
  39. {ucon-0.3.5rc1 → ucon-0.4.0}/requirements.txt +0 -0
  40. {ucon-0.3.5rc1 → ucon-0.4.0}/setup.cfg +0 -0
  41. {ucon-0.3.5rc1 → ucon-0.4.0}/setup.py +0 -0
  42. {ucon-0.3.5rc1 → ucon-0.4.0}/tests/__init__.py +0 -0
  43. {ucon-0.3.5rc1 → ucon-0.4.0}/tests/ucon/__init__.py +0 -0
  44. {ucon-0.3.5rc1 → ucon-0.4.0}/tests/ucon/test_units.py +0 -0
  45. {ucon-0.3.5rc1 → ucon-0.4.0}/ucon.egg-info/dependency_links.txt +0 -0
  46. {ucon-0.3.5rc1 → ucon-0.4.0}/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.3.5rc1
3
+ Version: 0.4.0
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
@@ -55,8 +55,8 @@ Dynamic: summary
55
55
  It combines **units**, **scales**, and **dimensions** into a composable algebra that supports:
56
56
 
57
57
  - Dimensional analysis through `Number` and `Ratio`
58
- - Scale-aware arithmetic and conversions
59
- - Metric and binary prefixes (`kilo`, `kibi`, `micro`, `mebi`, ect.)
58
+ - Scale-aware arithmetic via `UnitFactor` and `UnitProduct`
59
+ - Metric and binary prefixes (`kilo`, `kibi`, `micro`, `mebi`, etc.)
60
60
  - A clean foundation for physics, chemistry, data modeling, and beyond
61
61
 
62
62
  Think of it as **`decimal.Decimal` for the physical world** — precise, predictable, and type-safe.
@@ -70,20 +70,24 @@ The crux of this tiny library is to provide abstractions that simplify the answe
70
70
  To best answer this question, we turn to an age-old technique ([dimensional analysis](https://en.wikipedia.org/wiki/Dimensional_analysis)) which essentially allows for the solution to be written as a product of ratios. `ucon` comes equipped with some useful primitives:
71
71
  | Type | Defined In | Purpose | Typical Use Cases |
72
72
  | ----------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
73
- | **`Vector`** | `ucon.dimension` | Represents the exponent tuple of a physical quantitys base dimensions (e.g., T, L, M, I, Θ, J, N). | Internal representation of dimensional algebra; building derived quantities (e.g., area, velocity, force). |
74
- | **`Dimension`** | `ucon.dimension` | Encapsulates physical dimensions (e.g., length, time, mass) as algebraic combinations of vectors. | Enforcing dimensional consistency; defining relationships between quantities (e.g., length / time = velocity). |
75
- | **`Unit`** | `ucon.unit` | Represents a named, dimensioned measurement unit (e.g., meter, second, joule). | Attaching human-readable units to quantities; defining or composing new units (`newton = kilogram * meter / second²`). |
73
+ | **`Vector`** | `ucon.algebra` | Represents the 8-component exponent tuple of a physical quantity's base dimensions (T, L, M, I, Θ, J, N, B). | Internal representation of dimensional algebra; building derived quantities (e.g., area, velocity, force). |
74
+ | **`Exponent`** | `ucon.algebra` | Represents base-power pairs (e.g., 10³, 2¹⁰) used by `Scale`. | Performing arithmetic on powers and bases; normalizing scales across conversions. |
75
+ | **`Dimension`** | `ucon.core` | Encapsulates physical dimensions (e.g., length, time, mass) as algebraic combinations of vectors. | Enforcing dimensional consistency; defining relationships between quantities (e.g., length / time = velocity). |
76
76
  | **`Scale`** | `ucon.core` | Encodes powers of base magnitudes (binary or decimal prefixes like kilo-, milli-, mebi-). | Adjusting numeric scale without changing dimension (e.g., kilometer ↔ meter, byte ↔ kibibyte). |
77
- | **`Exponent`** | `ucon.core` | Represents base-power pairs (e.g., 10³, 2¹⁰) used by `Scale`. | Performing arithmetic on powers and bases; normalizing scales across conversions. |
78
- | **`Number`** | `ucon.core` | Combines a numeric quantity with a unit and scale; the primary measurable type. | Performing arithmetic with units; converting between compatible units; representing physical quantities like 5 m/s. |
77
+ | **`Unit`** | `ucon.core` | An atomic, scale-free measurement symbol (e.g., meter, second, joule) with a `Dimension`. | Defining base units; serving as graph nodes for future conversions. |
78
+ | **`UnitFactor`** | `ucon.core` | Pairs a `Unit` with a `Scale` (e.g., kilo + gram = kg). Used as keys inside `UnitProduct`. | Preserving user-provided scale prefixes through algebraic operations. |
79
+ | **`UnitProduct`** | `ucon.core` | A product/quotient of `UnitFactor`s with exponent tracking and simplification. | Representing composite units like m/s, kg·m/s², kJ·h. |
80
+ | **`Number`** | `ucon.core` | Combines a numeric quantity with a unit; the primary measurable type. | Performing arithmetic with units; representing physical quantities like 5 m/s. |
79
81
  | **`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). |
80
- | **`units` module** | `ucon.units` | Defines canonical unit instances (SI and common derived units). | Quick access to standard physical units (`units.meter`, `units.second`, `units.newton`, etc.). | |
82
+ | **`Map`** hierarchy | `ucon.maps` | Composable conversion morphisms: `LinearMap`, `AffineMap`, `ComposedMap`. | Defining conversion functions between units (e.g., meter→foot, celsius→kelvin). |
83
+ | **`ConversionGraph`** | `ucon.graph` | Registry of unit conversion edges with BFS path composition. | Converting between units via `Number.to(target)`; managing default and custom graphs. |
84
+ | **`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.). |
81
85
 
82
86
  ### Under the Hood
83
87
 
84
88
  `ucon` models unit math through a hierarchy where each layer builds on the last:
85
89
 
86
- <img src=https://gist.githubusercontent.com/withtwoemms/429d2ca1f979865aa80a2658bf9efa32/raw/0c704737a52b9e4a87cda5c839e9aa40f7e5bb48/ucon.data-model_v035.png align="center" alt="ucon Data Model" width=600/>
90
+ <img src=https://gist.githubusercontent.com/withtwoemms/429d2ca1f979865aa80a2658bf9efa32/raw/5df6a7fb2a6426ee6804096c092c10bed1b30b6f/ucon.data-model_v040.png align="center" alt="ucon Data Model" width=600/>
87
91
 
88
92
  ## Why `ucon`?
89
93
 
@@ -91,24 +95,22 @@ Python already has mature libraries for handling units and physical quantities
91
95
 
92
96
  | Library | Focus | Limitation |
93
97
  | --------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
94
- | **Pint** | Runtime unit conversion and compatibility checking | Treats quantities as decorated numbers — conversions work, but the algebra behind them isnt inspectable or type-safe. |
98
+ | **Pint** | Runtime unit conversion and compatibility checking | Treats quantities as decorated numbers — conversions work, but the algebra behind them isn't inspectable or type-safe. |
95
99
  | **SymPy** | Symbolic algebra and simplification of unit expressions | Excellent for symbolic reasoning, but not designed for runtime validation, conversion, or serialization. |
96
100
  | **Unum** | Unit-aware arithmetic and unit propagation | Tracks units through arithmetic but lacks explicit dimensional algebra, conversion taxonomy, or runtime introspection. |
97
101
 
98
102
  Together, these tools can _use_ units, but none can explicitly represent and verify the relationships between units and dimensions.
99
103
 
100
- Thats the gap `ucon` fills.
104
+ That's the gap `ucon` fills.
101
105
 
102
106
  It treats units, dimensions, and scales as first-class objects and builds a composable algebra around them.
103
107
  This allows you to:
104
108
  - Represent dimensional meaning explicitly (`Dimension`, `Vector`);
105
109
  - Compose and compute with type-safe, introspectable quantities (`Unit`, `Number`);
106
- - Perform reversible, declarative conversions (standard, linear, affine, nonlinear);
107
- - Serialize and validate measurements with Pydantic integration;
108
110
  - Extend the system with custom unit registries and conversion families.
109
111
 
110
112
  Where Pint, Unum, and SymPy focus on _how_ to compute with units,
111
- `ucon` focuses on why those computations make sense. Every operation checks the dimensional structure, _not just the unit labels_. This means ucon doesnt just track names: it enforces physics:
113
+ `ucon` focuses on why those computations make sense. Every operation checks the dimensional structure, _not just the unit labels_. This means ucon doesn't just track names: it enforces physics:
112
114
  ```python
113
115
  from ucon import Number, units
114
116
 
@@ -136,7 +138,8 @@ This sort of dimensional analysis:
136
138
  ```
137
139
  becomes straightforward when you define a measurement:
138
140
  ```python
139
- from ucon import Number, Scale, Units, Ratio
141
+ from ucon import Number, Scale, units
142
+ from ucon.quantity import Ratio
140
143
 
141
144
  # Two milliliters of bromine
142
145
  mL = Scale.milli * units.liter
@@ -149,27 +152,52 @@ bromine_density = Ratio(
149
152
  )
150
153
 
151
154
  # Multiply to find mass
152
- grams_bromine = two_mL_bromine * bromine_density
153
- print(grams_bromine) # <6.238 gram>
155
+ grams_bromine = bromine_density.evaluate() * two_mL_bromine
156
+ print(grams_bromine) # <6.238 g>
154
157
  ```
155
158
 
156
- Scale conversion is automatic and precise:
159
+ Scale prefixes compose naturally:
160
+ ```python
161
+ km = Scale.kilo * units.meter # UnitProduct with kilo-scaled meter
162
+ mg = Scale.milli * units.gram # UnitProduct with milli-scaled gram
163
+
164
+ print(km.shorthand) # 'km'
165
+ print(mg.shorthand) # 'mg'
166
+
167
+ # Scale arithmetic
168
+ print(km.fold_scale()) # 1000.0
169
+ print(mg.fold_scale()) # 0.001
170
+ ```
157
171
 
172
+ Units are callable for ergonomic quantity construction:
158
173
  ```python
159
- grams_bromine.to(Scale.milli) # <6238.0 milligram>
160
- grams_bromine.to(Scale.kibi) # <0.006091796875 kibigram>
174
+ from ucon import units, Scale
175
+
176
+ # Callable syntax: unit(quantity) → Number
177
+ height = units.meter(1.8)
178
+ speed = (units.mile / units.hour)(60)
179
+
180
+ # Convert between units
181
+ height_ft = height.to(units.foot)
182
+ print(height_ft) # <5.905... ft>
183
+
184
+ # Scaled units work too
185
+ km = Scale.kilo * units.meter
186
+ distance = km(5)
187
+ distance_mi = distance.to(units.mile)
188
+ print(distance_mi) # <3.107... mi>
161
189
  ```
162
190
 
163
191
  ---
164
192
 
165
193
  ## Roadmap Highlights
166
194
 
167
- | Version | Theme | Focus |
168
- |----------|-------|--------|
169
- | [**0.3.x**](https://github.com/withtwoemms/ucon/milestone/1) | Primitive Type Refinement | Unified algebraic foundation |
170
- | [**0.4.x**](https://github.com/withtwoemms/ucon/milestone/2) | Conversion System | Linear & affine conversions |
171
- | [**0.6.x**](https://github.com/withtwoemms/ucon/milestone/4) | Nonlinear / Specialized Units | Decibel, Percent, pH |
172
- | [**0.8.x**](https://github.com/withtwoemms/ucon/milestone/6) | Pydantic Integration | Type-safe quantity validation |
195
+ | Version | Theme | Focus | Status |
196
+ |----------|-------|--------|--------|
197
+ | **0.3.5** | Dimensional Algebra | Unit/Scale separation, `UnitFactor`, `UnitProduct` | ✅ Complete |
198
+ | [**0.4.x**](https://github.com/withtwoemms/ucon/milestone/2) | Conversion System | `ConversionGraph`, `Number.to()`, callable units | 🚧 In Progress |
199
+ | [**0.6.x**](https://github.com/withtwoemms/ucon/milestone/4) | Nonlinear / Specialized Units | Decibel, Percent, pH | ⏳ Planned |
200
+ | [**0.8.x**](https://github.com/withtwoemms/ucon/milestone/6) | Pydantic Integration | Type-safe quantity validation | ⏳ Planned |
173
201
 
174
202
  See full roadmap: [ROADMAP.md](./ROADMAP.md)
175
203
 
@@ -182,13 +210,13 @@ Ensure `nox` is installed.
182
210
  ```
183
211
  pip install -r requirements.txt
184
212
  ```
185
- Then run the full test suite (agains all supported python versions) before committing:
213
+ Then run the full test suite (against all supported python versions) before committing:
186
214
 
187
215
  ```bash
188
216
  nox -s test
189
217
  ```
190
218
  ---
191
219
 
192
- > If it can be measured, it can be represented.
220
+ > "If it can be measured, it can be represented.
193
221
  If it can be represented, it can be validated.
194
- If it can be validated, it can be trusted.”
222
+ If it can be validated, it can be trusted."
@@ -18,8 +18,8 @@
18
18
  It combines **units**, **scales**, and **dimensions** into a composable algebra that supports:
19
19
 
20
20
  - Dimensional analysis through `Number` and `Ratio`
21
- - Scale-aware arithmetic and conversions
22
- - Metric and binary prefixes (`kilo`, `kibi`, `micro`, `mebi`, ect.)
21
+ - Scale-aware arithmetic via `UnitFactor` and `UnitProduct`
22
+ - Metric and binary prefixes (`kilo`, `kibi`, `micro`, `mebi`, etc.)
23
23
  - A clean foundation for physics, chemistry, data modeling, and beyond
24
24
 
25
25
  Think of it as **`decimal.Decimal` for the physical world** — precise, predictable, and type-safe.
@@ -33,20 +33,24 @@ The crux of this tiny library is to provide abstractions that simplify the answe
33
33
  To best answer this question, we turn to an age-old technique ([dimensional analysis](https://en.wikipedia.org/wiki/Dimensional_analysis)) which essentially allows for the solution to be written as a product of ratios. `ucon` comes equipped with some useful primitives:
34
34
  | Type | Defined In | Purpose | Typical Use Cases |
35
35
  | ----------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
36
- | **`Vector`** | `ucon.dimension` | Represents the exponent tuple of a physical quantitys base dimensions (e.g., T, L, M, I, Θ, J, N). | Internal representation of dimensional algebra; building derived quantities (e.g., area, velocity, force). |
37
- | **`Dimension`** | `ucon.dimension` | Encapsulates physical dimensions (e.g., length, time, mass) as algebraic combinations of vectors. | Enforcing dimensional consistency; defining relationships between quantities (e.g., length / time = velocity). |
38
- | **`Unit`** | `ucon.unit` | Represents a named, dimensioned measurement unit (e.g., meter, second, joule). | Attaching human-readable units to quantities; defining or composing new units (`newton = kilogram * meter / second²`). |
36
+ | **`Vector`** | `ucon.algebra` | Represents the 8-component exponent tuple of a physical quantity's base dimensions (T, L, M, I, Θ, J, N, B). | Internal representation of dimensional algebra; building derived quantities (e.g., area, velocity, force). |
37
+ | **`Exponent`** | `ucon.algebra` | Represents base-power pairs (e.g., 10³, 2¹⁰) used by `Scale`. | Performing arithmetic on powers and bases; normalizing scales across conversions. |
38
+ | **`Dimension`** | `ucon.core` | Encapsulates physical dimensions (e.g., length, time, mass) as algebraic combinations of vectors. | Enforcing dimensional consistency; defining relationships between quantities (e.g., length / time = velocity). |
39
39
  | **`Scale`** | `ucon.core` | Encodes powers of base magnitudes (binary or decimal prefixes like kilo-, milli-, mebi-). | Adjusting numeric scale without changing dimension (e.g., kilometer ↔ meter, byte ↔ kibibyte). |
40
- | **`Exponent`** | `ucon.core` | Represents base-power pairs (e.g., 10³, 2¹⁰) used by `Scale`. | Performing arithmetic on powers and bases; normalizing scales across conversions. |
41
- | **`Number`** | `ucon.core` | Combines a numeric quantity with a unit and scale; the primary measurable type. | Performing arithmetic with units; converting between compatible units; representing physical quantities like 5 m/s. |
40
+ | **`Unit`** | `ucon.core` | An atomic, scale-free measurement symbol (e.g., meter, second, joule) with a `Dimension`. | Defining base units; serving as graph nodes for future conversions. |
41
+ | **`UnitFactor`** | `ucon.core` | Pairs a `Unit` with a `Scale` (e.g., kilo + gram = kg). Used as keys inside `UnitProduct`. | Preserving user-provided scale prefixes through algebraic operations. |
42
+ | **`UnitProduct`** | `ucon.core` | A product/quotient of `UnitFactor`s with exponent tracking and simplification. | Representing composite units like m/s, kg·m/s², kJ·h. |
43
+ | **`Number`** | `ucon.core` | Combines a numeric quantity with a unit; the primary measurable type. | Performing arithmetic with units; representing physical quantities like 5 m/s. |
42
44
  | **`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). |
43
- | **`units` module** | `ucon.units` | Defines canonical unit instances (SI and common derived units). | Quick access to standard physical units (`units.meter`, `units.second`, `units.newton`, etc.). | |
45
+ | **`Map`** hierarchy | `ucon.maps` | Composable conversion morphisms: `LinearMap`, `AffineMap`, `ComposedMap`. | Defining conversion functions between units (e.g., meter→foot, celsius→kelvin). |
46
+ | **`ConversionGraph`** | `ucon.graph` | Registry of unit conversion edges with BFS path composition. | Converting between units via `Number.to(target)`; managing default and custom graphs. |
47
+ | **`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.). |
44
48
 
45
49
  ### Under the Hood
46
50
 
47
51
  `ucon` models unit math through a hierarchy where each layer builds on the last:
48
52
 
49
- <img src=https://gist.githubusercontent.com/withtwoemms/429d2ca1f979865aa80a2658bf9efa32/raw/0c704737a52b9e4a87cda5c839e9aa40f7e5bb48/ucon.data-model_v035.png align="center" alt="ucon Data Model" width=600/>
53
+ <img src=https://gist.githubusercontent.com/withtwoemms/429d2ca1f979865aa80a2658bf9efa32/raw/5df6a7fb2a6426ee6804096c092c10bed1b30b6f/ucon.data-model_v040.png align="center" alt="ucon Data Model" width=600/>
50
54
 
51
55
  ## Why `ucon`?
52
56
 
@@ -54,24 +58,22 @@ Python already has mature libraries for handling units and physical quantities
54
58
 
55
59
  | Library | Focus | Limitation |
56
60
  | --------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
57
- | **Pint** | Runtime unit conversion and compatibility checking | Treats quantities as decorated numbers — conversions work, but the algebra behind them isnt inspectable or type-safe. |
61
+ | **Pint** | Runtime unit conversion and compatibility checking | Treats quantities as decorated numbers — conversions work, but the algebra behind them isn't inspectable or type-safe. |
58
62
  | **SymPy** | Symbolic algebra and simplification of unit expressions | Excellent for symbolic reasoning, but not designed for runtime validation, conversion, or serialization. |
59
63
  | **Unum** | Unit-aware arithmetic and unit propagation | Tracks units through arithmetic but lacks explicit dimensional algebra, conversion taxonomy, or runtime introspection. |
60
64
 
61
65
  Together, these tools can _use_ units, but none can explicitly represent and verify the relationships between units and dimensions.
62
66
 
63
- Thats the gap `ucon` fills.
67
+ That's the gap `ucon` fills.
64
68
 
65
69
  It treats units, dimensions, and scales as first-class objects and builds a composable algebra around them.
66
70
  This allows you to:
67
71
  - Represent dimensional meaning explicitly (`Dimension`, `Vector`);
68
72
  - Compose and compute with type-safe, introspectable quantities (`Unit`, `Number`);
69
- - Perform reversible, declarative conversions (standard, linear, affine, nonlinear);
70
- - Serialize and validate measurements with Pydantic integration;
71
73
  - Extend the system with custom unit registries and conversion families.
72
74
 
73
75
  Where Pint, Unum, and SymPy focus on _how_ to compute with units,
74
- `ucon` focuses on why those computations make sense. Every operation checks the dimensional structure, _not just the unit labels_. This means ucon doesnt just track names: it enforces physics:
76
+ `ucon` focuses on why those computations make sense. Every operation checks the dimensional structure, _not just the unit labels_. This means ucon doesn't just track names: it enforces physics:
75
77
  ```python
76
78
  from ucon import Number, units
77
79
 
@@ -99,7 +101,8 @@ This sort of dimensional analysis:
99
101
  ```
100
102
  becomes straightforward when you define a measurement:
101
103
  ```python
102
- from ucon import Number, Scale, Units, Ratio
104
+ from ucon import Number, Scale, units
105
+ from ucon.quantity import Ratio
103
106
 
104
107
  # Two milliliters of bromine
105
108
  mL = Scale.milli * units.liter
@@ -112,27 +115,52 @@ bromine_density = Ratio(
112
115
  )
113
116
 
114
117
  # Multiply to find mass
115
- grams_bromine = two_mL_bromine * bromine_density
116
- print(grams_bromine) # <6.238 gram>
118
+ grams_bromine = bromine_density.evaluate() * two_mL_bromine
119
+ print(grams_bromine) # <6.238 g>
117
120
  ```
118
121
 
119
- Scale conversion is automatic and precise:
122
+ Scale prefixes compose naturally:
123
+ ```python
124
+ km = Scale.kilo * units.meter # UnitProduct with kilo-scaled meter
125
+ mg = Scale.milli * units.gram # UnitProduct with milli-scaled gram
126
+
127
+ print(km.shorthand) # 'km'
128
+ print(mg.shorthand) # 'mg'
129
+
130
+ # Scale arithmetic
131
+ print(km.fold_scale()) # 1000.0
132
+ print(mg.fold_scale()) # 0.001
133
+ ```
120
134
 
135
+ Units are callable for ergonomic quantity construction:
121
136
  ```python
122
- grams_bromine.to(Scale.milli) # <6238.0 milligram>
123
- grams_bromine.to(Scale.kibi) # <0.006091796875 kibigram>
137
+ from ucon import units, Scale
138
+
139
+ # Callable syntax: unit(quantity) → Number
140
+ height = units.meter(1.8)
141
+ speed = (units.mile / units.hour)(60)
142
+
143
+ # Convert between units
144
+ height_ft = height.to(units.foot)
145
+ print(height_ft) # <5.905... ft>
146
+
147
+ # Scaled units work too
148
+ km = Scale.kilo * units.meter
149
+ distance = km(5)
150
+ distance_mi = distance.to(units.mile)
151
+ print(distance_mi) # <3.107... mi>
124
152
  ```
125
153
 
126
154
  ---
127
155
 
128
156
  ## Roadmap Highlights
129
157
 
130
- | Version | Theme | Focus |
131
- |----------|-------|--------|
132
- | [**0.3.x**](https://github.com/withtwoemms/ucon/milestone/1) | Primitive Type Refinement | Unified algebraic foundation |
133
- | [**0.4.x**](https://github.com/withtwoemms/ucon/milestone/2) | Conversion System | Linear & affine conversions |
134
- | [**0.6.x**](https://github.com/withtwoemms/ucon/milestone/4) | Nonlinear / Specialized Units | Decibel, Percent, pH |
135
- | [**0.8.x**](https://github.com/withtwoemms/ucon/milestone/6) | Pydantic Integration | Type-safe quantity validation |
158
+ | Version | Theme | Focus | Status |
159
+ |----------|-------|--------|--------|
160
+ | **0.3.5** | Dimensional Algebra | Unit/Scale separation, `UnitFactor`, `UnitProduct` | ✅ Complete |
161
+ | [**0.4.x**](https://github.com/withtwoemms/ucon/milestone/2) | Conversion System | `ConversionGraph`, `Number.to()`, callable units | 🚧 In Progress |
162
+ | [**0.6.x**](https://github.com/withtwoemms/ucon/milestone/4) | Nonlinear / Specialized Units | Decibel, Percent, pH | ⏳ Planned |
163
+ | [**0.8.x**](https://github.com/withtwoemms/ucon/milestone/6) | Pydantic Integration | Type-safe quantity validation | ⏳ Planned |
136
164
 
137
165
  See full roadmap: [ROADMAP.md](./ROADMAP.md)
138
166
 
@@ -145,13 +173,13 @@ Ensure `nox` is installed.
145
173
  ```
146
174
  pip install -r requirements.txt
147
175
  ```
148
- Then run the full test suite (agains all supported python versions) before committing:
176
+ Then run the full test suite (against all supported python versions) before committing:
149
177
 
150
178
  ```bash
151
179
  nox -s test
152
180
  ```
153
181
  ---
154
182
 
155
- > If it can be measured, it can be represented.
183
+ > "If it can be measured, it can be represented.
156
184
  If it can be represented, it can be validated.
157
- If it can be validated, it can be trusted.”
185
+ If it can be validated, it can be trusted."
ucon-0.4.0/ROADMAP.md ADDED
@@ -0,0 +1,244 @@
1
+ # 🧭 ucon Roadmap
2
+
3
+ > *A clear path from algebraic foundation to a stable 1.0 release.*
4
+
5
+ ---
6
+
7
+ ## 🪜 Current Version: **v0.4.0** (in progress)
8
+
9
+ Building on v0.3.5 baseline:
10
+ - `ucon.core` (`Dimension`, `Scale`, `Unit`, `UnitFactor`, `UnitProduct`, `Number`, `Ratio`)
11
+ - `ucon.maps` (`Map`, `LinearMap`, `AffineMap`, `ComposedMap`)
12
+ - `ucon.graph` (`ConversionGraph`, default graph, `get_default_graph()`, `using_graph()`)
13
+ - `ucon.units` (SI + imperial + information units, callable syntax)
14
+ - Callable unit API: `meter(5)`, `(mile / hour)(60)`
15
+ - `Number.simplify()` for base-scale normalization
16
+ - `Dimension.information` with `units.bit`, `units.byte`
17
+
18
+ ---
19
+
20
+ ## ✅ v0.3.x — Dimensional Algebra (Complete)
21
+
22
+ ### 🔹 Summary
23
+ > Introduces dimensional algebra and establishes the Unit/Scale separation
24
+ > that underpins all downstream work.
25
+
26
+ ### ✅ Goals
27
+ - [x] Implement `Vector` and `Dimension` classes
28
+ - [x] Integrate dimensions into `Unit`
29
+ - [x] Refactor `ucon.units` to use dimensional definitions
30
+ - [x] Publish documentation for dimensional operations
31
+ - [x] Verify uniqueness and hashing correctness across all Dimensions
32
+ - [x] Redesign `Exponent` to support algebraic operations (`__mul__`, `__truediv__`, `to_base`, etc.)
33
+ - [x] Remove redundant evaluated caching in favor of property-based computation
34
+ - [x] Integrate `Scale` with Exponent for consistent prefix arithmetic
35
+ - [x] Add regression tests for prefix math (`kilo / milli → mega`, `2¹⁰ / 10³ → 1.024×`)
36
+ - [x] Separate `scale` from `Unit`; delegate to `UnitFactor(unit, scale)`
37
+ - [x] Introduce `UnitProduct` with `fold_scale()` and `_residual_scale_factor`
38
+ - [x] `Number.value` returns as-expressed magnitude; `_canonical_magnitude` folds scale internally
39
+ - [x] Remove dead code and unify naming (`UnitFactor`, `UnitProduct`) across all docstrings and repr
40
+
41
+ ### 🧩 Outcomes
42
+ - All units acquire explicit dimensional semantics
43
+ - Enables composable and type-safe dimensional operations
44
+ - Establishes the mathematical foundation for future conversions
45
+ - Unified algebraic foundation for all scaling and magnitude operations
46
+ - Clean Unit/Scale separation: `Unit` is an atomic symbol, `UnitFactor` pairs it with a `Scale`
47
+ - `UnitProduct` correctly tracks residual scale from cancelled units
48
+ - Type system is ready for a `ConversionGraph` to be built on top
49
+
50
+ ---
51
+
52
+ ## ⚙️ v0.4.x — Conversion System Foundations (In Progress)
53
+
54
+ ### 🔹 Summary
55
+ > Implements unified conversion engine for standard, linear, and affine conversions.
56
+ > Introduces callable unit API for ergonomic quantity construction.
57
+
58
+ ### ✅ Goals
59
+ - [x] Introduce `ConversionGraph` registry keyed by `Dimension`
60
+ - [x] Add support for `standard`, `linear`, and `affine` conversion types
61
+ - [x] Implement `Number.to(target_unit)` conversion API
62
+ - [x] Scale-only conversions short-circuit without graph lookup
63
+ - [x] Composite-to-composite conversion via per-component decomposition
64
+ - [x] Round-trip validation for reversible conversions (inverse maps)
65
+ - [x] Callable unit syntax: `meter(5)`, `(mile / hour)(60)`
66
+ - [x] Default graph with common SI and imperial conversions
67
+ - [x] Imperial units: `foot`, `mile`, `yard`, `inch`, `pound`, `ounce`, `fahrenheit`, `gallon`
68
+ - [x] `Number.simplify()` — Express in base scale
69
+ - [x] `Dimension.information` with `units.bit` and `units.byte`
70
+ - [x] `Vector` extended to 8 components (added B for information)
71
+ - [x] Information unit conversions in default graph (byte ↔ bit)
72
+ - [ ] Extend tests to include temperature, pressure, and base SI conversions
73
+ - [ ] Document Exponent/Scale relationship in developer guide
74
+
75
+ ### 🧩 Outcomes
76
+ - Unified conversion taxonomy
77
+ - Reversible, dimension-checked conversions
78
+ - Scale-aware graph that leverages the `Unit`/`UnitFactor` separation from v0.3.x
79
+ - Ergonomic API: units are callable, returning `Number` instances
80
+ - Information dimension support (bit, byte) with binary prefix compatibility
81
+ - `Number.simplify()` for expressing quantities in base scale
82
+ - Forms the basis for nonlinear and domain-specific conversion families
83
+
84
+ ---
85
+
86
+ ## 🧱 v0.5.x — Unit Systems & Registries
87
+
88
+ ### 🔹 Summary
89
+ > Introduces an extensible registry system for custom units and aliases.
90
+
91
+ ### ✅ Goals
92
+ - [x] Implement `have(name)` membership check
93
+ - [ ] Add `UnitSystem` abstraction
94
+ - [ ] Support `registry.add(unit)` and dynamic system registration
95
+ - [ ] Validate alias uniqueness and collision prevention
96
+ - [ ] Include examples for user-defined unit extensions
97
+
98
+ ### 🧩 Outcomes
99
+ - Registry-based extensibility for domain-specific systems
100
+ - Dynamic unit registration and discovery
101
+ - Groundwork for plugin-style system extensions
102
+
103
+ ---
104
+
105
+ ## 🧪 v0.6.x — Nonlinear & Specialized Conversions
106
+
107
+ ### 🔹 Summary
108
+ > Adds support for logarithmic, fractional, and other specialized dimensionless conversions.
109
+
110
+ ### ✅ Goals
111
+ - [ ] Extend conversion registry schema with `"nonlinear"` family
112
+ - [ ] Add `to_base` / `from_base` lambdas for function-based mappings
113
+ - [ ] Define sample nonlinear conversions (`decibel`, `bel`, `pH`)
114
+ - [ ] Add tolerance-aware tests for nonlinear conversions
115
+ - [ ] Introduce structured dimensionless unit family (`radian`, `percent`, `ppm`, `count`, etc.)
116
+ - [ ] Define canonical dimensionless subtypes for angular, fractional, and count semantics
117
+ - [ ] Ensure automatic collapse of equivalent units (`m/m → none`, `J/J → none`) via Ratio
118
+
119
+ ### 🧩 Outcomes
120
+ - Support for function-based (nonlinear) physical conversions
121
+ - Unified algebraic framework across all conversion types
122
+ - Rich, semantically meaningful representation of dimensionless quantities
123
+ - Enables acoustics (dB), geometry (rad, sr), statistics (probability), and fractional scales (%, ppm)
124
+
125
+ ---
126
+
127
+ ## 🧰 v0.7.x — Testing, Developer Experience, & API Polish
128
+
129
+ ### 🔹 Summary
130
+ > Strengthens tests, developer ergonomics, and runtime feedback.
131
+
132
+ ### ✅ Goals
133
+ - [ ] Reach 95%+ test coverage
134
+ - [ ] Add property-based tests for dimensional invariants
135
+ - [ ] Improve error reporting, `__repr__`, and exception messaging
136
+ - [ ] Validate public API imports and maintain consistent naming
137
+ - [ ] Add CI coverage reports and build badges
138
+
139
+ ### 🧩 Outcomes
140
+ - Reliable, developer-friendly foundation
141
+ - Consistent runtime behavior and output clarity
142
+ - Prepares API for public documentation and 1.0 freeze
143
+
144
+ ---
145
+
146
+ ## 🧩 v0.8.x — Pydantic Integration
147
+
148
+ ### 🔹 Summary
149
+ > Introduces seamless integration with **Pydantic v2**, enabling validation, serialization, and typed dimensional models.
150
+
151
+ ### ✅ Goals
152
+ - [ ] Define Pydantic-compatible field types (`UnitType`, `NumberType`)
153
+ - [ ] Implement `__get_pydantic_core_schema__` for Units and Numbers
154
+ - [ ] Support automatic conversion/validation for user-defined models
155
+ - [ ] Add YAML / JSON encoding for quantities (`Number(unit="meter", quantity=5)`)
156
+ - [ ] Add Pydantic-based examples (API config, simulation parameters)
157
+
158
+ ### 🧩 Outcomes
159
+ - Native validation and serialization for dimensioned quantities
160
+ - Enables safe configuration in data models and APIs
161
+ - Bridges `ucon`'s algebraic model with modern Python typing ecosystems
162
+
163
+ ---
164
+
165
+ ## 📘 v0.9.x — Documentation & RC Phase
166
+
167
+ ### 🔹 Summary
168
+ > Completes documentation, finalizes examples, and preps release candidates.
169
+
170
+ ### ✅ Goals
171
+ - [ ] Write comprehensive README and developer guide
172
+ - [ ] Publish API reference docs (Sphinx / MkDocs)
173
+ - [ ] Add SymPy / Pint comparison appendix
174
+ - [ ] Freeze and document all public APIs
175
+ - [ ] Publish one or more release candidates (RC1, RC2)
176
+
177
+ ### 🧩 Outcomes
178
+ - Complete public-facing documentation
179
+ - API frozen and versioned for stability
180
+ - Ready for final testing and validation before 1.0
181
+
182
+ ---
183
+
184
+ ## 🏁 v1.0.0 — Stable, Introspective Core
185
+
186
+ ### 🔹 Summary
187
+ > First major release: a unified algebra for composable, type-safe, and semantically clear unit conversion.
188
+
189
+ ### ✅ Goals
190
+ - [ ] Tag and release to PyPI
191
+ - [ ] Validate packaging and dependency metadata
192
+ - [ ] Include examples and tutorials in docs
193
+ - [ ] Announce 1.0 on GitHub and PyPI
194
+
195
+ ### 🧩 Outcomes
196
+ - Stable, well-tested release
197
+ - Fully type-safe and validated core
198
+ - Production-ready for integration into scientific and engineering workflows
199
+
200
+ ---
201
+
202
+ ## 🧠 Post-1.0 Vision
203
+
204
+ | Future Direction | Description |
205
+ |------------------|-------------|
206
+ | **Graph-based conversion paths** | Automatically discover multi-hop conversions between compatible units |
207
+ | **Type-safe generics** | `Number[Dimension.length]` support for type checking and IDE hints |
208
+ | **Symbolic bridge to SymPy** | Export units and expressions for symbolic manipulation |
209
+ | **Visualization** | Dimensional relationship graphs and dependency trees |
210
+ | **Plugin architecture** | Load conversions and systems dynamically (YAML/JSON plugins) |
211
+
212
+ ---
213
+
214
+ ## 🗓️ Milestone Summary
215
+
216
+ | Version | Theme | Key Focus | Status |
217
+ |----------|--------|------------|---------|
218
+ | **0.3.5** | Dimensional Algebra | Unit/Scale separation, `UnitFactor`, `UnitProduct` | ✅ Complete |
219
+ | **0.4.0** | Conversion Engine | `ConversionGraph`, `Number.to()`, callable units | 🚧 In Progress |
220
+ | **0.5.0** | Unit Systems & Registries | Extensible registry system | ⏳ Planned |
221
+ | **0.6.0** | Nonlinear Conversions | Logarithmic / exponential families | ⏳ Planned |
222
+ | **0.7.0** | Testing & API Polish | Coverage, ergonomics, stability | ⏳ Planned |
223
+ | **0.8.0** | Pydantic Integration | Typed validation, serialization | ⏳ Planned |
224
+ | **0.9.x** | Documentation & RC | Freeze API, publish docs, RCs | ⏳ Planned |
225
+ | **1.0.0** | Stable Release | Publish production-ready core | 🔮 Future |
226
+
227
+ ---
228
+
229
+ ### ✨ Guiding Principle
230
+
231
+ > "If it can be measured, it can be represented.
232
+ > If it can be represented, it can be validated.
233
+ > If it can be validated, it can be trusted."
234
+
235
+ ---
236
+
237
+ ### 💡 Why Pydantic Integration Matters
238
+
239
+ - Enables **runtime validation** of dimensional correctness:
240
+ ```python
241
+ class Config(BaseModel):
242
+ length: NumberType[Dimension.length]
243
+ time: NumberType[Dimension.time]
244
+ ```
File without changes