Skip to content

ucon.graph

ConversionGraph implementation, ContextVar-based scoping, and conversion exceptions.

ucon.graph — backward-compatibility shim.

All functionality has moved to :mod:ucon.conversion. This module re-exports everything so existing from ucon.graph import ... statements continue to work.

DimensionMismatch

Bases: Exception

Raised when attempting to convert between incompatible dimensions.

ConversionNotFound

Bases: Exception

Raised when no conversion path exists between units.

CyclicInconsistency

Bases: Exception

Raised when adding an edge creates an inconsistent cycle.

Graph dataclass

Registry of conversion morphisms between units.

Stores edges between Unit nodes (partitioned by Dimension) and between UnitProduct nodes (for composite unit conversions like joule → watt_hour).

Supports: - Direct edge lookup - BFS path composition for multi-hop conversions - Factorwise decomposition for UnitProduct conversions - Graph-local unit name resolution (v0.7.3+) - Optional BasisGraph for cross-basis validation (v0.8.3+)

package_constants property

Constants materialized from loaded packages.

Returns

tuple[Constant, ...] All constants from loaded packages, in load order.

add_edge(*, src, dst, map, basis_transform=None, overwrite=False)

Register a conversion edge (and its inverse) on this graph.

add_edge is the primary mechanism for extending a :class:Graph with domain-specific units. Call it on either the default package graph (ucon.graph.default_graph) or your own Graph instance to wire new units into the conversion system.

For every forward edge src → dst registered with :class:Map m, the inverse dst → src is automatically registered using m.inverse(). Adding an inconsistent inverse (one whose round-trip is not the identity) raises :exc:CyclicInconsistency. Adding an edge between units of different dimensions without a basis_transform raises :exc:DimensionMismatch.

Parameters

src : Unit or UnitProduct Source unit expression. Both plain :class:Unit instances and composite :class:UnitProduct expressions are accepted; the graph dispatches to the appropriate edge-type internally. dst : Unit or UnitProduct Destination unit expression. map : Map The conversion morphism src → dst. Typically :class:LinearMap for pure scalings, :class:AffineMap for offset conversions (e.g., °C ↔ K), or :class:LogMap for logarithmic units (dB, nepers). basis_transform : BasisTransform or ConstantBoundBasisTransform, optional For cross-basis edges (e.g., CGS ↔ SI when dimensional exponents differ, as for electromagnetic units). The src unit is rebased to the dst's dimension and the edge connects the rebased unit to dst. Most edges do not need this. overwrite : bool, optional When True, remove any existing forward and inverse edges for the (src, dst) pair before inserting the new ones. This bypasses the cyclic-consistency check by clearing the prior state first. Used by :func:_merge_conversion_graphs under :attr:ConflictPolicy.PREFER_OTHER. Default False.

Raises

DimensionMismatch src and dst have different dimensions and no basis_transform was supplied. CyclicInconsistency A reverse edge exists already and composing it with the new map does not yield the identity (only when overwrite=False). This protects against silently registering incompatible conversions. NoTransformPath The graph has a :class:BasisGraph attached and no path exists between the source and destination bases.

Examples

Register a simple linear conversion on the default graph::

from ucon import units, LinearMap
from ucon.graph import default_graph

default_graph.add_edge(
    src=units.mile,
    dst=units.kilometer,
    map=LinearMap(1.609344),
)

Register an affine temperature conversion::

from ucon import units, AffineMap
from ucon.graph import default_graph

default_graph.add_edge(
    src=units.celsius,
    dst=units.kelvin,
    map=AffineMap(a=1.0, b=273.15),
)

Register a composite-unit edge (joule ↔ kWh)::

from ucon import units, LinearMap
from ucon.graph import default_graph

kwh = units.kilo * units.watt * units.hour
default_graph.add_edge(
    src=units.joule,
    dst=kwh,
    map=LinearMap(1 / 3.6e6),
)
See Also

Graph.convert : Path-find between registered units. Graph.connect_systems : Bulk-add cross-basis edges. Number.to : User-facing conversion that delegates to this graph.

Notes

Stability: add_edge is part of the public API of :mod:ucon as of v1.3.0. Its keyword-only signature (src, dst, map, basis_transform) is considered stable and will not change in a backwards-incompatible way without a major-version bump.

connect_systems(*, basis_transform, edges)

Bulk-add edges between systems.

Parameters

basis_transform : BasisTransform or ConstantBoundBasisTransform The transform bridging the two systems. edges : dict Mapping from (src_unit, dst_unit) to Map.

list_rebased_units()

Return all rebased units in the graph.

Returns

dict[Unit, list[RebasedUnit]] Mapping from original unit to its RebasedUnit(s).

