ucon 0.3.3rc1__tar.gz → 0.3.4__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 (43) hide show
  1. {ucon-0.3.3rc1 → ucon-0.3.4}/.github/workflows/publish.yaml +1 -0
  2. {ucon-0.3.3rc1 → ucon-0.3.4}/PKG-INFO +6 -5
  3. {ucon-0.3.3rc1 → ucon-0.3.4}/README.md +5 -4
  4. {ucon-0.3.3rc1 → ucon-0.3.4}/ROADMAP.md +3 -3
  5. ucon-0.3.4/docs/decisions/composable-unit-algebra.md +241 -0
  6. ucon-0.3.4/docs/decisions/composite-units.md +160 -0
  7. ucon-0.3.4/docs/decisions/unit-algebra-naming.md +169 -0
  8. ucon-0.3.4/docs/explainers/type-operation-matrix.md +14 -0
  9. ucon-0.3.4/docs/explainers/why-algebraic-closure-matters.md +175 -0
  10. ucon-0.3.4/docs/explainers/why-type-safety-matters.md +92 -0
  11. ucon-0.3.4/docs/proposals/project_unified-algebraic-core.md +171 -0
  12. ucon-0.3.4/docs/proposals/unified-unit-presentation.md +168 -0
  13. ucon-0.3.4/tests/ucon/test_algebra.py +237 -0
  14. ucon-0.3.4/tests/ucon/test_core.py +674 -0
  15. ucon-0.3.4/tests/ucon/test_quantity.py +363 -0
  16. {ucon-0.3.3rc1 → ucon-0.3.4}/tests/ucon/test_units.py +5 -3
  17. {ucon-0.3.3rc1 → ucon-0.3.4}/ucon/__init__.py +3 -3
  18. ucon-0.3.4/ucon/algebra.py +212 -0
  19. ucon-0.3.4/ucon/core.py +806 -0
  20. ucon-0.3.4/ucon/quantity.py +249 -0
  21. {ucon-0.3.3rc1 → ucon-0.3.4}/ucon/units.py +1 -2
  22. {ucon-0.3.3rc1 → ucon-0.3.4}/ucon.egg-info/PKG-INFO +6 -5
  23. {ucon-0.3.3rc1 → ucon-0.3.4}/ucon.egg-info/SOURCES.txt +12 -4
  24. ucon-0.3.3rc1/tests/ucon/test_core.py +0 -583
  25. ucon-0.3.3rc1/tests/ucon/test_dimension.py +0 -206
  26. ucon-0.3.3rc1/tests/ucon/test_unit.py +0 -143
  27. ucon-0.3.3rc1/ucon/core.py +0 -401
  28. ucon-0.3.3rc1/ucon/dimension.py +0 -172
  29. ucon-0.3.3rc1/ucon/unit.py +0 -92
  30. {ucon-0.3.3rc1 → ucon-0.3.4}/.github/workflows/tests.yaml +0 -0
  31. {ucon-0.3.3rc1 → ucon-0.3.4}/.gitignore +0 -0
  32. {ucon-0.3.3rc1 → ucon-0.3.4}/LICENSE +0 -0
  33. {ucon-0.3.3rc1 → ucon-0.3.4}/docs/decisions/unity-distance-metric-for-nearest-scale.md +0 -0
  34. {ucon-0.3.3rc1 → ucon-0.3.4}/docs/proposals/interface-unifying-the-value-layer.md +0 -0
  35. {ucon-0.3.3rc1 → ucon-0.3.4}/docs/proposals/support-for-fractional-exponents.md +0 -0
  36. {ucon-0.3.3rc1 → ucon-0.3.4}/noxfile.py +0 -0
  37. {ucon-0.3.3rc1 → ucon-0.3.4}/requirements.txt +0 -0
  38. {ucon-0.3.3rc1 → ucon-0.3.4}/setup.cfg +0 -0
  39. {ucon-0.3.3rc1 → ucon-0.3.4}/setup.py +0 -0
  40. {ucon-0.3.3rc1 → ucon-0.3.4}/tests/__init__.py +0 -0
  41. {ucon-0.3.3rc1 → ucon-0.3.4}/tests/ucon/__init__.py +0 -0
  42. {ucon-0.3.3rc1 → ucon-0.3.4}/ucon.egg-info/dependency_links.txt +0 -0
  43. {ucon-0.3.3rc1 → ucon-0.3.4}/ucon.egg-info/top_level.txt +0 -0
@@ -14,6 +14,7 @@ jobs:
14
14
  uses: actions/checkout@v4
