ucon 0.3.5__tar.gz → 0.3.5rc1__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.
- {ucon-0.3.5 → ucon-0.3.5rc1}/PKG-INFO +32 -42
- {ucon-0.3.5 → ucon-0.3.5rc1}/README.md +31 -41
- {ucon-0.3.5 → ucon-0.3.5rc1}/ROADMAP.md +89 -97
- {ucon-0.3.5 → ucon-0.3.5rc1}/ucon/__init__.py +1 -3
- {ucon-0.3.5 → ucon-0.3.5rc1}/ucon/core.py +15 -22
- {ucon-0.3.5 → ucon-0.3.5rc1}/ucon/quantity.py +1 -1
- {ucon-0.3.5 → ucon-0.3.5rc1}/ucon.egg-info/PKG-INFO +32 -42
- {ucon-0.3.5 → ucon-0.3.5rc1}/.github/workflows/publish.yaml +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/.github/workflows/tests.yaml +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/.gitignore +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/LICENSE +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/NOTICE +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/docs/decisions/composable-unit-algebra.md +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/docs/decisions/composite-units.md +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/docs/decisions/unit-algebra-naming.md +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/docs/decisions/unity-distance-metric-for-nearest-scale.md +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/docs/explainers/type-operation-matrix.md +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/docs/explainers/why-algebraic-closure-matters.md +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/docs/explainers/why-type-safety-matters.md +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/docs/proposals/interface-unifying-the-value-layer.md +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/docs/proposals/project_unified-algebraic-core.md +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/docs/proposals/support-for-fractional-exponents.md +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/docs/proposals/unified-unit-presentation.md +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/noxfile.py +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/requirements.txt +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/setup.cfg +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/setup.py +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/tests/__init__.py +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/tests/ucon/__init__.py +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/tests/ucon/test_algebra.py +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/tests/ucon/test_core.py +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/tests/ucon/test_quantity.py +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/tests/ucon/test_units.py +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/ucon/algebra.py +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/ucon/units.py +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/ucon.egg-info/SOURCES.txt +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/ucon.egg-info/dependency_links.txt +0 -0
- {ucon-0.3.5 → ucon-0.3.5rc1}/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.
|
|
3
|
+
Version: 0.3.5rc1
|
|
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
|
|
59
|
-
- Metric and binary prefixes (`kilo`, `kibi`, `micro`, `mebi`,
|
|
58
|
+
- Scale-aware arithmetic and conversions
|
|
59
|
+
- Metric and binary prefixes (`kilo`, `kibi`, `micro`, `mebi`, ect.)
|
|
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,22 +70,20 @@ 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.
|
|
74
|
-
| **`
|
|
75
|
-
| **`
|
|
73
|
+
| **`Vector`** | `ucon.dimension` | Represents the exponent tuple of a physical quantity’s 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²`). |
|
|
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
|
-
| **`
|
|
78
|
-
| **`
|
|
79
|
-
| **`
|
|
80
|
-
| **`
|
|
81
|
-
| **`Ratio`** | `ucon.quantity` | Represents the division of two `Number` objects; captures relationships between quantities. | Expressing rates, densities, efficiencies (e.g., energy / time = power, length / time = velocity). |
|
|
82
|
-
| **`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.). |
|
|
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. |
|
|
79
|
+
| **`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.). | |
|
|
83
81
|
|
|
84
82
|
### Under the Hood
|
|
85
83
|
|
|
86
84
|
`ucon` models unit math through a hierarchy where each layer builds on the last:
|
|
87
85
|
|
|
88
|
-
<img src=https://gist.githubusercontent.com/withtwoemms/429d2ca1f979865aa80a2658bf9efa32/raw/
|
|
86
|
+
<img src=https://gist.githubusercontent.com/withtwoemms/429d2ca1f979865aa80a2658bf9efa32/raw/0c704737a52b9e4a87cda5c839e9aa40f7e5bb48/ucon.data-model_v035.png align="center" alt="ucon Data Model" width=600/>
|
|
89
87
|
|
|
90
88
|
## Why `ucon`?
|
|
91
89
|
|
|
@@ -93,22 +91,24 @@ Python already has mature libraries for handling units and physical quantities
|
|
|
93
91
|
|
|
94
92
|
| Library | Focus | Limitation |
|
|
95
93
|
| --------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
96
|
-
| **Pint** | Runtime unit conversion and compatibility checking | Treats quantities as decorated numbers — conversions work, but the algebra behind them isn
|
|
94
|
+
| **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. |
|
|
97
95
|
| **SymPy** | Symbolic algebra and simplification of unit expressions | Excellent for symbolic reasoning, but not designed for runtime validation, conversion, or serialization. |
|
|
98
96
|
| **Unum** | Unit-aware arithmetic and unit propagation | Tracks units through arithmetic but lacks explicit dimensional algebra, conversion taxonomy, or runtime introspection. |
|
|
99
97
|
|
|
100
98
|
Together, these tools can _use_ units, but none can explicitly represent and verify the relationships between units and dimensions.
|
|
101
99
|
|
|
102
|
-
That
|
|
100
|
+
That’s the gap `ucon` fills.
|
|
103
101
|
|
|
104
102
|
It treats units, dimensions, and scales as first-class objects and builds a composable algebra around them.
|
|
105
103
|
This allows you to:
|
|
106
104
|
- Represent dimensional meaning explicitly (`Dimension`, `Vector`);
|
|
107
105
|
- 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
108
|
- Extend the system with custom unit registries and conversion families.
|
|
109
109
|
|
|
110
110
|
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 doesn
|
|
111
|
+
`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
112
|
```python
|
|
113
113
|
from ucon import Number, units
|
|
114
114
|
|
|
@@ -136,8 +136,7 @@ This sort of dimensional analysis:
|
|
|
136
136
|
```
|
|
137
137
|
becomes straightforward when you define a measurement:
|
|
138
138
|
```python
|
|
139
|
-
from ucon import Number, Scale,
|
|
140
|
-
from ucon.quantity import Ratio
|
|
139
|
+
from ucon import Number, Scale, Units, Ratio
|
|
141
140
|
|
|
142
141
|
# Two milliliters of bromine
|
|
143
142
|
mL = Scale.milli * units.liter
|
|
@@ -150,36 +149,27 @@ bromine_density = Ratio(
|
|
|
150
149
|
)
|
|
151
150
|
|
|
152
151
|
# Multiply to find mass
|
|
153
|
-
grams_bromine =
|
|
154
|
-
print(grams_bromine) # <6.238
|
|
152
|
+
grams_bromine = two_mL_bromine * bromine_density
|
|
153
|
+
print(grams_bromine) # <6.238 gram>
|
|
155
154
|
```
|
|
156
155
|
|
|
157
|
-
Scale
|
|
158
|
-
```python
|
|
159
|
-
km = Scale.kilo * units.meter # UnitProduct with kilo-scaled meter
|
|
160
|
-
mg = Scale.milli * units.gram # UnitProduct with milli-scaled gram
|
|
161
|
-
|
|
162
|
-
print(km.shorthand) # 'km'
|
|
163
|
-
print(mg.shorthand) # 'mg'
|
|
156
|
+
Scale conversion is automatic and precise:
|
|
164
157
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
158
|
+
```python
|
|
159
|
+
grams_bromine.to(Scale.milli) # <6238.0 milligram>
|
|
160
|
+
grams_bromine.to(Scale.kibi) # <0.006091796875 kibigram>
|
|
168
161
|
```
|
|
169
162
|
|
|
170
|
-
> **Note:** Unit _conversions_ (e.g., `number.to(units.inch)`) are planned for v0.4.x
|
|
171
|
-
> via the `ConversionGraph` abstraction. See [ROADMAP.md](./ROADMAP.md).
|
|
172
|
-
|
|
173
163
|
---
|
|
174
164
|
|
|
175
165
|
## Roadmap Highlights
|
|
176
166
|
|
|
177
|
-
| Version | Theme | Focus |
|
|
178
|
-
|
|
179
|
-
| **0.3.
|
|
180
|
-
| [**0.4.x**](https://github.com/withtwoemms/ucon/milestone/2) | Conversion System |
|
|
181
|
-
| [**0.6.x**](https://github.com/withtwoemms/ucon/milestone/4) | Nonlinear / Specialized Units | Decibel, Percent, pH |
|
|
182
|
-
| [**0.8.x**](https://github.com/withtwoemms/ucon/milestone/6) | Pydantic Integration | Type-safe quantity validation |
|
|
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 |
|
|
183
173
|
|
|
184
174
|
See full roadmap: [ROADMAP.md](./ROADMAP.md)
|
|
185
175
|
|
|
@@ -192,13 +182,13 @@ Ensure `nox` is installed.
|
|
|
192
182
|
```
|
|
193
183
|
pip install -r requirements.txt
|
|
194
184
|
```
|
|
195
|
-
Then run the full test suite (
|
|
185
|
+
Then run the full test suite (agains all supported python versions) before committing:
|
|
196
186
|
|
|
197
187
|
```bash
|
|
198
188
|
nox -s test
|
|
199
189
|
```
|
|
200
190
|
---
|
|
201
191
|
|
|
202
|
-
>
|
|
192
|
+
> “If it can be measured, it can be represented.
|
|
203
193
|
If it can be represented, it can be validated.
|
|
204
|
-
If it can be validated, it can be trusted
|
|
194
|
+
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
|
|
22
|
-
- Metric and binary prefixes (`kilo`, `kibi`, `micro`, `mebi`,
|
|
21
|
+
- Scale-aware arithmetic and conversions
|
|
22
|
+
- Metric and binary prefixes (`kilo`, `kibi`, `micro`, `mebi`, ect.)
|
|
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,22 +33,20 @@ 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.
|
|
37
|
-
| **`
|
|
38
|
-
| **`
|
|
36
|
+
| **`Vector`** | `ucon.dimension` | Represents the exponent tuple of a physical quantity’s 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²`). |
|
|
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
|
-
| **`
|
|
41
|
-
| **`
|
|
42
|
-
| **`
|
|
43
|
-
| **`
|
|
44
|
-
| **`Ratio`** | `ucon.quantity` | Represents the division of two `Number` objects; captures relationships between quantities. | Expressing rates, densities, efficiencies (e.g., energy / time = power, length / time = velocity). |
|
|
45
|
-
| **`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.). |
|
|
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. |
|
|
42
|
+
| **`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.). | |
|
|
46
44
|
|
|
47
45
|
### Under the Hood
|
|
48
46
|
|
|
49
47
|
`ucon` models unit math through a hierarchy where each layer builds on the last:
|
|
50
48
|
|
|
51
|
-
<img src=https://gist.githubusercontent.com/withtwoemms/429d2ca1f979865aa80a2658bf9efa32/raw/
|
|
49
|
+
<img src=https://gist.githubusercontent.com/withtwoemms/429d2ca1f979865aa80a2658bf9efa32/raw/0c704737a52b9e4a87cda5c839e9aa40f7e5bb48/ucon.data-model_v035.png align="center" alt="ucon Data Model" width=600/>
|
|
52
50
|
|
|
53
51
|
## Why `ucon`?
|
|
54
52
|
|
|
@@ -56,22 +54,24 @@ Python already has mature libraries for handling units and physical quantities
|
|
|
56
54
|
|
|
57
55
|
| Library | Focus | Limitation |
|
|
58
56
|
| --------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
59
|
-
| **Pint** | Runtime unit conversion and compatibility checking | Treats quantities as decorated numbers — conversions work, but the algebra behind them isn
|
|
57
|
+
| **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. |
|
|
60
58
|
| **SymPy** | Symbolic algebra and simplification of unit expressions | Excellent for symbolic reasoning, but not designed for runtime validation, conversion, or serialization. |
|
|
61
59
|
| **Unum** | Unit-aware arithmetic and unit propagation | Tracks units through arithmetic but lacks explicit dimensional algebra, conversion taxonomy, or runtime introspection. |
|
|
62
60
|
|
|
63
61
|
Together, these tools can _use_ units, but none can explicitly represent and verify the relationships between units and dimensions.
|
|
64
62
|
|
|
65
|
-
That
|
|
63
|
+
That’s the gap `ucon` fills.
|
|
66
64
|
|
|
67
65
|
It treats units, dimensions, and scales as first-class objects and builds a composable algebra around them.
|
|
68
66
|
This allows you to:
|
|
69
67
|
- Represent dimensional meaning explicitly (`Dimension`, `Vector`);
|
|
70
68
|
- 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
71
|
- Extend the system with custom unit registries and conversion families.
|
|
72
72
|
|
|
73
73
|
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 doesn
|
|
74
|
+
`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
75
|
```python
|
|
76
76
|
from ucon import Number, units
|
|
77
77
|
|
|
@@ -99,8 +99,7 @@ This sort of dimensional analysis:
|
|
|
99
99
|
```
|
|
100
100
|
becomes straightforward when you define a measurement:
|
|
101
101
|
```python
|
|
102
|
-
from ucon import Number, Scale,
|
|
103
|
-
from ucon.quantity import Ratio
|
|
102
|
+
from ucon import Number, Scale, Units, Ratio
|
|
104
103
|
|
|
105
104
|
# Two milliliters of bromine
|
|
106
105
|
mL = Scale.milli * units.liter
|
|
@@ -113,36 +112,27 @@ bromine_density = Ratio(
|
|
|
113
112
|
)
|
|
114
113
|
|
|
115
114
|
# Multiply to find mass
|
|
116
|
-
grams_bromine =
|
|
117
|
-
print(grams_bromine) # <6.238
|
|
115
|
+
grams_bromine = two_mL_bromine * bromine_density
|
|
116
|
+
print(grams_bromine) # <6.238 gram>
|
|
118
117
|
```
|
|
119
118
|
|
|
120
|
-
Scale
|
|
121
|
-
```python
|
|
122
|
-
km = Scale.kilo * units.meter # UnitProduct with kilo-scaled meter
|
|
123
|
-
mg = Scale.milli * units.gram # UnitProduct with milli-scaled gram
|
|
124
|
-
|
|
125
|
-
print(km.shorthand) # 'km'
|
|
126
|
-
print(mg.shorthand) # 'mg'
|
|
119
|
+
Scale conversion is automatic and precise:
|
|
127
120
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
121
|
+
```python
|
|
122
|
+
grams_bromine.to(Scale.milli) # <6238.0 milligram>
|
|
123
|
+
grams_bromine.to(Scale.kibi) # <0.006091796875 kibigram>
|
|
131
124
|
```
|
|
132
125
|
|
|
133
|
-
> **Note:** Unit _conversions_ (e.g., `number.to(units.inch)`) are planned for v0.4.x
|
|
134
|
-
> via the `ConversionGraph` abstraction. See [ROADMAP.md](./ROADMAP.md).
|
|
135
|
-
|
|
136
126
|
---
|
|
137
127
|
|
|
138
128
|
## Roadmap Highlights
|
|
139
129
|
|
|
140
|
-
| Version | Theme | Focus |
|
|
141
|
-
|
|
142
|
-
| **0.3.
|
|
143
|
-
| [**0.4.x**](https://github.com/withtwoemms/ucon/milestone/2) | Conversion System |
|
|
144
|
-
| [**0.6.x**](https://github.com/withtwoemms/ucon/milestone/4) | Nonlinear / Specialized Units | Decibel, Percent, pH |
|
|
145
|
-
| [**0.8.x**](https://github.com/withtwoemms/ucon/milestone/6) | Pydantic Integration | Type-safe quantity validation |
|
|
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 |
|
|
146
136
|
|
|
147
137
|
See full roadmap: [ROADMAP.md](./ROADMAP.md)
|
|
148
138
|
|
|
@@ -155,13 +145,13 @@ Ensure `nox` is installed.
|
|
|
155
145
|
```
|
|
156
146
|
pip install -r requirements.txt
|
|
157
147
|
```
|
|
158
|
-
Then run the full test suite (
|
|
148
|
+
Then run the full test suite (agains all supported python versions) before committing:
|
|
159
149
|
|
|
160
150
|
```bash
|
|
161
151
|
nox -s test
|
|
162
152
|
```
|
|
163
153
|
---
|
|
164
154
|
|
|
165
|
-
>
|
|
155
|
+
> “If it can be measured, it can be represented.
|
|
166
156
|
If it can be represented, it can be validated.
|
|
167
|
-
If it can be validated, it can be trusted
|
|
157
|
+
If it can be validated, it can be trusted.”
|
|
@@ -4,68 +4,61 @@
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
## 🪜 Current Version: **v0.3.
|
|
7
|
+
## 🪜 Current Version: **v0.3.0**
|
|
8
8
|
|
|
9
9
|
Stable baseline for:
|
|
10
|
-
- `ucon.core` (`
|
|
11
|
-
- `ucon.
|
|
10
|
+
- `ucon.core` (`Number`, `Scale`, `Ratio`)
|
|
11
|
+
- `ucon.unit` (basic unit representation and composition)
|
|
12
12
|
- `ucon.units` (canonical SI definitions)
|
|
13
13
|
- Initial CI, testing, and packaging
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
-
##
|
|
17
|
+
## 🚀 v0.3.x — Dimensional Algebra (In Progress)
|
|
18
18
|
|
|
19
19
|
### 🔹 Summary
|
|
20
|
-
> Introduces
|
|
21
|
-
> that underpins all downstream work.
|
|
20
|
+
> Introduces `ucon.dimension` as the foundation for algebraic reasoning.
|
|
22
21
|
|
|
23
22
|
### ✅ Goals
|
|
24
|
-
- [x] Implement `Vector` and `Dimension` classes
|
|
25
|
-
- [x] Integrate dimensions into `Unit`
|
|
26
|
-
- [x] Refactor `ucon.units` to use dimensional definitions
|
|
23
|
+
- [x] Implement `Vector` and `Dimension` classes
|
|
24
|
+
- [x] Integrate dimensions into `Unit`
|
|
25
|
+
- [x] Refactor `ucon.units` to use dimensional definitions
|
|
27
26
|
- [x] Publish documentation for dimensional operations
|
|
28
|
-
- [x] Verify uniqueness and hashing correctness across all Dimensions
|
|
29
|
-
- [x] Redesign `Exponent` to support algebraic operations (`__mul__`, `__truediv__`, `to_base`, etc.)
|
|
30
|
-
- [x] Remove redundant evaluated caching in favor of property-based computation
|
|
31
|
-
- [x] Integrate `Scale` with Exponent for consistent prefix arithmetic
|
|
27
|
+
- [x] Verify uniqueness and hashing correctness across all Dimensions
|
|
28
|
+
- [x] Redesign `Exponent` to support algebraic operations (`__mul__`, `__truediv__`, `to_base`, etc.)
|
|
29
|
+
- [x] Remove redundant evaluated caching in favor of property-based computation
|
|
30
|
+
- [x] Integrate `Scale` with Exponent for consistent prefix arithmetic
|
|
31
|
+
- [ ] Update `Number` and `Ratio` to use Exponent-driven scaling
|
|
32
32
|
- [x] Add regression tests for prefix math (`kilo / milli → mega`, `2¹⁰ / 10³ → 1.024×`)
|
|
33
|
-
- [
|
|
34
|
-
- [x] Introduce `UnitProduct` with `fold_scale()` and `_residual_scale_factor`
|
|
35
|
-
- [x] `Number.value` returns as-expressed magnitude; `_canonical_magnitude` folds scale internally
|
|
36
|
-
- [x] Remove dead code and unify naming (`UnitFactor`, `UnitProduct`) across all docstrings and repr
|
|
33
|
+
- [ ] Document Exponent/Scale relationship in developer guide
|
|
37
34
|
|
|
38
35
|
### 🧩 Outcomes
|
|
39
|
-
- All units acquire explicit dimensional semantics
|
|
40
|
-
- Enables composable and type-safe dimensional operations
|
|
41
|
-
- Establishes the mathematical foundation for future conversions
|
|
42
|
-
- Unified algebraic foundation for all scaling and magnitude operations
|
|
43
|
-
-
|
|
44
|
-
- `
|
|
45
|
-
-
|
|
36
|
+
- All units acquire explicit dimensional semantics
|
|
37
|
+
- Enables composable and type-safe dimensional operations
|
|
38
|
+
- Establishes the mathematical foundation for future conversions
|
|
39
|
+
- Unified algebraic foundation for all scaling and magnitude operations
|
|
40
|
+
- ~Precise, reversible cross-base math (`2ⁿ ↔ 10ᵐ`)~
|
|
41
|
+
- Simplified, consistent `Scale` and `Number` behavior
|
|
42
|
+
- Ready for integration into the conversion engine (`ucon.conversions`)
|
|
46
43
|
|
|
47
44
|
---
|
|
48
45
|
|
|
49
|
-
## ⚙️ v0.4.x — Conversion System Foundations
|
|
46
|
+
## ⚙️ v0.4.x — Conversion System Foundations
|
|
50
47
|
|
|
51
48
|
### 🔹 Summary
|
|
52
49
|
> Implements unified conversion engine for standard, linear, and affine conversions.
|
|
53
50
|
|
|
54
51
|
### ✅ Goals
|
|
55
|
-
- [ ] Introduce `
|
|
56
|
-
- [ ] Add support for `standard`, `linear`, and `affine` conversion types
|
|
57
|
-
- [ ] Implement
|
|
58
|
-
- [ ]
|
|
59
|
-
- [ ]
|
|
60
|
-
- [ ] Round-trip validation for reversible conversions
|
|
61
|
-
- [ ] Extend tests to include temperature, pressure, and base SI conversions
|
|
62
|
-
- [ ] Document Exponent/Scale relationship in developer guide
|
|
52
|
+
- [ ] Introduce `ucon.conversions` registry keyed by `Dimension`
|
|
53
|
+
- [ ] Add support for `standard`, `linear`, and `affine` conversion types
|
|
54
|
+
- [ ] Implement `.to(target_unit)` for `Number`
|
|
55
|
+
- [ ] Round-trip validation for reversible conversions
|
|
56
|
+
- [ ] Extend tests to include temperature, pressure, and base SI conversions
|
|
63
57
|
|
|
64
58
|
### 🧩 Outcomes
|
|
65
|
-
- Unified conversion taxonomy
|
|
66
|
-
- Reversible, dimension-checked conversions
|
|
67
|
-
-
|
|
68
|
-
- Forms the basis for nonlinear and domain-specific conversion families
|
|
59
|
+
- Unified conversion taxonomy
|
|
60
|
+
- Reversible, dimension-checked conversions
|
|
61
|
+
- Forms the basis for nonlinear and domain-specific conversion families
|
|
69
62
|
|
|
70
63
|
---
|
|
71
64
|
|
|
@@ -75,16 +68,16 @@ Stable baseline for:
|
|
|
75
68
|
> Introduces an extensible registry system for custom units and aliases.
|
|
76
69
|
|
|
77
70
|
### ✅ Goals
|
|
78
|
-
- [x] Implement `have(name)` membership check
|
|
79
|
-
- [ ] Add `UnitSystem` abstraction
|
|
80
|
-
- [ ] Support `registry.add(unit)` and dynamic system registration
|
|
81
|
-
- [ ] Validate alias uniqueness and collision prevention
|
|
82
|
-
- [ ] Include examples for user-defined unit extensions
|
|
71
|
+
- [x] Implement `have(name)` membership check
|
|
72
|
+
- [ ] Add `UnitSystem` abstraction
|
|
73
|
+
- [ ] Support `registry.add(unit)` and dynamic system registration
|
|
74
|
+
- [ ] Validate alias uniqueness and collision prevention
|
|
75
|
+
- [ ] Include examples for user-defined unit extensions
|
|
83
76
|
|
|
84
77
|
### 🧩 Outcomes
|
|
85
|
-
- Registry-based extensibility for domain-specific systems
|
|
86
|
-
- Dynamic unit registration and discovery
|
|
87
|
-
- Groundwork for plugin-style system extensions
|
|
78
|
+
- Registry-based extensibility for domain-specific systems
|
|
79
|
+
- Dynamic unit registration and discovery
|
|
80
|
+
- Groundwork for plugin-style system extensions
|
|
88
81
|
|
|
89
82
|
---
|
|
90
83
|
|
|
@@ -94,20 +87,20 @@ Stable baseline for:
|
|
|
94
87
|
> Adds support for logarithmic, fractional, and other specialized dimensionless conversions.
|
|
95
88
|
|
|
96
89
|
### ✅ Goals
|
|
97
|
-
- [ ] Extend conversion registry schema with `"nonlinear"` family
|
|
98
|
-
- [ ] Add `to_base` / `from_base` lambdas for function-based mappings
|
|
99
|
-
- [ ] Define sample nonlinear conversions (`decibel`, `bel`, `pH`)
|
|
100
|
-
- [ ] Add tolerance-aware tests for nonlinear conversions
|
|
90
|
+
- [ ] Extend conversion registry schema with `"nonlinear"` family
|
|
91
|
+
- [ ] Add `to_base` / `from_base` lambdas for function-based mappings
|
|
92
|
+
- [ ] Define sample nonlinear conversions (`decibel`, `bel`, `pH`)
|
|
93
|
+
- [ ] Add tolerance-aware tests for nonlinear conversions
|
|
101
94
|
- [ ] Introduce structured dimensionless unit family (`radian`, `percent`, `ppm`, `count`, etc.)
|
|
102
95
|
- [ ] Define canonical dimensionless subtypes for angular, fractional, and count semantics
|
|
103
96
|
- [ ] Ensure automatic collapse of equivalent units (`m/m → none`, `J/J → none`) via Ratio
|
|
104
97
|
|
|
105
98
|
### 🧩 Outcomes
|
|
106
|
-
- Support for function-based (nonlinear) physical conversions
|
|
107
|
-
- Unified algebraic framework across all conversion types
|
|
99
|
+
- Support for function-based (nonlinear) physical conversions
|
|
100
|
+
- Unified algebraic framework across all conversion types
|
|
108
101
|
- Rich, semantically meaningful representation of dimensionless quantities
|
|
109
102
|
- Enables acoustics (dB), geometry (rad, sr), statistics (probability), and fractional scales (%, ppm)
|
|
110
|
-
|
|
103
|
+
|
|
111
104
|
---
|
|
112
105
|
|
|
113
106
|
## 🧰 v0.7.x — Testing, Developer Experience, & API Polish
|
|
@@ -116,16 +109,16 @@ Stable baseline for:
|
|
|
116
109
|
> Strengthens tests, developer ergonomics, and runtime feedback.
|
|
117
110
|
|
|
118
111
|
### ✅ Goals
|
|
119
|
-
- [ ] Reach 95%+ test coverage
|
|
120
|
-
- [ ] Add property-based tests for dimensional invariants
|
|
121
|
-
- [ ] Improve error reporting, `__repr__`, and exception messaging
|
|
122
|
-
- [ ] Validate public API imports and maintain consistent naming
|
|
123
|
-
- [ ] Add CI coverage reports and build badges
|
|
112
|
+
- [ ] Reach 95%+ test coverage
|
|
113
|
+
- [ ] Add property-based tests for dimensional invariants
|
|
114
|
+
- [ ] Improve error reporting, `__repr__`, and exception messaging
|
|
115
|
+
- [ ] Validate public API imports and maintain consistent naming
|
|
116
|
+
- [ ] Add CI coverage reports and build badges
|
|
124
117
|
|
|
125
118
|
### 🧩 Outcomes
|
|
126
|
-
- Reliable, developer-friendly foundation
|
|
127
|
-
- Consistent runtime behavior and output clarity
|
|
128
|
-
- Prepares API for public documentation and 1.0 freeze
|
|
119
|
+
- Reliable, developer-friendly foundation
|
|
120
|
+
- Consistent runtime behavior and output clarity
|
|
121
|
+
- Prepares API for public documentation and 1.0 freeze
|
|
129
122
|
|
|
130
123
|
---
|
|
131
124
|
|
|
@@ -135,16 +128,16 @@ Stable baseline for:
|
|
|
135
128
|
> Introduces seamless integration with **Pydantic v2**, enabling validation, serialization, and typed dimensional models.
|
|
136
129
|
|
|
137
130
|
### ✅ Goals
|
|
138
|
-
- [ ] Define Pydantic-compatible field types (`UnitType`, `NumberType`)
|
|
139
|
-
- [ ] Implement `__get_pydantic_core_schema__` for Units and Numbers
|
|
140
|
-
- [ ] Support automatic conversion/validation for user-defined models
|
|
141
|
-
- [ ] Add YAML / JSON encoding for quantities (`Number(unit="meter", quantity=5)`)
|
|
142
|
-
- [ ] Add Pydantic-based examples (API config, simulation parameters)
|
|
131
|
+
- [ ] Define Pydantic-compatible field types (`UnitType`, `NumberType`)
|
|
132
|
+
- [ ] Implement `__get_pydantic_core_schema__` for Units and Numbers
|
|
133
|
+
- [ ] Support automatic conversion/validation for user-defined models
|
|
134
|
+
- [ ] Add YAML / JSON encoding for quantities (`Number(unit="meter", quantity=5)`)
|
|
135
|
+
- [ ] Add Pydantic-based examples (API config, simulation parameters)
|
|
143
136
|
|
|
144
137
|
### 🧩 Outcomes
|
|
145
|
-
- Native validation and serialization for dimensioned quantities
|
|
146
|
-
- Enables safe configuration in data models and APIs
|
|
147
|
-
- Bridges `ucon
|
|
138
|
+
- Native validation and serialization for dimensioned quantities
|
|
139
|
+
- Enables safe configuration in data models and APIs
|
|
140
|
+
- Bridges `ucon`’s algebraic model with modern Python typing ecosystems
|
|
148
141
|
|
|
149
142
|
---
|
|
150
143
|
|
|
@@ -154,16 +147,16 @@ Stable baseline for:
|
|
|
154
147
|
> Completes documentation, finalizes examples, and preps release candidates.
|
|
155
148
|
|
|
156
149
|
### ✅ Goals
|
|
157
|
-
- [ ] Write comprehensive README and developer guide
|
|
158
|
-
- [ ] Publish API reference docs (Sphinx / MkDocs)
|
|
159
|
-
- [ ] Add SymPy / Pint comparison appendix
|
|
160
|
-
- [ ] Freeze and document all public APIs
|
|
161
|
-
- [ ] Publish one or more release candidates (RC1, RC2)
|
|
150
|
+
- [ ] Write comprehensive README and developer guide
|
|
151
|
+
- [ ] Publish API reference docs (Sphinx / MkDocs)
|
|
152
|
+
- [ ] Add SymPy / Pint comparison appendix
|
|
153
|
+
- [ ] Freeze and document all public APIs
|
|
154
|
+
- [ ] Publish one or more release candidates (RC1, RC2)
|
|
162
155
|
|
|
163
156
|
### 🧩 Outcomes
|
|
164
|
-
- Complete public-facing documentation
|
|
165
|
-
- API frozen and versioned for stability
|
|
166
|
-
- Ready for final testing and validation before 1.0
|
|
157
|
+
- Complete public-facing documentation
|
|
158
|
+
- API frozen and versioned for stability
|
|
159
|
+
- Ready for final testing and validation before 1.0
|
|
167
160
|
|
|
168
161
|
---
|
|
169
162
|
|
|
@@ -173,15 +166,15 @@ Stable baseline for:
|
|
|
173
166
|
> First major release: a unified algebra for composable, type-safe, and semantically clear unit conversion.
|
|
174
167
|
|
|
175
168
|
### ✅ Goals
|
|
176
|
-
- [ ] Tag and release to PyPI
|
|
177
|
-
- [ ] Validate packaging and dependency metadata
|
|
178
|
-
- [ ] Include examples and tutorials in docs
|
|
179
|
-
- [ ] Announce 1.0 on GitHub and PyPI
|
|
169
|
+
- [ ] Tag and release to PyPI
|
|
170
|
+
- [ ] Validate packaging and dependency metadata
|
|
171
|
+
- [ ] Include examples and tutorials in docs
|
|
172
|
+
- [ ] Announce 1.0 on GitHub and PyPI
|
|
180
173
|
|
|
181
174
|
### 🧩 Outcomes
|
|
182
|
-
- Stable, well-tested release
|
|
183
|
-
- Fully type-safe and validated core
|
|
184
|
-
- Production-ready for integration into scientific and engineering workflows
|
|
175
|
+
- Stable, well-tested release
|
|
176
|
+
- Fully type-safe and validated core
|
|
177
|
+
- Production-ready for integration into scientific and engineering workflows
|
|
185
178
|
|
|
186
179
|
---
|
|
187
180
|
|
|
@@ -199,24 +192,24 @@ Stable baseline for:
|
|
|
199
192
|
|
|
200
193
|
## 🗓️ Milestone Summary
|
|
201
194
|
|
|
202
|
-
| Version | Theme | Key Focus | Status |
|
|
203
|
-
|
|
204
|
-
| **0.3.
|
|
205
|
-
| **0.4.0** | Conversion Engine |
|
|
206
|
-
| **0.5.0** | Unit Systems & Registries | Extensible registry system | ⏳ Planned |
|
|
207
|
-
| **0.6.0** | Nonlinear Conversions | Logarithmic / exponential families | ⏳ Planned |
|
|
208
|
-
| **0.7.0** | Testing & API Polish | Coverage, ergonomics, stability | ⏳ Planned |
|
|
209
|
-
| **0.8.0** | Pydantic Integration | Typed validation, serialization |
|
|
210
|
-
| **0.9.x** | Documentation & RC | Freeze API, publish docs, RCs | ⏳ Planned |
|
|
211
|
-
| **1.0.0** | Stable Release | Publish production-ready core | 🔮 Future |
|
|
195
|
+
| Version | Theme | Key Focus | Target | Status |
|
|
196
|
+
|----------|--------|------------|---------|---------|
|
|
197
|
+
| **0.3.0** | Dimensional Algebra | Introduce `ucon.dimension` | **Nov 2025** | 🚧 In Progress |
|
|
198
|
+
| **0.4.0** | Conversion Engine | Standard, linear, affine conversions | **Jan 2026** | ⏳ Planned |
|
|
199
|
+
| **0.5.0** | Unit Systems & Registries | Extensible registry system | **Mar 2026** | ⏳ Planned |
|
|
200
|
+
| **0.6.0** | Nonlinear Conversions | Logarithmic / exponential families | **May 2026** | ⏳ Planned |
|
|
201
|
+
| **0.7.0** | Testing & API Polish | Coverage, ergonomics, stability | **Jul 2026** | ⏳ Planned |
|
|
202
|
+
| **0.8.0** | 🧩 **Pydantic Integration** | Typed validation, serialization | **Sep 2026** | 🧭 Newly Added |
|
|
203
|
+
| **0.9.x** | Documentation & RC | Freeze API, publish docs, RCs | **Nov 2026** | ⏳ Planned |
|
|
204
|
+
| **1.0.0** | Stable Release | Publish production-ready core | **Jan 2027** | 🔮 Future |
|
|
212
205
|
|
|
213
206
|
---
|
|
214
207
|
|
|
215
208
|
### ✨ Guiding Principle
|
|
216
209
|
|
|
217
|
-
>
|
|
218
|
-
> If it can be represented, it can be validated.
|
|
219
|
-
> If it can be validated, it can be trusted
|
|
210
|
+
> “If it can be measured, it can be represented.
|
|
211
|
+
> If it can be represented, it can be validated.
|
|
212
|
+
> If it can be validated, it can be trusted.”
|
|
220
213
|
|
|
221
214
|
---
|
|
222
215
|
|
|
@@ -227,4 +220,3 @@ Stable baseline for:
|
|
|
227
220
|
class Config(BaseModel):
|
|
228
221
|
length: NumberType[Dimension.length]
|
|
229
222
|
time: NumberType[Dimension.time]
|
|
230
|
-
```
|
|
@@ -38,7 +38,7 @@ Design Philosophy
|
|
|
38
38
|
"""
|
|
39
39
|
from ucon import units
|
|
40
40
|
from ucon.algebra import Exponent
|
|
41
|
-
from ucon.core import Dimension, Scale, Unit
|
|
41
|
+
from ucon.core import Dimension, Scale, Unit
|
|
42
42
|
from ucon.quantity import Number, Ratio
|
|
43
43
|
|
|
44
44
|
|
|
@@ -49,7 +49,5 @@ __all__ = [
|
|
|
49
49
|
'Ratio',
|
|
50
50
|
'Scale',
|
|
51
51
|
'Unit',
|
|
52
|
-
'UnitFactor',
|
|
53
|
-
'UnitProduct',
|
|
54
52
|
'units',
|
|
55
53
|
]
|
|
@@ -240,7 +240,8 @@ class Scale(Enum):
|
|
|
240
240
|
|
|
241
241
|
def __mul__(self, other):
|
|
242
242
|
# --- Case 1: applying Scale to simple Unit --------------------
|
|
243
|
-
if isinstance(other, Unit):
|
|
243
|
+
if isinstance(other, Unit) and not isinstance(other, UnitProduct):
|
|
244
|
+
# Unit no longer has scale attribute - always safe to apply
|
|
244
245
|
return UnitProduct({UnitFactor(unit=other, scale=self): 1})
|
|
245
246
|
|
|
246
247
|
# --- Case 2: other cases are NOT handled here -----------------
|
|
@@ -342,6 +343,8 @@ class Unit:
|
|
|
342
343
|
Unit * Unit -> UnitProduct
|
|
343
344
|
Unit * UnitProduct -> UnitProduct
|
|
344
345
|
"""
|
|
346
|
+
from ucon.core import UnitProduct # local import to avoid circulars
|
|
347
|
+
|
|
345
348
|
if isinstance(other, UnitProduct):
|
|
346
349
|
# let UnitProduct handle merging
|
|
347
350
|
return other.__rmul__(self)
|
|
@@ -353,13 +356,12 @@ class Unit:
|
|
|
353
356
|
|
|
354
357
|
def __truediv__(self, other):
|
|
355
358
|
"""
|
|
356
|
-
Unit / Unit
|
|
359
|
+
Unit / Unit:
|
|
360
|
+
- If same unit => dimensionless Unit()
|
|
361
|
+
- If denominator is dimensionless => self
|
|
362
|
+
- Else => UnitProduct
|
|
357
363
|
"""
|
|
358
|
-
|
|
359
|
-
combined = {self: 1.0}
|
|
360
|
-
for u, exp in other.factors.items():
|
|
361
|
-
combined[u] = combined.get(u, 0.0) - exp
|
|
362
|
-
return UnitProduct(combined)
|
|
364
|
+
from ucon.core import UnitProduct # local import
|
|
363
365
|
|
|
364
366
|
if not isinstance(other, Unit):
|
|
365
367
|
return NotImplemented
|
|
@@ -383,13 +385,13 @@ class Unit:
|
|
|
383
385
|
"""
|
|
384
386
|
Unit ** n => UnitProduct with that exponent.
|
|
385
387
|
"""
|
|
388
|
+
from ucon.core import UnitProduct # local import
|
|
389
|
+
|
|
386
390
|
return UnitProduct({self: power})
|
|
387
391
|
|
|
388
392
|
# ----------------- equality & hashing -----------------
|
|
389
393
|
|
|
390
394
|
def __eq__(self, other):
|
|
391
|
-
if isinstance(other, UnitProduct):
|
|
392
|
-
return other.__eq__(self)
|
|
393
395
|
if not isinstance(other, Unit):
|
|
394
396
|
return NotImplemented
|
|
395
397
|
return (
|
|
@@ -496,7 +498,7 @@ class UnitFactor:
|
|
|
496
498
|
return NotImplemented
|
|
497
499
|
|
|
498
500
|
|
|
499
|
-
class UnitProduct:
|
|
501
|
+
class UnitProduct(Unit):
|
|
500
502
|
"""
|
|
501
503
|
Represents a product or quotient of Units.
|
|
502
504
|
|
|
@@ -525,7 +527,8 @@ class UnitProduct:
|
|
|
525
527
|
encountered UnitFactor (keeps user-intent scale).
|
|
526
528
|
"""
|
|
527
529
|
|
|
528
|
-
|
|
530
|
+
# UnitProduct always starts dimensionless
|
|
531
|
+
super().__init__(name="", dimension=Dimension.none)
|
|
529
532
|
self.aliases = ()
|
|
530
533
|
|
|
531
534
|
merged: dict[UnitFactor, float] = {}
|
|
@@ -703,16 +706,6 @@ class UnitProduct:
|
|
|
703
706
|
result *= factor.scale.value.evaluated ** power
|
|
704
707
|
return result
|
|
705
708
|
|
|
706
|
-
# ------------- Helpers ---------------------------------------------------
|
|
707
|
-
|
|
708
|
-
def _norm(self, aliases: tuple[str, ...]) -> tuple[str, ...]:
|
|
709
|
-
"""Normalize alias bag: drop empty/whitespace-only aliases."""
|
|
710
|
-
return tuple(a for a in aliases if a.strip())
|
|
711
|
-
|
|
712
|
-
def __pow__(self, power):
|
|
713
|
-
"""UnitProduct ** n => new UnitProduct with scaled exponents."""
|
|
714
|
-
return UnitProduct({u: exp * power for u, exp in self.factors.items()})
|
|
715
|
-
|
|
716
709
|
# ------------- Algebra ---------------------------------------------------
|
|
717
710
|
|
|
718
711
|
def __mul__(self, other):
|
|
@@ -806,7 +799,7 @@ class UnitProduct:
|
|
|
806
799
|
return f"<{self.__class__.__name__} {self.shorthand}>"
|
|
807
800
|
|
|
808
801
|
def __eq__(self, other):
|
|
809
|
-
if isinstance(other, Unit):
|
|
802
|
+
if isinstance(other, Unit) and not isinstance(other, UnitProduct):
|
|
810
803
|
# Only equal to a plain Unit if we have exactly that unit^1
|
|
811
804
|
# Here, the tuple comparison will invoke UnitFactor.__eq__(Unit)
|
|
812
805
|
# on the key when factors are keyed by UnitFactor.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ucon
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.5rc1
|
|
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
|
|
59
|
-
- Metric and binary prefixes (`kilo`, `kibi`, `micro`, `mebi`,
|
|
58
|
+
- Scale-aware arithmetic and conversions
|
|
59
|
+
- Metric and binary prefixes (`kilo`, `kibi`, `micro`, `mebi`, ect.)
|
|
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,22 +70,20 @@ 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.
|
|
74
|
-
| **`
|
|
75
|
-
| **`
|
|
73
|
+
| **`Vector`** | `ucon.dimension` | Represents the exponent tuple of a physical quantity’s 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²`). |
|
|
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
|
-
| **`
|
|
78
|
-
| **`
|
|
79
|
-
| **`
|
|
80
|
-
| **`
|
|
81
|
-
| **`Ratio`** | `ucon.quantity` | Represents the division of two `Number` objects; captures relationships between quantities. | Expressing rates, densities, efficiencies (e.g., energy / time = power, length / time = velocity). |
|
|
82
|
-
| **`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.). |
|
|
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. |
|
|
79
|
+
| **`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.). | |
|
|
83
81
|
|
|
84
82
|
### Under the Hood
|
|
85
83
|
|
|
86
84
|
`ucon` models unit math through a hierarchy where each layer builds on the last:
|
|
87
85
|
|
|
88
|
-
<img src=https://gist.githubusercontent.com/withtwoemms/429d2ca1f979865aa80a2658bf9efa32/raw/
|
|
86
|
+
<img src=https://gist.githubusercontent.com/withtwoemms/429d2ca1f979865aa80a2658bf9efa32/raw/0c704737a52b9e4a87cda5c839e9aa40f7e5bb48/ucon.data-model_v035.png align="center" alt="ucon Data Model" width=600/>
|
|
89
87
|
|
|
90
88
|
## Why `ucon`?
|
|
91
89
|
|
|
@@ -93,22 +91,24 @@ Python already has mature libraries for handling units and physical quantities
|
|
|
93
91
|
|
|
94
92
|
| Library | Focus | Limitation |
|
|
95
93
|
| --------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
96
|
-
| **Pint** | Runtime unit conversion and compatibility checking | Treats quantities as decorated numbers — conversions work, but the algebra behind them isn
|
|
94
|
+
| **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. |
|
|
97
95
|
| **SymPy** | Symbolic algebra and simplification of unit expressions | Excellent for symbolic reasoning, but not designed for runtime validation, conversion, or serialization. |
|
|
98
96
|
| **Unum** | Unit-aware arithmetic and unit propagation | Tracks units through arithmetic but lacks explicit dimensional algebra, conversion taxonomy, or runtime introspection. |
|
|
99
97
|
|
|
100
98
|
Together, these tools can _use_ units, but none can explicitly represent and verify the relationships between units and dimensions.
|
|
101
99
|
|
|
102
|
-
That
|
|
100
|
+
That’s the gap `ucon` fills.
|
|
103
101
|
|
|
104
102
|
It treats units, dimensions, and scales as first-class objects and builds a composable algebra around them.
|
|
105
103
|
This allows you to:
|
|
106
104
|
- Represent dimensional meaning explicitly (`Dimension`, `Vector`);
|
|
107
105
|
- 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
108
|
- Extend the system with custom unit registries and conversion families.
|
|
109
109
|
|
|
110
110
|
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 doesn
|
|
111
|
+
`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
112
|
```python
|
|
113
113
|
from ucon import Number, units
|
|
114
114
|
|
|
@@ -136,8 +136,7 @@ This sort of dimensional analysis:
|
|
|
136
136
|
```
|
|
137
137
|
becomes straightforward when you define a measurement:
|
|
138
138
|
```python
|
|
139
|
-
from ucon import Number, Scale,
|
|
140
|
-
from ucon.quantity import Ratio
|
|
139
|
+
from ucon import Number, Scale, Units, Ratio
|
|
141
140
|
|
|
142
141
|
# Two milliliters of bromine
|
|
143
142
|
mL = Scale.milli * units.liter
|
|
@@ -150,36 +149,27 @@ bromine_density = Ratio(
|
|
|
150
149
|
)
|
|
151
150
|
|
|
152
151
|
# Multiply to find mass
|
|
153
|
-
grams_bromine =
|
|
154
|
-
print(grams_bromine) # <6.238
|
|
152
|
+
grams_bromine = two_mL_bromine * bromine_density
|
|
153
|
+
print(grams_bromine) # <6.238 gram>
|
|
155
154
|
```
|
|
156
155
|
|
|
157
|
-
Scale
|
|
158
|
-
```python
|
|
159
|
-
km = Scale.kilo * units.meter # UnitProduct with kilo-scaled meter
|
|
160
|
-
mg = Scale.milli * units.gram # UnitProduct with milli-scaled gram
|
|
161
|
-
|
|
162
|
-
print(km.shorthand) # 'km'
|
|
163
|
-
print(mg.shorthand) # 'mg'
|
|
156
|
+
Scale conversion is automatic and precise:
|
|
164
157
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
158
|
+
```python
|
|
159
|
+
grams_bromine.to(Scale.milli) # <6238.0 milligram>
|
|
160
|
+
grams_bromine.to(Scale.kibi) # <0.006091796875 kibigram>
|
|
168
161
|
```
|
|
169
162
|
|
|
170
|
-
> **Note:** Unit _conversions_ (e.g., `number.to(units.inch)`) are planned for v0.4.x
|
|
171
|
-
> via the `ConversionGraph` abstraction. See [ROADMAP.md](./ROADMAP.md).
|
|
172
|
-
|
|
173
163
|
---
|
|
174
164
|
|
|
175
165
|
## Roadmap Highlights
|
|
176
166
|
|
|
177
|
-
| Version | Theme | Focus |
|
|
178
|
-
|
|
179
|
-
| **0.3.
|
|
180
|
-
| [**0.4.x**](https://github.com/withtwoemms/ucon/milestone/2) | Conversion System |
|
|
181
|
-
| [**0.6.x**](https://github.com/withtwoemms/ucon/milestone/4) | Nonlinear / Specialized Units | Decibel, Percent, pH |
|
|
182
|
-
| [**0.8.x**](https://github.com/withtwoemms/ucon/milestone/6) | Pydantic Integration | Type-safe quantity validation |
|
|
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 |
|
|
183
173
|
|
|
184
174
|
See full roadmap: [ROADMAP.md](./ROADMAP.md)
|
|
185
175
|
|
|
@@ -192,13 +182,13 @@ Ensure `nox` is installed.
|
|
|
192
182
|
```
|
|
193
183
|
pip install -r requirements.txt
|
|
194
184
|
```
|
|
195
|
-
Then run the full test suite (
|
|
185
|
+
Then run the full test suite (agains all supported python versions) before committing:
|
|
196
186
|
|
|
197
187
|
```bash
|
|
198
188
|
nox -s test
|
|
199
189
|
```
|
|
200
190
|
---
|
|
201
191
|
|
|
202
|
-
>
|
|
192
|
+
> “If it can be measured, it can be represented.
|
|
203
193
|
If it can be represented, it can be validated.
|
|
204
|
-
If it can be validated, it can be trusted
|
|
194
|
+
If it can be validated, it can be trusted.”
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|