list_transforms()

Return all BasisTransforms active in the graph.

Returns

list[BasisTransform] Unique transforms used by rebased units.

edges_for_transform(transform)

Return all edges that use a specific BasisTransform.

Parameters

transform : BasisTransform The transform to filter by.

Returns

list[tuple[Unit, Unit]] List of (original_unit, destination_unit) pairs.

register_unit(unit)

Register a unit for name resolution within this graph.

Populates both case-insensitive and case-sensitive registries with the unit's name, shorthand, and aliases.

Parameters

unit : Unit The unit to register.

resolve_unit(name)

Resolve a unit string in graph-local registry.

Only performs case-sensitive matching. Case-insensitive resolution is handled by the global _lookup_factor() which applies the correct priority chain (priority aliases → scale-prefix decomposition → case-sensitive → case-insensitive).

Returning a case-insensitive match here would short-circuit scale-prefix decomposition, causing e.g. GB (giga-byte) to resolve as Gb (gilbert) or nm (nano-meter) as nautical mile (alias NM).

Parameters

name : str The unit name or alias to resolve.

Returns

tuple[Unit, Scale] | None (unit, Scale.one) if found, None otherwise. Caller should fall back to global registry if None.

contains_unit_by_identity(unit)

Return True iff unit is registered in this graph by identity.

For a plain :class:Unit, returns True iff the exact object (is-equal) appears as a node in any per-dimension edge map. For a :class:UnitProduct, returns True iff every factor's unit satisfies the same condition.

Unlike value-based dict lookup (which uses __eq__ / __hash__), this method iterates with is-comparison so that two structurally-equal :class:Unit instances with the same (name, dimension, aliases) are not considered the same node.

This is the semantics required by strict=True source-unit resolution in :meth:ucon.Number.to: a :class:Number whose unit is value-equal but identity-distinct from any node in the active graph must surface a :exc:UnitDefinitionMismatch rather than silently re-resolve via name.

Parameters

unit : Unit or UnitProduct The unit expression to check.

Returns

bool True iff every constituent :class:Unit is is-equal to a node already registered in _unit_edges.

copy()

Return a deep copy suitable for extension.

Creates independent copies of edge dictionaries and name registries. The returned graph can be modified without affecting the original.

Returns

Graph A new graph with copied state.

register_context(ctx)

Register a named conversion context for serialization.

Parameters

ctx : ConversionContext A frozen context to store by name.

with_package(package)

Return a new graph with this package's units and edges added.

Creates a copy of this graph and applies the package contents. The original graph is not modified.

Parameters

package : UnitPackage Package containing unit and edge definitions.

Returns

Graph New graph with package contents added.

Example

from ucon.packages import load_package aero = load_package("aerospace.ucon.toml") graph = get_default_graph().with_package(aero)

convert(*, src, dst)

Find or compose a conversion Map from src to dst.

Parameters

src : Unit or UnitProduct Source unit expression. dst : Unit or UnitProduct Destination unit expression.

Returns

Map The conversion morphism.

Raises

DimensionMismatch If src and dst have different dimensions. ConversionNotFound If no conversion path exists.

to_toml(path, *, kinds=None, formulas=None, aspects=None)

Export this graph to a TOML file.

Parameters

path : str or Path Destination file path. kinds : KindLattice or None Optional kind lattice to serialize as [[kinds]] sections. formulas : FormulaRegistry or None Optional formula registry to serialize as [[formulas]] sections. aspects : AspectForest or None Optional aspect forest to serialize as [[aspects]] sections.

Raises

ImportError If tomli_w is not installed.

from_toml(path, *, strict=True) classmethod

Import a graph from a TOML file.

Parameters

path : str or Path Source file path. strict : bool When True (default), raise GraphLoadError if any edge references an unresolvable unit. When False, silently skip unresolvable edges.

Returns

Graph The reconstructed graph.

__eq__(other)

Compare graphs for structural equality.

Two graphs are equal if they have the same registered unit names, the same edge conversions (within tolerance), the same loaded packages, the same constants, and the same contexts.

get_default_graph()

Get the current conversion graph.

Priority: 1. Context-local graph (from using_conversion_graph) 2. Active :class:~ucon.system.UnitSystem's conversion_graph

using_conversion_graph(graph)

Context manager for scoped :class:Graph override.

Sets both the conversion graph and parsing graph contexts, so that name resolution and conversions both use the same graph.

Usage::

with using_conversion_graph(custom_graph):
    result = value.to(target)        # uses custom_graph
    unit = parse_unit("custom_unit") # resolves in custom_graph

Parameters

graph : Graph The graph to use within this context.

Yields

Graph The same graph passed in.