yunruse / Noether

Just another units package
MIT License
9 stars 0 forks source link

Composed units #42

Closed yunruse closed 1 year ago

yunruse commented 1 year ago

Feature suggestion

I believe this is one important feature above all else that needs developing.

km ** 2, cm / s, lightyear / day. These are all common composed units used by people, and currently, for display purposes, Noether does not support them, which is a great shame.

A ComposedUnit should represent a combination of Units, on the following operations:

In essence, any algorithm used for Dimension can be reused here: a dictionary of items and exponents. It is effectively changing the basis vectors on dimension space. This dictionary-of-items-and-exponents should be made into an internal class Multiplication, such that it may be shared by Dimension and ComposedUnit.

CompusedUnit should function as any other Unit, albeit with an internal accessible Multiplication class. It wouldn't hurt to test on AffineUnit, just to make sure unit subclasses still get along.

The syntax for explicitly making a ComposedUnit takes a dictionary and optional names and units.

Note that this does NOT include committing any ComposedUnit units to the namespace.

Units themselves have names, and composed units, as a rule, derive their names. Hence, I will not modify the Unit class.

The feature will require special handling for named composed units such as mph or kmph. It would help that Catalogue becomes instead a more suitably generic dict[ClassType[c], dict[str, c]]. That way these special-case lookups are super-quick.

We can use the instantiation signature:

class ComposedUnit(Multiplication[Unit]):
    def __init__(
        unit: 'ComposedUnit' | Multiplication[Unit],
        names: str | List[str] | None,
        units: str | List[str] | None,
    ): pass

Hehe. ): pass. That's how I feel about development right now.

yunruse commented 1 year ago

The case for ComposedUnit becomes ever-larger with errors such as:

>>> knot / (mile / second)
0.0003196609577843174   # dimensionless
>>> knot
knot  # speed, 0.5144444444444445 mi / s
yunruse commented 1 year ago

As of 88bfdf6e2178c31c27dd896b5580f36020de60ae and merging the composed branch into develop, this issue has been closed. Dimension has been changed to subclass the new Multiplication, which serves ComposedUnit too.

Now, any *, / or ** on a Unit will create a ComposedUnit where logical (eg Unit * Unit, Unit ** int).

In addition, the same hack for Measure @ Unit & Unit has been applied for these operations, such that the default order of operations, (acre @ meter) * meter is interpreted as acre @ (meter * meter).

See it in action:

>>> c @ mile/hour
670616629.3843951 mi / hr  # speed
>>> meter**2
meter**2  # area

The display issue above is still lingering – it's a problem with the unit context Info handlers. This still needed fixing, but will be considered a separate issue now.