Skip to content

Quantity: constructing quantities #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
lucascolley opened this issue Feb 8, 2025 · 8 comments · Fixed by #9
Closed

Quantity: constructing quantities #7

lucascolley opened this issue Feb 8, 2025 · 8 comments · Fixed by #9

Comments

@lucascolley
Copy link
Member

How must we be able to construct a Quantity?

  • Must Quantity(value, unit) return Q such that Q.value is value, and Q.unit is unit? If so, can we type that without enforcing certain signatures or parameter names which may be difficult for existing libraries to adopt?
  • If not, do we instead want to standardise a QuantityNamespace, of which the Quantity class is a member, but which also has a creation function such as QuantityNamespace.asquantity? Would all existing libraries be happy with exposing such a namespace?
  • Or do we instead leave creation to the unit API, via something like value << unit?
@nstarman
Copy link
Contributor

nstarman commented Feb 9, 2025

  • Must Quantity(value, unit) return Q such that Q.value is value, and Q.unit is unit?

I don't think we want to force the init signature. Stuff like Quantity(value, dtype, unit) also feels perfectly reasonable for a library to have as its init signature but would be incompatible with the API.

  • If not, do we instead want to standardise a QuantityNamespace, of which the Quantity class is a member, but which also has a creation function such as QuantityNamespace.asquantity? Would all existing libraries be happy with exposing such a namespace?

IMO something like this sounds very reasonable. I think the ArrayAPI does this instead of option 1 for similar reasons as mentioned above.

  • Or do we instead leave creation to the unit API, via something like value << unit?

If we want value * unit (and value / unit ) to result in a Quantity that is also technically a constructor. However, I don't think that should be the only way.

@neutrinoceros
Copy link

Must Quantity(value, unit) return Q such that Q.value is value, and Q.unit is unit?

I'd rather have type flexibility on the value and unit arguments, but narrowed types on the output, so I don't think they should generally match. For instance, unit could accept str values, but Q.unit's type should satisfy the Unit protocol.

Stuff like Quantity(value, dtype, unit) also feels perfectly reasonable for a library to have as its init signature but would be incompatible with the API.

Why not mandate that Quantity-implementation have value and unit as their first two arguments (possibly not exposed as keywords), and allow any other additional argument on the condition that it is keyword-only ?

  • If not, do we instead want to standardise a QuantityNamespace, of which the Quantity class is a member, but which also has a creation function such as QuantityNamespace.asquantity? Would all existing libraries be happy with exposing such a namespace?

I don't see why not.

@andrewgsavage
Copy link
Contributor

If not, do we instead want to standardise a QuantityNamespace, of which the Quantity class is a member, but which also has a creation function such as QuantityNamespace.asquantity? Would all existing libraries be happy with exposing such a namespace?

Would this be a single namespace to cover Quantity, Unit, Dimension etc?

@nstarman
Copy link
Contributor

Why not mandate that Quantity-implementation have value and unit as their first two arguments (possibly not exposed as keywords), and allow any other additional argument on the condition that it is keyword-only ?

Why not emulate the Array API and have a constructor function as the API and leave the class constructor up to the libraries?
If Quantity is a dataclass then Quantity(value, unit) makes the most sense. If it's a subclass of ndarray then less so. If it's a subclass of array.array then not much sense... But a separate constructor function asquantity always makes good sense.

@nstarman
Copy link
Contributor

nstarman commented Feb 13, 2025

Would this be a single namespace to cover Quantity, Unit, Dimension etc?

Ah! That's a thorny problem. My initial reaction was to say yes. But then an obvious counter-example arose: if someone makes a Quantity wrapper class! E.g. https://unxt.readthedocs.io/en/latest/, which has a custom Quantity but can / will be able to support different unit backends. Then the namespace would have to be quite dynamic, e.g.

def mywrapper_asquantity(): ...

class QuantityWappingOtherLibraryUnits:
    def __unitful_namespace__(self): -> NameSpace
        namespace = self.unit.__unitful_namespace__()
        namespace.asquantity = mywrapper_asquantity  # ☹ dynamic override
        return namespace

But maybe that's not such a problem...

@andrewgsavage
Copy link
Contributor

Would this be a single namespace to cover Quantity, Unit, Dimension etc?

Ah! That's a thorny problem. My initial reaction was to say yes. But then an obvious counter-example arose: if someone makes a Quantity wrapper class! E.g. https://unxt.readthedocs.io/en/latest/, which has a custom Quantity but can / will be able to support different unit backends. Then the namespace would have to be quite dynamic, e.g.

def mywrapper_asquantity(): ...

class QuantityWappingOtherLibraryUnits:
    def __unitful_namespace__(self): -> NameSpace
        namespace = self.unit.__unitful_namespace__()
        namespace.asquantity = mywrapper_asquantity  # ☹ dynamic override
        return namespace

But maybe that's not such a problem...

IMO that's not a use case we should be considering. The dataframe API has a namespace with dataframe and column so there's precedent for them being under the same namespace

@nstarman
Copy link
Contributor

The dataframe API has a namespace with dataframe and column so there's precedent for them being under the same namespace.

Agreed. Packaging them together is a solveable problem. And there are ways to even make it statically compatible by a custom Protocol object for the use case demonstrated above.

IMO that's not a use case we should be considering.

Strong disagree 😄

I could probably think of more examples. Wrapping quantities and / or their units is not uncommon. As reference, the Array API is not just about vendoring Array classes, but also about making it easy to wrap an array object and have it act like the underlying array. Same here. It should be considered. Thankfully I think we agree there's probably a workable solution.

@andrewgsavage
Copy link
Contributor

Ahh I misunderstood. Yes that's important.

Yea we should be able to do similar to the array API

@lucascolley lucascolley changed the title Constructing quantities Quantity: constructing quantities Mar 12, 2025
@lucascolley lucascolley transferred this issue from quantity-dev/quantity-api Mar 12, 2025
@lucascolley lucascolley linked a pull request Mar 27, 2025 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants