Unitful.jl for physical units

1,133 views
Skip to first unread message

Andrew Keller

unread,
Feb 12, 2016, 3:23:22 PM2/12/16
to julia-users
I'm happy to share a package I wrote for using physical units in Julia, Unitful.jl. Much credit and gratitude is due to Keno Fischer for the SIUnits.jl package which served as my inspiration. This is a work in progress, but I think perhaps a serviceable one depending on what you're doing. 

Like SIUnits.jl, this package encodes units in the type signature to avoid run-time performance penalties. From there, the implementations diverge. The package is targeted to Julia 0.5 / master, as there are some limitations with how promote_op is used in Julia 0.4 (#13803). I decided it wasn't worth targeting 0.4 if the behavior would be inconsistent. 

Some highlights include:
  • Non-SI units are treated on the same footing as SI units, with only a few exceptions (unit conversion method). Use whatever weird units you want.
  • Support for units like micron / (meter Kelvin), where some of the units could cancel out but you don't necessarily want them to.
  • Support for LinSpace and other Range types. Probably there are still some glitches to be found, though.
  • Support for rational exponents of units.
  • Some tests (see these for usage examples).
Please see the documentation for a comprehensive discussion, including issues / to do list, as well as how to add your own units, etc.
Comments and feedback are welcome.

Best,
Andrew Keller

Stefan Karpinski

unread,
Feb 12, 2016, 4:09:02 PM2/12/16
to Julia Users
This is very, very cool. The implementation does seems to suggest some interesting potential way that Base could support this better. Please don't hesitate to bring these up as issues or on julia-dev. Really smooth support for units in Julia is a killer feature.

Tim Holy

unread,
Feb 12, 2016, 4:27:56 PM2/12/16
to julia...@googlegroups.com
I have long been meaning to get around to making SIUnits type-stable under
arithmetic. I've only glanced at your code, but it looks like you just
scratched one big, bad TODO item off my list! Seems like you've included a
number of other nice features, too (many thanks for the microns support, it
will make a difference). I'll be excited to play with this.

Best,
--Tim

On Friday, February 12, 2016 12:23:22 PM Andrew Keller wrote:
> I'm happy to share a package I wrote for using physical units in Julia,
> Unitful.jl <https://www.github.com/ajkeller34/Unitful.jl>. Much credit and
> gratitude is due to Keno Fischer for the SIUnits.jl
> <https://www.github.com/keno/SIUnits.jl> package which served as my
> inspiration. This is a work in progress, but I think perhaps a serviceable
> one depending on what you're doing.
>
> Like SIUnits.jl, this package encodes units in the type signature to avoid
> run-time performance penalties. From there, the implementations diverge.
> The package is targeted to Julia 0.5 / master, as there are some
> limitations with how promote_op is used in Julia 0.4 (#13803)
> <https://github.com/JuliaLang/julia/pull/13803>. I decided it wasn't worth
> targeting 0.4 if the behavior would be inconsistent.
>
> Some highlights include:
>
> - Non-SI units are treated on the same footing as SI units, with only a
> few exceptions (unit conversion method). Use whatever weird units you
> want.
> - Support for units like micron / (meter Kelvin), where some of the
> units could cancel out but you don't necessarily want them to.
> - Support for LinSpace and other Range types. Probably there are still
> some glitches to be found, though.
> - Support for rational exponents of units.
> - Some tests (see these for usage examples).

Tom Breloff

unread,
Feb 12, 2016, 4:42:20 PM2/12/16
to julia-users
Andrew this looks really great.  Thanks for the contribution!

yousef.k...@gmail.com

unread,
Feb 16, 2016, 9:11:52 AM2/16/16
to julia-users
Hello Andrew,

Your work is very promising. If you have ever dealt with oil field equations, then you'll know that units are a headache to deal with.
Your package should make life much easier.

I'm curious, however, as if you'll implement an easier means of defining new units.
Something like a function that takes a unit, its base factor, and its conversion to SI units, then gets it working in Unitful.

Thanks again for your work,
Yousef

Andrew Keller

unread,
Feb 17, 2016, 1:06:12 AM2/17/16
to julia-users
Hi Yousef, 

Yes, I anticipate that there will be a simpler way to add units sometime, probably via a macro. However, my concern first and foremost is to make sure that operations involving units do not fail unexpectedly, particularly where ranges, arrays, and matrices are involved. This is really key for widespread adoption, I think, as well as for my own application. I'd also like to make it so that exact conversions are preserved where possible (see issue #3 on the Github page). In the meantime, you're welcome to submit a pull request for the units you need, or just modify your local copy.

Thanks to everyone for the positive feedback! It looks like there was a recent commit to Julia master that may introduce some breakage—I'll prioritize fixing that once the Mac OS X binary for Julia is updated. I may fix it sooner since I was able to build master.

Best
Andrew

Jake Rosoman

unread,
Feb 17, 2016, 4:43:13 AM2/17/16
to julia-users
Is it possible to talk about abstract types of units like size. e.g `drawline(s::Size) = ...`?

Jeffrey Sarnoff

unread,
Feb 17, 2016, 6:25:37 AM2/17/16
to julia-users
Jake,

Julia's type system is well suited to do just that.  Unitful is focused on units in SI system, things like Meters, Kilograms and Joules.

One approach to abstract units like size, where size may be relative to the number of pixels on a screen or the width of a page,
is define your own type, a kind of size relative to something.  In your example, s is not a unit of measure (strictly speaking);
s is a quantity interpreted in terms of some absolute or relative unit of measure -- 5 pixels, 1/4 page.  Because pixels and pages
are not always the same number of, say, millimeters, using SI units for that abstraction likely is not what you want.

If you want more guidance, please give some more context.

Andrew Keller

unread,
Feb 17, 2016, 1:02:40 PM2/17/16
to julia-users
Hi Jake,

I agree with Jeffrey's response mostly, but want to clarify that Unitful is not strictly focused on the SI system. You'll see that you can use units of acre-feet, very much not an SI unit, which could be useful if you happen to manage a water reservoir in the United States, I guess.

If you were instead asking whether or not you could write methods that dispatch on the dimensions of a unit, the current answer is no, although maybe that could change eventually.

Best,
Andrew

Jake Rosoman

unread,
Feb 17, 2016, 3:59:15 PM2/17/16
to julia-users
Oh yup I thought that was the case but wanted to check.

I've started experimenting with ways to achieve this but I am getting massive amounts of code generated compared to Unitful. I just uploaded uploaded my work so far at https://GitHub.com/jkroso/units.jl. If anyone could help explain the difference in code gen that would be great and I'll try contribute back to Unitful

Jeffrey Sarnoff

unread,
Feb 17, 2016, 4:25:02 PM2/17/16
to julia-users
Jake,

It would be helpful to know more clearly what is the "this" that you are experimenting with "ways to achieve this"?  
I took a glance at your code, and one thing that is happening is you have left some low-hanging abstraction on the table.  That constrains Julia's ability to simplify through organized multidispatch.  I will give it more than a glance overnight -- pretend it all works perfectly, show me what you want to do with it then.

Jake Rosoman

unread,
Feb 17, 2016, 5:10:15 PM2/17/16
to julia-users
Oops thanks for being patient. By "this" I meant "dispatch on abstract units/dimensions". The motivation is to be able to write a function which takes a length without caring what the specific unit of that length is. Be it feet or cm etc. This is to make the intent of the function clearer and enable polymorphism. For example:

direction = 0radians
move(r::Angle) = direction += r
move(l::Length) = translate(direction, l)

Also speed could be defined as Ratio{d<:size, t<:time}. Which would be hard to do using unitful as is

Jeffrey Sarnoff

unread,
Feb 17, 2016, 5:29:19 PM2/17/16
to julia-users
In this unit-land, is length always quantity * unit_of_length? or also may it be proportion * reference_distance (1/4 of the current line length)?
Or -- shall we seek an avenue that somehow is both without being either?

Jake Rosoman

unread,
Feb 17, 2016, 6:29:24 PM2/17/16
to julia...@googlegroups.com, julia-users
Its always the former. I hadn't thought about using proportions. Is that idea related to the idea of delta units which I have seen discussed in the Unitful issue tracker.

Scott Jones

unread,
Feb 18, 2016, 12:15:21 AM2/18/16
to julia-users
Is there anything to convert to/from Julia's UT seconds (which are tied to the Earth's rotation), and SI seconds?

Eric Forgy

unread,
Feb 18, 2016, 12:26:32 AM2/18/16
to julia-users
This is cool (Unitful.jl and SIUnits.jl). I imagine similar work can be done for currencies?

Stefan Karpinski

unread,
Feb 18, 2016, 10:10:54 AM2/18/16
to Julia Users
That requires an implementation of ΔT, which I don't think we have unless ICU has it.

Jeffrey Sarnoff

unread,
Feb 18, 2016, 11:41:25 AM2/18/16
to julia-users
Money (fiat value) as an econophysical dimension, with currencies the given units of measure and exchange rates the interconversion ratios with bid-asked spreads the ratios' associated uncertainties.  The interconversion ratios & uncertainties are dynamic rather than internal system characteristics. 

    (  atsign other, See the need for concretable prototypes that then are further elaborable, and each elaboration just works as a distinct concrete leaf type? ) 

Kristoffer Carlsson

unread,
Feb 18, 2016, 12:51:19 PM2/18/16
to julia-users
Just for your info, you likely exclude a lot of non native speakers (like me) from understanding your point when you go 17th century poet style.

Adjust your language to the audience and all that..

Jeffrey Sarnoff

unread,
Feb 18, 2016, 2:39:16 PM2/18/16
to julia-users
Kristoffer,

I am glad that you said this.  In the future, I will write and be respectful of that.

Kind regards,

Jeffrey Sarnoff

Jeffrey Sarnoff

unread,
Feb 18, 2016, 3:30:56 PM2/18/16
to julia-users
I am thinking about how Currencies would fit with software for handling SI and other physical dimensions and the usual units of measure.
The following approach seems workable and (mostly) not disruptive to the working and evolving code Andrew Keller offers the community.

A currency behaves as a representation of, or a stand-in or proxy for all the banknotes, sovereign wealth and other property interests of a nation.
Two or more currencies for which there is a market that allows one to be converted into another at an agreed upon, current rate of exchange are not that different from two or more units of length or units of time that are allowed to be converted into one another using a predefined multiplicative ratio.

     (now, 1.0 US Dollar = 8.45536 Swedish Krona, refreshing the page, now 1.0 US Dollar = 8.45518 Swedish Krona)

The most impactful distinction is that rates of exchange are dynamic while the physical conversion factors (BIPM standards, CODATA values) persist.
However currencies may be accommodated, it must protect our ability work with units with ease and with invisibly amortized processing overhead.
So, the request that gets the appropriate exchange rate to use when converting from one currency to another must come from the Units code in a way that does not require other physical unit conversions to operate any differently.

One of the things on Andrew's future to do list is allow measures and their uncertainties to be used and to interact respecting the uncertainty math.
In any currency exchange trade, there is a buyer and a seller.  Usually, when they (or their requests) meet, they disagree.  The buyer wants to pay as little as possible and the seller wants to receive as much as possible. That separation is called the bid-ask spread (the buyer bids, the seller asks); almost always it is very, very small relative to the total value being exchanged.  The uncertainties associated with physical measures and conversions usually are very, very small relative to the total quantity being determined or the value being mapped into different units.  When uncertainty management is introduced, it is conceivable that currency support could take advantage of that analogous role.

Mauro

unread,
Feb 18, 2016, 3:56:27 PM2/18/16
to julia...@googlegroups.com
Concerning exchange rates, this is a good read:
http://www.mathstat.dal.ca/~selinger/accounting/tutorial.html#4
In particular the referenced section.

Tamas Papp

unread,
Feb 19, 2016, 3:13:10 AM2/19/16
to julia...@googlegroups.com
Exchange rates are prices, and thus are (1) dynamic (and can be very
volatile even in the short run) and (2) dispersed: you observe multiple
exchange rates depending on quantity, location, assets exchanged
(banknotes or other claims).

It is very difficult to make a "general" library for exchange rates, not
because of the coding, but because of conceptual issues. Specific
applications (eg accounting with a given source of "official" exchange
rates, or a trader in a specific market) allow you to make further
assumptions and simplify.

In particular, I don't see how exchange rates fit with SI units at all.

Best,

Tamas

Jeffrey Sarnoff

unread,
Feb 19, 2016, 4:57:55 AM2/19/16
to julia-users
Jake,

code_warntype( function, (argtype, ..) ) is helpful, and so is the Benchmarks package https://github.com/johnmyleswhite/Benchmarks.jl

for Unitful,

julia> code_warntype(+,(typeof(1m),typeof(1m)))
Variables:
  #self#::Base.#+
  x::Unitful.RealQuantity{Int64,Unitful.UnitData{(m,)}}
  y::Unitful.RealQuantity{Int64,Unitful.UnitData{(m,)}}

Body:
  begin  # /home/jas/.julia/v0.5/Unitful/src/Unitful.jl, line 321:
      return $(Expr(:new, Unitful.RealQuantity{Int64,Unitful.UnitData{(m,)}}, :((Base.box)(Int64,(Base.add_int)((top(getfield))(x::Unitful.RealQuantity{Int64,Unitful.UnitData{(m,)}},:val)::Int64,(top(getfield))(y::Unitful.RealQuantity{Int64,Unitful.UnitData{(m,)}},:val)::Int64)))))
  end::Unitful.RealQuantity{Int64,Unitful.UnitData{(m,)}}

and
@benchmark 1m+1cm shows 0 bytes were allocated

for your code

julia> code_warntype(+,(typeof(1m),typeof(1m)))
Variables:
  a::Meter{1,0}
  b::Meter{1,0}
  mag::Int64
  aval::Any
  bval::Any
  #s4::Int64

Body:
  begin  # none, line 4:
      GenSym(0) = (Main.normalize)(a::Meter{1,0},b::Meter{1,0})::Tuple{Int64,Any,Any}
      #s4 = 1
      GenSym(4) = (Base.getfield)(GenSym(0),1)::Any
      GenSym(5) = (Base.box)(Base.Int,(Base.add_int)(1,1))
      mag = GenSym(4)
      #s4 = GenSym(5)
      GenSym(6) = (Base.getfield)(GenSym(0),2)::Any
      GenSym(7) = (Base.box)(Base.Int,(Base.add_int)(2,1))
      aval = GenSym(6)
      #s4 = GenSym(7)
      GenSym(8) = (Base.getfield)(GenSym(0),3)::Any
      GenSym(9) = (Base.box)(Base.Int,(Base.add_int)(3,1))
      bval = GenSym(8)
      #s4 = GenSym(9) # none, line 5:
      return call((top(apply_type))(Main.Meter,1,mag::Int64)::Type{_<:Meter{1,magnitude}},aval + bval::Any)::Meter{d,magnitude}
  end::Meter{d,magnitude}

and 
@benchmark 1m+1m show 48 bytes allocated

Anything code_warntype highlights indicates type ambiguity or lack of type specialization; that derails multidispatched specialization, and so costs time. And code that does not allocate memory is, in a sense, doing more with less -- or doing more earlier, so it does less at runtime.


Tom Breloff

unread,
Feb 19, 2016, 7:50:55 AM2/19/16
to julia...@googlegroups.com
Currencies could be applicable, but not converting between countries (for practical reasons already mentioned). The use could be: 

100 cent == 1 dollar
1000 dollar == 1 kdollar
etc

1 == 100 percent
100 bip == 1 percent
etc

1 dollar * 1 percent == 1 cent

I could see some nice applications for this stuff (including replacing a special "Price" type that I have)

Jeffrey Sarnoff

unread,
Feb 19, 2016, 9:41:21 AM2/19/16
to julia-users
Then also, any named store or carrier of value (as a unique id) and any named store or carrier of value with named invariant subvaluations.

For interconversion of units of temperature to be unexceptional, interconversion logic would 'know' (predispatch on @generated type info) to apply a linear transformation; for many other physical units interconversion within a physical dimension would (and does) 'know' to apply a simple scaling (a linear transformation without the translation).  Presupposing this, what additional things would become supportable immediately if we augment that predispatch with the ability to apply an affine transformation (any one or more of {translate, scale, rotate})?

Tom Breloff

unread,
Feb 19, 2016, 10:31:07 AM2/19/16
to julia-users
Jeffrey:  I love trying to figure out what you're saying... it's a good replacement for a daily crossword puzzle.  :P

Yes if it was relatively easy to define affine transformations between units, then this could possibly open up other more interesting uses in computer graphics, etc.

Scott Jones

unread,
Feb 19, 2016, 2:13:21 PM2/19/16
to julia-users
On Friday, February 19, 2016 at 10:31:07 AM UTC-5, Tom Breloff wrote:
Jeffrey:  I love trying to figure out what you're saying... it's a good replacement for a daily crossword puzzle.  :P

Yes, I second that!  I always found Jeffrey's language quite refreshing, but then again, I'm significantly challenged in my on-line communications skills myself,
so maybe that's not a great recommendation!

Andrew Keller

unread,
Sep 6, 2016, 3:02:16 PM9/6/16
to julia-users
Unitful.jl has recently undergone a major update with enhanced functionality. If the previous version was an alpha release, this is something like a beta release before the package is registered and tagged. I intend to do that soon so that others can think about using the package more seriously, probably shortly after Julia 0.5.0 is released. Now is a good time to provide feedback if you find bugs or think there are some trouble spots that would prevent the package from being useful to you. 

I am sorry to inconvenience existing users with syntax changes, but please raise an issue or post here if you have any trouble. I hope the revised documentation explains any questions you may have, and as usual, test/runtests.jl can be useful to figure out how things are working. Once the package is registered and tagged, I will be more conscientious regarding breaking changes.

Enjoy,
Andrew Keller

Bart Janssens

unread,
Sep 6, 2016, 3:28:59 PM9/6/16
to julia...@googlegroups.com
Hi Andrew,

Unitful.jl is awesome, many thanks for this work, I intend to use it for all my classroom notebooks. I especially like the fact that it supports units with offsets, like the different temperature scales, and the fact that it keeps derived units like J displayed as such in the output.

The new version seems to have an exporting issue, unless I did something wrong:
https://github.com/ajkeller34/Unitful.jl/issues/14

Cheers,

Bart
Reply all
Reply to author
Forward
0 new messages