From e0b9ace44e0d465847e81b376bf25ce0baf7e478 Mon Sep 17 00:00:00 2001 From: Eskild Schroll-Fleischer <54531016+nneskildsf@users.noreply.github.com> Date: Fri, 3 Jan 2025 08:23:15 +0100 Subject: [PATCH] Update registry.py Calculate factor by first constructing the scaling fraction, eliminating terms that appear in numerator and denominator and then finally evaluating the fraction. --- pint/facets/plain/registry.py | 50 +++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/pint/facets/plain/registry.py b/pint/facets/plain/registry.py index 76d310e4c..e45388513 100644 --- a/pint/facets/plain/registry.py +++ b/pint/facets/plain/registry.py @@ -897,11 +897,44 @@ def _get_root_units( except KeyError: pass + accumulators: dict[str | None, int] = defaultdict(int) - accumulators[None] = 1 - self._get_root_units_recurse(input_units, 1, accumulators) + fraction: dict[str, dict[Decimal|Float, Decimal|int]] = dict(numerator=dict(), denominator=dict()) + self._get_root_units_recurse(input_units, 1, accumulators, fraction) + + # Identify if terms appear in both numerator and denominator + def terms_are_unique(fraction): + for n_factor, n_exp in fraction["numerator"].items(): + if n_factor in fraction["denominator"]: + return False + return True + # Cancel out terms where factor matches + while not terms_are_unique(fraction): + for n_factor, n_exponent in fraction["numerator"].items(): + if n_factor in fraction["denominator"]: + if n_exponent >= fraction["denominator"][n_factor]: + fraction["numerator"][n_factor] -= fraction["denominator"][n_factor] + del fraction["denominator"][n_factor] + continue + for d_factor, d_exponent in fraction["denominator"].items(): + if d_factor in fraction["numerator"]: + if d_exponent >= fraction["numerator"][d_factor]: + fraction["denominator"][d_factor] -= fraction["numerator"][d_factor] + del fraction["numerator"][d_factor] + continue + + factor = 1 + for n_factor, n_exponent in fraction["numerator"].copy().items(): + if n_exponent == 0: + del fraction["numerator"][n_factor] + else: + factor *= n_factor**n_exponent + for d_factor, d_exponent in fraction["denominator"].copy().items(): + if d_exponent == 0: + del fraction["denominator"][d_factor] + else: + factor /= d_factor**d_exponent - factor = accumulators[None] units = self.UnitsContainer( {k: v for k, v in accumulators.items() if k is not None and v != 0} ) @@ -948,7 +981,7 @@ def get_base_units( # TODO: accumulators breaks typing list[int, dict[str, int]] # So we have changed the behavior here def _get_root_units_recurse( - self, ref: UnitsContainer, exp: Scalar, accumulators: dict[str | None, int] + self, ref: UnitsContainer, exp: Scalar, accumulators: dict[str | None, int], fraction: dict[str, dict[Decimal|float, Decimal|int]] ) -> None: """ @@ -962,13 +995,14 @@ def _get_root_units_recurse( if reg.is_base: accumulators[key] += exp2 else: - # Use division operator for division instead of exponentiation + # Build numerator and denominator + #print(reg.converter.scale, exp2) if exp2 < 0: - accumulators[None] /= reg.converter.scale**abs(exp2) + fraction['denominator'][reg.converter.scale] = fraction['denominator'].get(reg.converter.scale, 0)-exp2 else: - accumulators[None] *= reg.converter.scale**abs(exp2) + fraction['numerator'][reg.converter.scale] = fraction['numerator'].get(reg.converter.scale, 0)+exp2 if reg.reference is not None: - self._get_root_units_recurse(reg.reference, exp2, accumulators) + self._get_root_units_recurse(reg.reference, exp2, accumulators, fraction) def get_compatible_units(self, input_units: QuantityOrUnitLike) -> frozenset[UnitT]: """ """