Releases: withtwoemms/ucon
v1.6.3
Added
- Cross-basis coercion in
@enforce_dimensions. When a CGS-basis argument
(e.g. dyne, erg, poise) passes dimensional validation against an SI
constraint, the decorator now automatically coerces it to the coherent SI
equivalent before the function body runs. This eliminates "Cannot multiply
dimensions from different bases" errors inside decorated functions.
v1.6.3a1
first alpha release of v1.6.3
v1.6.2
Added
-
Base-form conversion fallback.
ConversionGraph._convert_via_base_form()
decomposes both source and destinationUnitProductexpressions to their SI
base units and computes the conversion factor as the ratio of prefactors.
This handles conversions where factor structures don't align (e.g.,
kg·m/s² → N,J/L → Pa,W/m² → BTU/(h·ft²)) without requiring
explicit product edges — the definitional identity is already encoded in
each unit'sbase_form. -
Cross-basis dimensional compatibility for
@enforce_dimensions.
_dimensions_compatible()inucon/checking.pynormalizes both actual and
expected dimensions to SI viaBasisGraphbefore comparing. CGS units like
poise (dimensioncgs_dynamic_viscosity) now satisfy constraints expecting
the SI equivalent (dynamic_viscosity). -
22 new
base_formentries incomprehensive.ucon.tomlfor SI-basis
units that previously lacked decompositions: acre, ampere_per_meter, barn,
becquerel, coulomb_meter, curie, farad, gray, hectare, henry,
joule_per_kelvin, knot, lumen, mile_per_hour, molar, rad_dose, rem,
siemens, sievert, tesla, weber, webers_per_meter.
Fixed
-
Liter
base_formprefactor corrected from1.0to0.001
(1 L = 0.001 m³). -
Tex
base_formprefactor corrected from9.0to1e-6
(1 tex = 1 g/km = 1e-6 kg/m). -
Denier
base_formprefactor corrected from1.0to
1.1111111111111111e-07(1 denier = 1 g/9000 m). -
23 volume unit
base_formprefactors corrected. All were calibrated
relative to liter = 1.0 (wrong) instead of m³. Affected units: acre_foot,
barrel, bushel, cubic_foot, cubic_inch, cubic_yard, cup, fluid_ounce,
gallon, gill, imperial_cup, imperial_fluid_ounce, imperial_gallon,
imperial_gill, imperial_pint, imperial_quart, minim, peck, pint, quart,
stere, tablespoon, teaspoon. -
generate_base_forms.pyBFS oracle no longer assumes all reference
units are SI-coherent (prefactor = 1.0). The BFS seed prefactor is now
read from the unit's own TOMLbase_form, making the oracle a consistency
checker across each dimension partition rather than relying on a hardcoded
override table. -
_dimensions_compatiblecrash on isolated bases. The except clause
in_dimensions_compatible()did not catchNoTransformPath(raised by
BasisGraph.get_transform()for disconnected bases), causing an unhandled
exception instead of returningFalse.
v1.6.1
Fixed
-
Unit expression parser unified to standard left-to-right associativity.
_UnitParser(the recursive-descent parser inucon/parsing.py) was using a
non-standard "slash-opens-denominator" convention wherea/b*cwas parsed as
a/(b·c). Reverted to standard order of operations:*and/have equal
precedence and associate left-to-right, soa/b*c=(a/b)·c. Multi-term
denominators require explicit parentheses:m³/(kg·s²). -
Gravitational constant (G), molar gas constant (R), and Stefan-Boltzmann
constant (σ) unit strings incomprehensive.ucon.tomlrestored to use
explicit parentheses:m³/(kg·s²),J/(mol·K),W/(m²·K⁴). Without
parentheses, left-to-right parsing produced incorrect unit decompositions. -
TOML exponent formatting. The serializer emitted
^2.0for integral
exponents; now emits^2. The parser also now accepts float exponents
(^2.0) for backward compatibility with previously-emitted TOML files.
Removed
_parse_product_expression()and_resolve_single_factor()from
ucon/serialization.py. Product expression parsing is now handled
exclusively by_UnitParserviaget_unit_by_name(), eliminating a
redundant regex-based parser that had diverged in associativity semantics.
v1.6.1a1
first alpha release of v1.6.1a1
v1.6.0
Added
-
TOML as single source of truth.
ucon/comprehensive.ucon.tomlis now
the canonical declaration site for all units, conversion edges, and physical
constants. Python modules (ucon/units.py,ucon/graph.py,
ucon/constants.py) consume the TOML at import time via a central
single-load cache (ucon/_loader.py). ~1,250 lines of hardcoded Python
declarations replaced by ~150 lines of loader infrastructure. -
ucon/_loader.py— central TOML loader with caching.get_graph(),
get_units(),get_constants()return the same object instances across
all consumers.reset()for test isolation. -
ucon/expressions.py— AST-based safe expression evaluator for TOML
factor fields. Evaluates symbolic expressions like"1 / Eₕ"and
"mP * c**2 / Eₕ"where symbols resolve to physical constants. Propagates
relative uncertainty via GUM rules (quadrature for multiply/divide,
|n| * relfor power, absolute quadrature for add/subtract). Only
ast.Constant,ast.Name,ast.BinOp, andast.UnaryOpnodes are
accepted — noeval(). -
Symbolic expression factors in TOML. 14 conversion edges now use
constant expression strings instead of hardcoded numeric values:factor = "gₙ"for kilogram_force → newton, millimeter_water → pascalfactor = "1 / Eₕ"for joule → hartreefactor = "1 / Ry"for joule → rydbergfactor = "1 / e"for joule → electron_voltfactor = "1 / a₀"for meter → bohr_radiusfactor = "1 / mP","1 / lP","1 / tP","1 / TP"for
SI → Planck unit edgesfactor = "e / Eₕ","e / (mP * c**2)","mP * c**2 / Eₕ"for
compound cross-basis edgesrel_uncertaintyauto-derived from referenced constants via GUM
quadrature — no longer hardcoded on these edges.
-
Auto-generated
.pyitype stubs for IDE autocomplete:ucon/units.pyi— all unit names typed asUnitucon/constants.pyi— all constant instances and accessor functions- Generated by
scripts/generate_unit_stubs.pyand
scripts/generate_constant_stubs.py
-
Makefile targets for stub generation:
make stubs— generate all stubs (units, constants, dimensions)make unit-stubs,make constant-stubs,make dimension-stubs—
individual targetsmake stubs-check— verify all stubs are current (for CI)
-
stubsCI job (.github/workflows/tests.yaml) — verifies.pyi
stubs match the current TOML on every push and PR. Wired into the
cigate job. -
tests/ucon/test_expressions.py— expression parser tests covering
numeric literals, constant references, division, multiplication, power,
compound expressions, unknown symbols, and unsupported syntax. -
tests/ucon/test__loader.py— loader integration tests covering graph
construction, unit extraction, constant extraction, object identity
guarantees, and cache behavior.
Changed
-
ucon/units.pyrewritten — ~400 lines of hardcodedUnit()
declarations removed, replaced by a thin loader that populates module
globals from the TOML.register_priority_scaled_alias()calls,
UnitSystemdefinitions, andhave()preserved. -
ucon/graph.pytrimmed —_build_standard_edges()(~557 lines of
hardcodedgraph.add_edge()calls) removed._build_standard_graph()
delegates to_loader.get_graph(). -
ucon/constants.pyrewritten —_build_constants()(~300 lines of
hardcodedConstant()instances) removed, replaced by loader delegation.
Constantgains analiases: tuple = ()field for TOML round-trip
fidelity. -
ucon/serialization.pyenhanced — constants materialized before edges
(step 7, was step 10) so expression factors can resolve constant symbols.
_build_edge_map()accepts string expression factors containing constant
references.FORMAT_VERSIONbumped to"1.4". NFKC-normalized constant
symbols registered in the lookup table for Python AST compatibility. -
ucon/comprehensive.ucon.tomlmoved into package — canonical copy
now atucon/comprehensive.ucon.toml, shipped as package data. Includes
26 constants with full CODATA metadata, 14 expression-based edge factors,
no[contexts.*]section (contexts stay in Python). -
Makefile
stubstarget expanded from dimension-only to all three
stub types (units, constants, dimensions). Oldtomltarget removed.
Removed
-
scripts/generate_comprehensive_toml.py— the old Python → TOML export
script. The TOML is now hand-edited as the source of truth. -
_build_standard_edges()body inucon/graph.py(retained as no-op
stub for backward compatibility). -
_build_constants()body inucon/constants.py. -
Hardcoded
Unit()declarations inucon/units.py. -
make tomlMakefile target.
Notes
-
Backward compatibility. All public APIs (
from ucon.units import meter,
from ucon.constants import speed_of_light,get_default_graph(), etc.)
work identically. The 2,095-test suite passes without modification. -
Object identity guarantee. All consumers share the same
Unitand
Constantinstances from a single TOML parse via_loader.py. E.g.,
units.meter is get_default_graph()._name_registry_cs["meter"]. -
Import performance. The ~4,600-line TOML is parsed once at first
import and cached. If performance becomes a concern, a pre-compiled
binary cache can be added in a future release.
v1.5.0
Added
-
Conversion factor uncertainty propagation. When a conversion factor
derives from a measured physical constant (e.g., Hartree energy, Planck
mass), its CODATA 2022 relative uncertainty can now propagate into the
converted result via GUM quadrature:
(δy/y)² = (δx/x)² + (δa/a)². -
rel_uncertaintyfield onMapsubclasses — optional
rel_uncertainty: float = 0.0onLinearMap,AffineMap, and
ReciprocalMap. Composition rules:@(composition): quadraturesqrt(r₁² + r₂²)inverse(): preserved unchanged**n(power):|n| * rComposedMap: computed property via quadrature ofouterandinner- Default
0.0means exact conversions carry zero overhead.
-
Number.to(target, propagate_factor_uncertainty=False)— opt-in
parameter. WhenTrue, combines measurement uncertainty and conversion
factor uncertainty via GUM quadrature. WhenFalse(default), behavior
is unchanged from prior versions. -
8 new physical constants in
ucon.constantswith CODATA 2022
uncertainties:- Atomic-scale:
hartree_energy(Eₕ),rydberg_energy(Ry),
bohr_radius(a₀),atomic_unit_of_time(ℏ/Eₕ) - Planck-scale:
planck_mass(m_P),planck_length(l_P),
planck_time(t_P),planck_temperature(T_P) - All carry
category="measured"and are accessible via
get_constant_by_symbol()with both Unicode and ASCII aliases.
- Atomic-scale:
-
15 default graph edges with
rel_uncertaintyfrom measured constants:- Atomic: kg↔electron_mass, J↔hartree, eV↔hartree, J↔rydberg,
m↔bohr_radius, s↔atomic_time, electron_mass→hartree,
bohr_radius→atomic_time - Planck: kg↔planck_mass, J↔planck_energy, eV↔planck_energy,
m↔planck_length, s↔planck_time, K↔planck_temperature,
planck_energy↔hartree
- Atomic: kg↔electron_mass, J↔hartree, eV↔hartree, J↔rydberg,
-
EdgeDef.rel_uncertaintyfield inucon.packages—.ucon.toml
packages can now declare conversion factor uncertainty on[[edges]]
entries. -
TOML serialization of
rel_uncertainty._edge_dict()emits
rel_uncertaintywhen non-zero;_build_edge_map()reads it back.
Backward-compatible: existing TOML files without the field deserialize
withrel_uncertainty=0.0. -
to_toml()dimension collection from unit registry. Dimensions
referenced by registered units (e.g., area, velocity, force) are now
included in the output even when they have no dedicated edge partition.
Required for self-contained TOML files that don't rely on a runtime
dimension registry — preparatory for the TOML-as-source-of-truth
transition. -
scripts/generate_comprehensive_toml.py— generation script that
producesexamples/units/comprehensive.ucon.tomlfrom the default graph
viato_toml(), with cosmetic array collapsing for readability. -
make toml— Makefile target that runs the TOML generation script. -
tests/ucon/test_factor_uncertainty.py— 32 new tests across 8 classes
covering map construction, composition rules (quadrature, inverse,
power),Number.to()backward compatibility, factor uncertainty
propagation, multi-hop accumulation, and serialization round-trips. -
tests/ucon/conversion/test_map.py::TestRelUncertaintyComposition—
6 new tests forrel_uncertaintycomposition, inverse, and power on
LinearMapandAffineMap.
Changed
-
comprehensive.ucon.tomlis now machine-generated from the default
graph rather than hand-maintained. Contains 7 bases, 71 dimensions,
15 transforms, 227 units, 148 edges, 17 product edges, and 42
cross-basis edges (15 withrel_uncertainty). -
ucon.constantsused as single source for conversion factors in
ucon.graph. The default graph's_build_standard_edges()now loads
fullConstantobjects and extracts both.valueand.uncertainty,
rather than using hard-coded numeric literals. This ensures conversion
factor values and their uncertainties stay in sync with the constants
module.
Notes
-
Backward compatibility.
propagate_factor_uncertaintydefaults to
False. All existing code produces identical results. The
rel_uncertaintyfield defaults to0.0and is omitted from TOML
output when exact, so serialized files remain unchanged for exact
conversions. -
GUM model. The implementation uses relative uncertainty because it
composes cleanly under multiplication and is preserved under inversion.
For affine maps (temperature),rel_uncertaintyrefers to the slope
aonly; the offsetbis exact by definition.
v1.5.0a1
first v1.5.0 alpha release
v1.4.0
Added
-
Planck basis — 1-component energy basis (
E) where ℏ = c = G = k_B = 1.PLANCKbasis inucon.basis.builtinSI_TO_PLANCK/PLANCK_TO_SItransforms viaConstantBoundBasisTransform
with constant bindings for ℏ, c, G, k_BPLANCK_ENERGY(E¹) andPLANCK_LENGTH(E⁻¹) dimensions- 5 Planck units:
planck_energy(E_P),planck_mass(m_P),
planck_length(l_P),planck_time(t_P),
planck_temperature(T_P) — all with CODATA 2018 values - Mass and energy share
PLANCK_ENERGY(E¹); length and time share
PLANCK_LENGTH(E⁻¹). This is physically correct: when c = 1,
mass ≡ energy and length ≡ time.
-
Atomic basis — 1-component energy basis (
E) where ℏ = e = mₑ = 4πε₀ = 1.ATOMICbasis inucon.basis.builtinSI_TO_ATOMIC/ATOMIC_TO_SItransforms with constant bindings
for a₀, ℏ, mₑc², e/ℏATOMIC_ENERGY(E¹) andATOMIC_LENGTH(E⁻¹) dimensions- 3 new atomic units:
bohr_radius(a_0,a0),atomic_time(t_au),
electron_mass(m_e) - Differs from Natural/Planck in that electric current is representable
(I → E¹) but temperature is not (k_B ≠ 1)
-
Inter-basis isomorphisms — 6 bidirectional 1×1 identity transforms
connecting NATURAL, PLANCK, and ATOMIC bases:NATURAL_TO_PLANCK/PLANCK_TO_NATURAL(mediated by G)NATURAL_TO_ATOMIC/ATOMIC_TO_NATURAL(mediated by e, mₑ, 4πε₀)PLANCK_TO_ATOMIC/ATOMIC_TO_PLANCK(mediated by G, e, mₑ, 4πε₀)- Cross-basis conversion edges:
eV ↔ planck_energy,
eV ↔ hartree,planck_energy ↔ hartree - Inter-basis edge factors are computed from shared SI bridge constants
(e.g.,_eV_J / _EP_J) rather than independently rounded, ensuring
exact algebraic cancellation on round-trips.
-
CGS-EMU basis promotion —
CGS_EMUpromoted from 3-component
(L, M, T) to 4-component (L, M, T, Φ) basis to support the
ESU↔EMU bridge:SI_TO_CGS_EMUis now an 8×4 transform withI → Φ¹mappingCGS_ESU_TO_CGS_EMUandCGS_EMU_TO_CGS_ESUbridge transforms
(4×4) connecting the two electromagnetic subsystems- 15 ESU/EMU dimension vectors redefined with integer exponents on
the expanded bases - ESU↔EMU conversion edges via the speed of light c:
statcoulomb ↔ abcoulomb,statvolt ↔ abvolt,
statampere ↔ biot,statohm ↔ abohm,
statfarad ↔ abfarad,stathenry ↔ abhenry - Fulfills the v1.3.1 deferral: "ESU↔EMU cross-family conversion
deferred to v1.4.0"
-
33+ new tests across
test_cross_basis.py:
TestPlanckDimensionIsolation(5),TestPlanckConversions(6),
TestAtomicDimensionIsolation(7),TestAtomicConversions(6),
TestInterBasisIsomorphisms(6 including full J→E_P→eV→Eₕ→J
round-trip atplaces=10), plus ESU↔EMU bridge tests.
Changed
-
hartreeandrydbergmoved from NATURAL to ATOMIC basis.
These units physically belong to the atomic system (ℏ = e = mₑ =
4πε₀ = 1), not the particle-physics natural system. Their dimension
changes fromNATURAL_ENERGYtoATOMIC_ENERGY; numeric conversion
values to SI are unchanged. Cross-basis edges from SI
(joule → hartree,joule → rydberg) now route through
SI_TO_ATOMICinstead ofSI_TO_NATURAL. -
BasisGraph standard graph now registers 15 transforms (was 6):
SI↔CGS, SI↔CGS_ESU, SI↔CGS_EMU, CGS_ESU↔CGS_EMU, SI↔NATURAL,
SI↔PLANCK, SI↔ATOMIC, NATURAL↔PLANCK, NATURAL↔ATOMIC, PLANCK↔ATOMIC.
Notes
-
Round-trip precision. The full cross-basis round-trip
joule → planck_energy → electron_volt → hartree → joulereturns
exactly 1.0 (verified atplaces=10). This is achieved by defining
physical constants once and computing inter-basis factors from those
shared values, avoiding independently-rounded intermediate constants. -
Dimension sharing on reduced bases. On any 1-component energy
basis, units mapping to E¹ (energy, mass, temperature) share one
Dimensionobject, and units mapping to E⁻¹ (length, time) share
another. This is not a collision — it encodes the physics of
c = ℏ = 1. Consequently,planck_mass(1).to(planck_energy)→ 1
andplanck_length(1).to(planck_time)→ 1 are both valid
conversions.
v1.3.1
Added
-
Photometric luminance units — 4 new SI-basis ILLUMINANCE units:
nit(nt) — 1 cd/m², the SI-coherent luminance unitstilb(sb) — 1 cd/cm² = 10,000 cd/m²lambert(La) — (1/π) cd/cm² ≈ 3183.1 cd/m²apostilb(asb) — (1/π) cd/m² ≈ 0.3183 cd/m²
All four carry
base_formwithprefactorrelative tocd·m⁻²and
same-basis conversion edges (nit→lux,stilb→nit,lambert→nit,
apostilb→nit). No cross-basis edges needed — these are SI-basis
units because their dimensional formula involvescandela, which
belongs exclusively to the SI basis (CGS has no luminous intensity
component). -
TestPhotometricConversions— 6 new tests coveringstilb↔nit,
lambert→nit,apostilb→nit,stilb→lux(multi-hop), and
phot→stilb(cross-validation). -
Disposition comment on
photinucon/units.pyexplaining why it
uses SI-basis ILLUMINANCE despite being conventionally called "CGS". -
Deferral comment on ESU↔EMU cross-family conversion in
ucon/graph.py,
noting that the bridge requires promotingCGS_EMUto a 4-component
basis and is scheduled for v1.4.0 (basis isomorphisms release). See
docs/internal/IMPLEMENTATION_basis_isomorphisms.md.
Notes
-
Cross-basis edge audit (24/24 CGS→SI, 7/7 SI→CGS). All atomic
CGS-family units were verified to have correct bidirectional edges in
the default graph. No missing edges found. -
ESU↔EMU cross-family conversion deferred to v1.4.0. Requires
promotingCGS_EMUto a 4-component basis (L, M, T, Φ),
redefining ~15 dimension vectors, and adding a quantity-dependent
CGS_ESU_TO_CGS_EMU4×4 transform. This is a refactoring-scale
change best done while the drift detector is still active as a safety
net.