15
15
  with:
16
16
  fetch-depth: 0 # needed for ancestry check
17
+ ref: ${{ github.ref_name }}
17
18
 
18
19
  - name: Set up Python
19
20
  uses: actions/setup-python@v5
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ucon
3
- Version: 0.3.3rc1
3
+ Version: 0.3.4
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
@@ -82,7 +82,7 @@ To best answer this question, we turn to an age-old technique ([dimensional anal
82
82
 
83
83
  `ucon` models unit math through a hierarchy where each layer builds on the last:
84
84
 
85
- <img src=https://gist.githubusercontent.com/withtwoemms/429d2ca1f979865aa80a2658bf9efa32/raw/f3518d37445301950026fc9ffd1bd062768005fe/ucon.data-model.png align="center" alt="ucon Data Model" width=600/>
85
+ <img src=https://gist.githubusercontent.com/withtwoemms/429d2ca1f979865aa80a2658bf9efa32/raw/0c704737a52b9e4a87cda5c839e9aa40f7e5bb48/ucon.data-model_v035.png align="center" alt="ucon Data Model" width=600/>
86
86
 
87
87
  ## Why `ucon`?
88
88
 
@@ -138,12 +138,13 @@ becomes straightforward when you define a measurement:
138
138
  from ucon import Number, Scale, Units, Ratio
139
139
 
140
140
  # Two milliliters of bromine
141
- two_mL_bromine = Number(unit=Units.liter, scale=Scale.milli, quantity=2)
141
+ mL = Scale.milli * units.liter
142
+ two_mL_bromine = Number(quantity=2, unit=mL)
142
143
 
143
144
  # Density of bromine: 3.119 g/mL
144
145
  bromine_density = Ratio(
145
- numerator=Number(unit=Units.gram, quantity=3.119),
146
- denominator=Number(unit=Units.liter, scale=Scale.milli),
146
+ numerator=Number(unit=units.gram, quantity=3.119),
147
+ denominator=Number(unit=mL),
147
148
  )
148
149
 
149
150
  # Multiply to find mass
@@ -46,7 +46,7 @@ To best answer this question, we turn to an age-old technique ([dimensional anal
46
46
 
47
47
  `ucon` models unit math through a hierarchy where each layer builds on the last:
48
48
 
49
- <img src=https://gist.githubusercontent.com/withtwoemms/429d2ca1f979865aa80a2658bf9efa32/raw/f3518d37445301950026fc9ffd1bd062768005fe/ucon.data-model.png align="center" alt="ucon Data Model" width=600/>
49
+ <img src=https://gist.githubusercontent.com/withtwoemms/429d2ca1f979865aa80a2658bf9efa32/raw/0c704737a52b9e4a87cda5c839e9aa40f7e5bb48/ucon.data-model_v035.png align="center" alt="ucon Data Model" width=600/>
50
50
 
51
51
  ## Why `ucon`?
52
52
 
@@ -102,12 +102,13 @@ becomes straightforward when you define a measurement:
102
102
  from ucon import Number, Scale, Units, Ratio
103
103
 
104
104
  # Two milliliters of bromine
105
- two_mL_bromine = Number(unit=Units.liter, scale=Scale.milli, quantity=2)
105
+ mL = Scale.milli * units.liter
106
+ two_mL_bromine = Number(quantity=2, unit=mL)
106
107
 
107
108
  # Density of bromine: 3.119 g/mL
108
109
  bromine_density = Ratio(
109
- numerator=Number(unit=Units.gram, quantity=3.119),
110
- denominator=Number(unit=Units.liter, scale=Scale.milli),
110
+ numerator=Number(unit=units.gram, quantity=3.119),
111
+ denominator=Number(unit=mL),
111
112
  )
112
113
 
113
114
  # Multiply to find mass
@@ -23,13 +23,13 @@ Stable baseline for:
23
23
  - [x] Implement `Vector` and `Dimension` classes
24
24
  - [x] Integrate dimensions into `Unit`
25
25
  - [x] Refactor `ucon.units` to use dimensional definitions
26
- - [ ] Publish documentation for dimensional operations
26
+ - [x] Publish documentation for dimensional operations
27
27
  - [x] Verify uniqueness and hashing correctness across all Dimensions
28
28
  - [x] Redesign `Exponent` to support algebraic operations (`__mul__`, `__truediv__`, `to_base`, etc.)
29
29
  - [x] Remove redundant evaluated caching in favor of property-based computation
30
30
  - [x] Integrate `Scale` with Exponent for consistent prefix arithmetic
31
31
  - [ ] Update `Number` and `Ratio` to use Exponent-driven scaling
32
- - [ ] Add regression tests for prefix math (`kilo / milli → mega`, `2¹⁰ / 10³ → 1.024×`)
32
+ - [x] Add regression tests for prefix math (`kilo / milli → mega`, `2¹⁰ / 10³ → 1.024×`)
33
33
  - [ ] Document Exponent/Scale relationship in developer guide
34
34
 
35
35
  ### 🧩 Outcomes
@@ -37,7 +37,7 @@ Stable baseline for:
37
37
  - Enables composable and type-safe dimensional operations
38
38
  - Establishes the mathematical foundation for future conversions
39
39
  - Unified algebraic foundation for all scaling and magnitude operations
40
- - Precise, reversible cross-base math (`2ⁿ ↔ 10ᵐ`)
40
+ - ~Precise, reversible cross-base math (`2ⁿ ↔ 10ᵐ`)~
41
41
  - Simplified, consistent `Scale` and `Number` behavior
42
42
  - Ready for integration into the conversion engine (`ucon.conversions`)
43
43
 
@@ -0,0 +1,241 @@
1
+ # UnitFactor — Structure for a Composable Unit Algebra
2
+
3
+ ## 1. Overview
4
+
5
+ `UnitFactor` is introduced as a **structural atom** of the unit algebra:
6
+
7
+ ```
8
+ UnitFactor = (unit, scale)
9
+ ```
10
+
11
+ where:
12
+ - `unit` is a stable, canonical unit identity (e.g., gram, liter, meter),
13
+ - `scale` is a prefix-like symbolic modifier (e.g., milli, kilo, micro).
14
+
15
+ Unlike the legacy `Unit` object—which merged name, dimension, formatting hints, and scale—`UnitFactor` is **atomic**, **unambiguous**, and **hashable**.
16
+ It acts as the _basis element_ of the unit algebra — the analogue of a "BasisDimension" (i.e. Length, Time, etc.) in dimensional algebra.
17
+
18
+ By elevating scale into a first-class algebraic component,
19
+ `UnitFactor` restores clean structural separation and enables unit expressions that are lossless, composable, and reversible.
20
+
21
+ ---
22
+
23
+ ## 2. The Problem It Solves
24
+
25
+ ### 2.1. Scale Entanglement in Legacy Unit Semantics
26
+
27
+ Legacy `Unit` objects encoded scale internally:
28
+
29
+ ```
30
+ mg = Unit("mg", dimension=MASS, scale=milli)
31
+ ```
32
+
33
+ This resulted in a system where scale and identity were inseparable, leading to:
34
+
35
+ - duplicate definitions (`mg` vs `milli * gram`),
36
+ - unwanted normalization of prefixes,
37
+ - scale leaking into numeric magnitude,
38
+ - loss of provenance during calculations.
39
+
40
+ The system could _represent_ scaled units but could not _reason_ about them consistently.
41
+
42
+ **`UnitFactor` resolves this by making scale a distinct algebraic coordinate.**
43
+
44
+ ---
45
+
46
+ ### 2.2. Loss of User Intent During Composite Operations
47
+
48
+ `CompositeUnit` formerly stored raw `Unit` instances, which meant that user-intent information, especially regarding prefixes, was immediately lost.
49
+
50
+ Example issues:
51
+
52
+ - `mL` and `L` collapsed unpredictably
53
+ - `(mg/mL) * mL` produced incorrect quantities
54
+ - scale information disappeared inside normalization (absorbed by numeric magnitude)
55
+ - mathematically identical expressions yielded structurally different objects
56
+
57
+ By shifting to:
58
+
59
+ ```
60
+ { UnitFactor(base=gram, scale=milli) → +1,
61
+ UnitFactor(base=liter, scale=milli) → -1 }
62
+ ```
63
+
64
+ the algebra becomes **lossless**, **deterministic**, and fully **transparent**.
65
+
66
+ ---
67
+
68
+ ### 2.3. Embedding Interpretation Inside Algebra
69
+
70
+ Prior to `UnitFactor`, unit multiplication/division required interpretation:
71
+
72
+ - Should prefix scale merge?
73
+ - Should the result canonicalize to SI?
74
+ - Should scale affect numeric magnitude?
75
+ - Should shorthand labels collapse?
76
+
77
+ These semantic choices polluted the algebra layer.
78
+
79
+ **`UnitFactor` restores algebraic purity by keeping scale symbolic rather than interpretive.**
80
+
81
+ ---
82
+
83
+ ## 3. `UnitFactor` as an Atom in Unit Space
84
+
85
+ `UnitFactor` parallels dimensional algebra:
86
+
87
+ **Dimensional Algebra**
88
+ ```
89
+ Vector({ BasisDimension → exponent })
90
+ ```
91
+
92
+ **Unit Algebra (with `UnitFactor`)**
93
+ ```
94
+ UnitProduct({ UnitFactor → exponent })
95
+ ```
96
+
97
+ `UnitProduct` is the successor to `CompositeUnit`: _a free abelian product over `UnitFactor`s._
98
+
99
+ This yields a clean architectural symmetry:
100
+
101
+ | Dimensional Layer | Unit Layer (future) |
102
+ |-------------------|----------------------|
103
+ | BasisDimension | UnitFactor |
104
+ | Vector | UnitProduct |
105
+ | Dimension | UnitForm |
106
+
107
+ `UnitFactor` is the “basis atom” that makes this composite unit space possible.
108
+
109
+ ---
110
+
111
+ ## 4. What `UnitFactor` Enables
112
+
113
+ ### 4.1. Lossless, Predictable Unit Arithmetic
114
+
115
+ - user prefixes remain intact
116
+ - cancellation is structural, not heuristic
117
+ - unit cancellation _before_ scale factor reconciliation
118
+ - algebraic operations preserve user intent
119
+ - scale never leaks into numeric magnitude
120
+
121
+ Examples:
122
+
123
+ ```
124
+ (g/mL) * mL → g
125
+ (mg/mL) * mL → mg
126
+ ```
127
+
128
+ Correctness is now a mathematical property, not an accidental outcome.
129
+
130
+ ---
131
+
132
+ ### 4.2. Algebraic Normalization without Canonicalization
133
+
134
+ `UnitFactor` supports strict algebraic behavior:
135
+
136
+ - no forced SI normalization
137
+ - no heuristic prefix adjustments
138
+ - no implicit conversion to base units
139
+
140
+ Canonicalization moves into higher layers (`UnitForm`, `ConversionGraph`).
141
+
142
+ ---
143
+
144
+ ### 4.3. Foundation for ConversionGraph
145
+
146
+ `UnitFactor` exposes:
147
+
148
+ - base identity
149
+ - scale prefix
150
+ - dimension
151
+
152
+ as independent coordinates, enabling semantic conversions such as:
153
+
154
+ ```
155
+ kJ·h/s → J·h/s → W·h
156
+ ```
157
+
158
+ `UnitFactor` & `UnitProduct` provide the structural substrate, while `ConversionGraph` handles meaning.
159
+
160
+ ---
161
+
162
+ ## 5. The Path It Paves
163
+
164
+ ### 5.1. Eliminating `Unit.scale`
165
+
166
+ `Unit` becomes purely declarative:
167
+
168
+ ```
169
+ Unit(name="gram", dimension=MASS)
170
+ ```
171
+
172
+ All scaling semantics shift into `FactoredUnit`.
173
+ Once scale is removed, `Unit` no longer participates in algebraic operations.
174
+ It becomes a **canonical identity object**, analogous to a basis `Dimension` (e.g. time, length, mass, etc.):
175
+ a stable nameable anchor used by registries, `UnitForm`, and `ConversionGraph`.
176
+
177
+ A Unit carries only semantic information (name, aliases, dimension) and does _not_ combine multiplicatively.
178
+ All algebraic behavior moves exclusively into `UnitFactor` and `UnitProduct`, allowing `Unit` to remain a pure symbolic identity within the broader type ecosystem.
179
+
180
+ ---
181
+
182
+ ### 5.2. CompositeUnit Evolves into UnitProduct
183
+
184
+ `CompositeUnit` is ultimately replaced by:
185
+
186
+ ```
187
+ UnitProduct = { UnitFactor → exponent }
188
+ ```
189
+
190
+ This yields a principled free abelian structure paralleling dimensional vectors.
191
+
192
+ ---
193
+
194
+ ### 5.3. User-Facing Layer Becomes UnitForm
195
+
196
+ **UnitForm** becomes the user-visible representation layer:
197
+
198
+ - parses expressions (`"g/mL"`)
199
+ - prints canonical or preferred forms
200
+ - integrates with registries
201
+ - applies prefix or display policies
202
+ - guarantees round-trip stability
203
+
204
+ `UnitProduct` becomes the algebra;
205
+ `UnitForm` becomes the language.
206
+
207
+ ---
208
+
209
+ ### 5.4. Integration with ConversionGraph
210
+
211
+ `UnitFactor` and `UnitProduct` act as the structural substrate for semantic conversions:
212
+
213
+ - J ↔ W·s
214
+ - cal ↔ J
215
+ - mL ↔ L
216
+ - h ↔ 3600 s
217
+
218
+ `ConversionGraph` operates on meaning; `UnitProduct` ensures consistent structure.
219
+
220
+ ---
221
+
222
+ ## 6. Architectural Summary
223
+
224
+ | Layer | Role | Value |
225
+ |-------|------|--------|
226
+ | **Unit** | atomic, canonical identity | base symbol for units |
227
+ | **UnitFactor** | algebraic atom | scale-inclusive symbolic factor |
228
+ | **UnitProduct** | formal product | composable, invertible algebra |
229
+ | **UnitForm** | user-facing interface | formatting, parsing, canonicalization |
230
+ | **ConversionGraph** | semantic layer | derived-unit conversions |
231
+
232
+ For a deep dive into the new naming conventions check [this](./unit-algebra-naming.md) out
233
+
234
+ ---
235
+
236
+ ## 7. Philosophical Note
237
+
238
+ > A unit is not a label.
239
+ > It is a structural atom in an algebra whose interactions encode physical meaning.
240
+
241
+ `UnitFactor` restores this foundation, revealing the future shape of `ucon`’s unit algebra.
@@ -0,0 +1,160 @@
1
+ # Unit Combination and Algebraic Closure in `ucon`
2
+
3
+ ## 1. Composite Units
4
+
5
+ Composite dimensions (e.g., velocity, acceleration, density) arise as products or quotients of base dimensions.
6
+ In the continuous model, these combine algebraically in exponent space.
7
+
8
+ Example: velocity = length / time
9
+
10
+ $$
11
+ V = \frac{L}{T} = a^{x_L - x_T}
12
+ $$
13
+
14
+ If the length and time graphs are independent CCGs, their exponents combine by **subtraction of vectors**.
15
+ This allows multi-dimensional conversion to be expressed purely algebraically.
16
+
17
+ | Quantity | Formula | Exponent Form |
18
+ |-----------|----------|---------------|
19
+ | Velocity | L/T | \( $x_L - x_T$ \) |
20
+ | Acceleration | L/T² | \( $x_L - 2x_T$ \) |
21
+ | Force | M·L/T² | \( $x_M + x_L - 2x_T$ \) |
22
+ | Density | M/L³ | \( $x_M - 3x_L$ \) |
23
+
24
+ Each component dimension (L, T, M, etc.) maintains its own graph — conversions for composite units are vector sums.
25
+
26
+ ---
27
+
28
+ ## 2. What Would Be Impossible Without `CompositeUnit`
29
+
30
+ Without `CompositeUnit`, **ucon would be descriptive, not algebraic.**
31
+ It could *store* conversions but not *derive* them.
32
+
33
+ | Capability | Without `CompositeUnit` | With `CompositeUnit` |
34
+ |-------------|------------------------|-----------------------|
35
+ | Unit algebra | Flat mapping of names | Free abelian group of composable morphisms |
36
+ | Derived dimensions | Pre-registered only | Algebraically inferred |
37
+ | Simplification | Manual or string-based | Symbolic, lossless cancellation |
38
+ | Conversion chaining | Requires graph heuristics | Structural traversal via decomposition |
39
+ | Reasoning | String pattern matching | Category-theoretic functor between symbolic and numeric domains |
40
+ | Expressiveness | Lookup tables | Algebraic system of morphisms |
41
+ | Extensibility | Must predefine all derived units | Derived units emerge from composition |
42
+
43
+ > `CompositeUnit` transforms ucon from a **registry of facts** into an **engine of derivation.**
44
+
45
+ ---
46
+
47
+ ## 3. CompositeUnit, Exponent, and Scale: Cohesive Closure
48
+
49
+ | Role | Domain | Operation | Closure Guarantee |
50
+ |------|---------|------------|------------------|
51
+ | **Exponent** | Algebra | Powers of numeric bases (10, 2, etc.) | Defines magnitude algebra |
52
+ | **Scale** | Ontology | Named prefixes for exponents | Names exponent values |
53
+ | **Unit** | Ontology | Typed measure symbol | Couples dimension + scale |
54
+ | **CompositeUnit** | Algebra on units | Product, quotient, powers | Closes the group |
55
+
56
+ This creates the **closure chain**:
57
+
58
+ $$
59
+ \text{Exponent} \Rightarrow \text{Scale} \Rightarrow \text{Unit} \Rightarrow \text{CompositeUnit}
60
+ $$
61
+
62
+ Each layer embeds the previous one, while adding new semantics (naming, dimension, composability).
63
+
64
+ ---
65
+
66
+ ## 4. CompositeUnit as Morphism
67
+
68
+ `CompositeUnit` is both an **element** and a **morphism** in the unit group:
69
+
70
+ $$
71
+ f: U_1 \to U_2, \quad f(a \cdot b) = f(a) \cdot f(b)
72
+ $$
73
+
74
+ This gives the unit algebra:
75
+
76
+ - **Associativity** (composition)
77
+ - **Identity** (`dimensionless`)
78
+ - **Inverses** (`u⁻¹`)
79
+ - **Closure** (`U × U → CompositeUnit ⊂ Unit`)
80
+
81
+ It forms a **monoidal category** of units where:
82
+ - objects = base units,
83
+ - morphisms = composite transformations,
84
+ - functors = conversions between systems (SI, CGS, Imperial).
85
+
86
+ ---
87
+
88
+ ## 5. Why the ConversionGraph Depends on It
89
+
90
+ The ConversionGraph functor acts over this unit algebra:
91
+
92
+ $$
93
+ F: (\text{Unit Algebra}) \to (\text{Numeric Transformations})
94
+ $$
95
+
96
+ `CompositeUnit` guarantees totality — all symbolic unit expressions can be decomposed and recomposed in graph traversal.
97
+
98
+ Example:
99
+ ```python
100
+ force = units.kilogram * units.meter / (units.second ** 2)
101
+ graph.convert(force, "N") # Converts via decomposed path
102
+ ```
103
+
104
+ Without `CompositeUnit`, such a conversion is not possible,
105
+ since the graph would have no way to represent composite relationships between base units.
106
+
107
+ ---
108
+
109
+ ## 6. How Competitors Handle This (or Don’t)
110
+
111
+ | Library | Composite Concept | Mechanism | Limitations |
112
+ |----------|------------------|------------|--------------|
113
+ | **Pint** | Implicit composite via `Quantity` + registry | Tuple of (unit, exponent) pairs parsed from strings | Composite logic is hidden, not a first-class algebraic type; no symbolic morphisms |
114
+ | **SymPy.physics.units** | Composition through symbolic `Mul(Unit, Unit)` trees | Full symbolic representation | Algebraic but non-canonical; requires tree simplification and lacks registry consistency |
115
+ | **Unyt** | `UnitRegistry` + array wrapper | Exponents over strings | Conversion graph is registry-based only; no morphic algebra |
116
+ | **Astropy.units** | `UnitBase` and compound units via operator overloads | Functional for end users | No first-class algebra for composition; context-dependent simplification |
117
+ | **Unum** | Explicit *universal number* with lazy dimension algebra | Arithmetic objects track dimension exponents | Symbolic but not categorical: lacks conversion composition semantics and no functorial conversion; operations can lose context |
118
+ | **ucon** | **`CompositeUnit` as morphism in a composable unit group** | Explicit algebraic closure; conversion is a functor | Symbolic reasoning, compositional simplification, direct ConversionGraph integration |
119
+
120
+ ### 🔍 Commentary on *Unum*
121
+ - **Strength:** Unum pioneered embedding dimensional arithmetic directly into number objects (`Unum(3, 'm/s²')`).
122
+ - **Weakness:** Its algebra is _monolithic_, coupling value, dimension, and conversion rules in one type.
123
+ - There’s no separation of concerns — no morphism layer between symbolic representation and numeric transformation.
124
+ - Conversions are _eager_ rather than structural, meaning they cannot be composed functorially.
125
+ - **Contrast with ucon:**
126
+ ucon’s `CompositeUnit` isolates the symbolic structure from numeric evaluation.
127
+ This allows deferred evaluation, symbolic simplification, and graph-based reasoning across systems of units.
128
+ Thus, ucon’s algebra is **refactorable and extensible**, while unum’s is **monolithic and eager**.
129
+
130
+ ---
131
+
132
+ ## 7. Philosophical Summary
133
+
134
+ > In most libraries, a “unit” is a label.
135
+ > In ucon, a unit is a **morphism** in a composable algebra.
136
+
137
+ `CompositeUnit` makes ucon a **structural model** of physics:
138
+
139
+ $$
140
+ \frac{L}{T^2} \Rightarrow \text{Acceleration}
141
+ $$
142
+
143
+ not as a string or a heuristic, but as a typed algebraic expression.
144
+
145
+ It allows ucon to:
146
+ - **derive** (not just define) relationships,
147
+ - **normalize** representations,
148
+ - **reason** about systems symbolically and numerically.
149
+
150
+ Thus:
151
+ > ucon is not merely a _unit-aware computation library_ — it is a **dimensional reasoning framework.**
152
+
153
+ ---
154
+
155
+ ## 7. Next Steps (Implementation Implications)
156
+
157
+ 1. Keep `CompositeUnit` algebraically pure (no conversion logic).
158
+ 1. Allow `Unit` and `Scale` to compose naturally.
159
+ 1. Ensure simplification of reciprocal terms (`g·mL/mL → g`).
160
+ 1. Use canonical hashing for composite signatures.
@@ -0,0 +1,169 @@
1
+ # Future Naming Scheme for the `Unit` Algebra
2
+ *(UnitFactor, UnitProduct, UnitForm)*
3
+
4
+ ## 1. Context
5
+
6
+ ucon’s evolving unit system is transitioning toward a more explicit algebraic foundation.
7
+ Recent advances—particularly the introduction of `FactoredUnit`—have clarified the need for:
8
+
9
+ - cleaner separation between *atomic unit components*
10
+ - purely algebraic composite unit structures
11
+ - a stable, user-facing layer for rendering, parsing, and canonicalization
12
+
13
+ As ucon’s unit semantics converge toward a **free abelian group** model (mirroring the dimensional algebra), the existing naming (`CompositeUnit`, `FactoredUnit`, `Unit`) no longer captures the distinctions between:
14
+
15
+ - the algebraic substrate
16
+ - the atomic symbolic components
17
+ - the user interface layer
18
+
19
+ This document outlines a naming scheme aligned with that architecture.
20
+
21
+ ---
22
+
23
+ ## 2. Decision
24
+
25
+ Adopt the following naming triad for a future ucon unit algebra:
26
+
27
+ ### **1. UnitFactor**
28
+ The atomic building block of unit expressions.
29
+
30
+ - Represents a pair: _(canonical unit identity, scale)_
31
+ - Corresponds structurally to a “coordinate” or “basis element” of the unit algebra
32
+ - Replaces and generalizes the existing `FactoredUnit`
33
+ - Holds no formatting, registry, or canonicalization responsibilities
34
+ - Exists solely for algebraic manipulation
35
+
36
+ ### **2. UnitProduct**
37
+ The algebraic combination of UnitFactors.
38
+
39
+ - A mapping `{UnitFactor → exponent}`
40
+ - Represents a formal multiplicative product
41
+ - Free abelian group structure (addition/subtraction of exponents)
42
+ - Replaces the current `CompositeUnit`
43
+ - Pure algebraic core: no interpretation, no formatting, no normalization
44
+ - Positionally analogous to the dimensional `Vector` type, but specialized to units
45
+
46
+ ### **3. UnitForm**
47
+ The user-facing representation of units.
48
+
49
+ - Provides formatting, parsing, canonicalization rules, alias handling, and registry integration
50
+ - Wraps a `UnitProduct` internally
51
+ - Represents what users write and see (`"g/mL"`, `"kW·h"`, `"psi"`)
52
+ - Cleanly decouples UI semantics from algebraic mechanics
53
+ - Replaces the conceptual role of “Unit” as the object users interact with
54
+
55
+ ---
56
+
57
+ ## 3. Rationale
58
+
59
+ ### 3.1. Why **UnitFactor**
60
+ “Factor” is:
61
+
62
+ - algebraically accurate
63
+ - readable and intuitive
64
+ - directly compatible with the term “product”
65
+ - expressive of the role: a scaled, atomic unit element that participates in multiplication
66
+
67
+ Compared to alternatives (“Term”, “BasisUnit”, “Atom”, “Coordinate”),
68
+ **UnitFactor** strikes the ideal balance between mathematical precision and everyday usability.
69
+
70
+ ---
71
+
72
+ ### 3.2. Why **UnitProduct**
73
+ The algebra underlying ucon unit combinations is not vector algebra
74
+ (in the sense of magnitude-1 unit vectors), but **formal multiplicative algebra**.
75
+
76
+ A UnitProduct is:
77
+
78
+ - a product of UnitFactors
79
+ - a free abelian group element
80
+ - the natural analogue of a symbolic monomial
81
+
82
+ “Product” is accessible to users without sacrificing correctness.
83
+ It avoids the misleading connotation of “vector” and is friendlier than “monomial.”
84
+
85
+ ---
86
+
87
+ ### 3.3. Why **UnitForm**
88
+ The system needs a distinct layer that:
89
+
90
+ - users interact with
91
+ - ties into the registry
92
+ - controls printing and parsing
93
+ - decides when/how to reflect prefixes
94
+ - chooses canonical representations
95
+ - remains stable across refactors
96
+
97
+ “UnitExpression” was serviceable but generic.
98
+ “UnitForm” is:
99
+
100
+ - concise
101
+ - semantically clean
102
+ - visually and conceptually distinct from algebraic layers
103
+ - evocative of representation rather than structure
104
+
105
+ It completes the tiered architecture:
106
+
107
+ ```
108
+ UnitFactor → structural atom
109
+ UnitProduct → algebraic composite
110
+ UnitForm → user-facing representation
111
+ ```
112
+
113
+ ---
114
+
115
+ ## 4. Consequences
116
+
117
+ ### Positive
118
+
119
+ - Clear conceptual separation between algebra and user semantics
120
+ - Naming aligns with physical intuition and mathematical structure
121
+ - Improves API clarity and documentation readability
122
+ - Prepares the codebase for future features:
123
+ - ConversionGraph
124
+ - Semantic canonicalization
125
+ - Unit registry enhancements
126
+ - Parsing/formatting improvements
127
+
128
+ ### Neutral/Deferred
129
+
130
+ - No immediate refactor is required; this ADR guides future work
131
+ - CompositeUnit and FactoredUnit can coexist temporarily during transition
132
+ - Existing APIs remain intact until migration pathways are established
133
+
134
+ ### Negative
135
+
136
+ - Some renaming churn is expected during adoption
137
+ - Downstream references to “CompositeUnit” and “FactoredUnit” will need updating
138
+
139
+ ---
140
+
141
+ ## 5. Alternatives Considered
142
+
143
+ - **UnitMonomial / UnitTerm**
144
+ - Mathematically elegant but feels overly academic
145
+ - **UnitCoordinate / UnitVector**
146
+ - Accurate but too abstract, and “unit vector” is overloaded in linear algebra
147
+ - **UnitExpression**
148
+ - Adequate, but less crisp than “UnitForm” as a representation-layer concept
149
+ - **Retaining existing names**
150
+ - Would perpetuate conceptual muddiness as the system grows
151
+
152
+ ---
153
+
154
+ ## 6. Future Work (Nonbinding)
155
+
156
+ - Introduce `UnitFactor` and `UnitProduct` in parallel with existing structures
157
+ - Evolve `UnitForm` as the primary user-facing unit API
158
+ - Gradually refactor internal algebra toward the new architecture
159
+ - Propose complementary ADRs covering:
160
+ - CanonicalUnit identity objects
161
+ - Registry architecture
162
+ - ConversionGraph integration
163
+ - Unit parsing and pretty-printing models
164
+
165
+ ---
166
+
167
+ ## 7. Acknowledgements
168
+
169
+ This naming scheme emerged from design discussions surrounding the FactoredUnit refactor, the need for scale-preserving operations, and the broader goal of unifying unit algebra with dimensional algebra in ucon’s long-term architecture.
@@ -0,0 +1,14 @@
1
+ # Type × Operation Matrix
2
+
3
+ | Type | + | - | * | / | ** | Neg | == | Category |
4
+ |---------------|-----|-----|-----|-----|-----|-----|--------|-------------------------|
5
+ | Vector | ✓ | ✓ | ✓ | — | — | ✓ | ✓ | Algebraic |
6
+ | Exponent | — | — | ✓ | ✓ | ✓ | — | ✓ | Algebraic |
7
+ | Dimension | — | — | ✓ | ✓ | ✓ | — | ✓ | Algebraic |
8
+ | CompositeUnit | — | — | ✓ | ✓ | ✓ | — | ✓ | Algebraic |
9
+ | Number | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | structural | Algebraic |
10
+ | Ratio | — | — | ✓ | ✓ | — | — | — | Partial Algebra |
11
+ | Unit | — | — | — | — | — | — | ✓ | Semantic |
12
+ | Scale | — | — | ✓ | — | — | — | — | Semantic |
13
+ | ScaleDescriptor | — | — | — | — | — | — | — | Semantic |
14
+ | FactoredUnit | — | — | — | — | — | — | ✓ | Semantic |