Skip to content
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

Unitless typeof and eltype #22216

Open
ChrisRackauckas opened this issue Jun 5, 2017 · 15 comments
Open

Unitless typeof and eltype #22216

ChrisRackauckas opened this issue Jun 5, 2017 · 15 comments
Labels
feature Indicates new feature / enhancement requests types and dispatch Types, subtyping and method dispatch

Comments

@ChrisRackauckas
Copy link
Member

ChrisRackauckas commented Jun 5, 2017

A very common operation when trying to write package which works with unitful quantities is getting the unitless element-type. This is required to find the right eps or set the tolerance to the correct Number type. However, I am not sure of a completely generic way to handle this. typeof(first(one(u))) works well in a pinch, but I am noticing an issue with GPUArrays which do not have indexing and thus don't have first defined. So the only way I know of to always get the unitless element type is eltype(u./u), which has an extra operation just to grab the type.

Is there a generic function to strip the units off of a type? Some unitless(T) to get whatever the type of the non-unit part is? That would be a clear way to implement this.

@ChrisRackauckas ChrisRackauckas changed the title Unitless Eltype Unitless eltype Jun 5, 2017
@ChrisRackauckas ChrisRackauckas changed the title Unitless eltype Unitless typeof and eltype Jun 5, 2017
@timholy
Copy link
Member

timholy commented Jun 5, 2017

Even eltype(u./u) doesn't work:

julia> using Unitful

julia> x = 3u"m"
3 m

julia> x/x
1.0

which is Float64 rather than Int.

Unitful has ustrip. If you're asking about supporting multiple unit frameworks, then this doesn't seem to be a Julia issue, it's more something for the package ecosystem to agree on. There could be a UnitStubs.jl package that all of them import and extend; the stubs package would contain only the fallbacks, like

ustrip(x::Number) = x

and then each package could specialize it for the types defined in the package.

@ChrisRackauckas
Copy link
Member Author

Unitful has ustrip. If you're asking about supporting multiple unit frameworks, then this doesn't seem to be a Julia issue, it's more something for the package ecosystem to agree on. There could be a UnitStubs.jl package that all of them import and extend

Wouldn't this be useful in Base for the same reason as oneunit? Yes, the reason for this is because getting all of the different unit packages to work is extremely difficult without this, and I am looking for a good way to not take on a dependency for each library. But, if something like this is useful for unitful types in Base like Dates, then that would be a good solution.

@ChrisRackauckas
Copy link
Member Author

A kind of funny workaround for this is typeof(one(eltype(x))), since one strips units unlike oneunit. I'm not sure how universally applicable it is though.

@timholy
Copy link
Member

timholy commented Jun 5, 2017

Yes, if one is properly implemented that should work. If it's not properly implemented, it should be fixed anyway...

If you want to go the UnitStubs way, the main point is that JuliaDiffEq packages could just say using UnitStubs and not need to know which package is being used by the user to input values.

I agree these are conceptually relevant for dates, but AFAICT all dates objects are fundamentally Int64, so I'm not sure generic eltype manipulations are necessary. The main reason we added oneunit is that people were misusing one, conflating the additive generator with the multiplicative identity. I'm not certain further unit manipulations are really necessary? Not that I think it would be a big deal, though.

@ChrisRackauckas
Copy link
Member Author

Yes, if one is properly implemented that should work. If it's not properly implemented, it should be fixed anyway...

Well if that's the case then I'm fine with using it. Would a simple

unitless_eltype(x) = typeof(one(eltype(x)))

be worthy of inclusion into Base?

@StefanKarpinski
Copy link
Member

I think quantity(x) would be a good name for getting the numeric value without the unit. A generic definition could be quantity(x) = x*(one(x)/oneunit(x)). Does not work well for integers though.

@KristofferC
Copy link
Member

Would changing / for ÷ work?

@StefanKarpinski
Copy link
Member

StefanKarpinski commented Jun 5, 2017

It might well do so. I guess as long as uniful quantities behave as expected wrt ÷. Of course there's the issue that multiplying 1m * 1m^{-1} will produce a 1*m^0 whereas what one wants is actually 1 without any unit – unless I'm wrong and Unitful handles this. Perhaps the best approach is just to introduce Base.quantity which acts as the identity on pure numbers and allow unit packages to extend it by unwrapping unitful quantities and returning the wrapped value. I.e. don't try to provide a generic fallback.

@ChrisRackauckas
Copy link
Member Author

unless I'm wrong and Unitful handles this.

Unitful actually does handle this:

using Unitful
1u"m"/1u"m"
# 1.00

Perhaps the best approach is just to introduce Base.quantity which acts as the identity on pure numbers and allow unit packages to extend it by unwrapping unitful quantities and returning the wrapped value. I.e. don't try to provide a generic fallback.

I agree with this because while Unitful special-cased it to work, there's no reason to assume each other units package does. A generic fallback is inherently brittle for this reason, so I agree that it should probably be left up to the units packages to handle the overload.

@TotalVerb
Copy link
Contributor

TotalVerb commented Jun 6, 2017

The usual nomenclature (e.g. mathematica) is that quantity refers to the unitful quantity, not the unitless one.

Nevertheless it's not strictly always possible to strip a unit, because that may be ambiguous or not make sense... taking Dates.CompoundPeriod as an example. This comes up in practice with non-dates also, so as much code as possible should be written in a dimensionally-correct manner and avoid unit stripping. Of course, in cases where oneunit can be defined, presumably the unit-stripping operation can also.

@martinholters
Copy link
Member

I think having quantity (or whatever name is agreed on) along with oneunit makes sense because it allows to formulate some nice invariants, e.g.

  • quantity(oneunit(x)) == one(x)
  • quantity(x) * oneunit(x) == x

My gut feeling is that these equalities should also hold when taking typeof on both sides, but there might be cases where that cannot be made to work, not sure.

@StefanKarpinski
Copy link
Member

If quantity typically means the opposite then maybe unitless would be a better name?

@stevengj
Copy link
Member

stevengj commented Oct 14, 2017

How often does one need to get the value without the unit? The original question was about getting the unitless type, where typeof(one(x)) works just fine. But I guess if we have unitless(T) we might as well have unitless(x) as well.

I don't think we should have unitless_eltype(x), however, since I see no advantage of this vs. unitless(eltype(x)).

@mschauer
Copy link
Contributor

unitless(x) would be also a solution to the zeronounit-question. Especially for arrays one(x) does not work and typeof loses the size information, so unitless(x) would be useful there.

@rashidrafeek
Copy link
Contributor

rashidrafeek commented Jul 24, 2022

Now there is Unitless.jl which can do this:

julia> using Unitful, Unitless

julia> baretype(u"3km/s")
Int64

julia> baretype(u"3.2km/s")
Float64

@brenhinkeller brenhinkeller added types and dispatch Types, subtyping and method dispatch feature Indicates new feature / enhancement requests labels Nov 21, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Indicates new feature / enhancement requests types and dispatch Types, subtyping and method dispatch
Projects
None yet
Development

No branches or pull requests

10 participants