[HN Gopher] Scientific computing with confidence using typed dim...
___________________________________________________________________
Scientific computing with confidence using typed dimensions
Author : g0xA52A2A
Score : 71 points
Date : 2024-11-21 10:26 UTC (12 hours ago)
(HTM) web link (laurentrdc.xyz)
(TXT) w3m dump (laurentrdc.xyz)
| cosmic_quanta wrote:
| Author here, thanks for posting!
|
| On the Haskell Discourse [0], someone posted about another
| Haskell library, units [1], which allows to define unit systems
| NOT limited to the 7 physical dimensions. For example, adding a
| base dimension for currency would be nice to model electricity
| prices in currency per energy.
|
| [0]: https://discourse.haskell.org/t/blog-post-scientific-
| computi...
|
| [1]: https://hackage.haskell.org/package/units
| readthenotes1 wrote:
| Dimension analysis is a super power in applied math. I was
| first taught it in chemistry, but was able to use it to avoid
| tripping myself up on just about every real world math problem
| I did...
|
| (And I too changed an implementation to add units of measure to
| a system. It took a few days, and caused some grief with
| downstream system developers. But we also avoided the Mars
| Climate Orbiter experience in production )
| toxik wrote:
| I was so disappointed that this turned from Python to Haskell, I
| would just _love_ to have something like this in Python. Even
| just type annotations.
|
| A similar thing I have been thinking about doing in robotics
| contexts is to annotate vectors and transformation matrices with
| their reference frames. So you can only matrix-vector multiply
| between compatible types: `object_in_world = camera_to_world @
| object_in_camera`. It can be done somewhat in C++.
| cosmic_quanta wrote:
| Something I briefly mention in the post is pint [0] for Python,
| but unfortunately, I don't think dimensions can be specified
| via type annotations.
|
| At least you can check the input of functions at runtime [1].
|
| [0]: https://github.com/hgrecco/pint
|
| [1]:
| https://pint.readthedocs.io/en/stable/advanced/wrapping.html
| kardos wrote:
| Could this be done with an IDE plugin (for Pycharm or whatnot)?
| It is tedious to go though code and carefully annotate each
| variable and run so many compile test, read error, revise,
| cycles. It should be possible on a per function basis to
| statically check how each variable relates to the inputs and
| outputs, and just annotate those. Then the plugin could solve
| the units for each item, and only ask for an annotation where
| needed to resolve an ambiguous case.
| ttshaw1 wrote:
| Check out sympy.physics.units. I've been using it for unit-
| checking in my symbolic work for a few years.
|
| https://docs.sympy.org/latest/modules/physics/units/index.ht...
| librasteve wrote:
| This is a great article and I like that you are highlighting the
| benefits of Dimensions as Types.
|
| While Raku is less popular than Python, it does have deep roots
| in Haskell and strong types (the first Raku / Perl6 parser - PUGS
| - was written in Haskell and all the early devs were encouraged
| to learn Haskell first).
|
| Similar concepts are used in these Raku modules... which provide
| dimensional analysis and marry types to dimensions.
|
| - https://raku.land/zef:librasteve/Physics::Measure
|
| - https://raku.land/zef:librasteve/Physics::Unit
|
| I had some fun with making this example of using these Raku
| modules with Jupyter https://rakujourney.wordpress.com/wp-
| content/uploads/2023/08...
|
| [disclaimer: I am the author of these modules]
|
| Raku is also good at Slangs (Sublanguages) and unicode, so these
| tools can be used in a very intuitive way:
| #!/usr/bin/env raku use Physics::Constants; use
| Physics::Measure :ALL; say ~; #1.054571817e-34 J.s
| my \l = 2.5nm; say "Wavelength of photon (l) is " ~l;
| my \n = c / l; say "Frequency of photon (n) is "
| ~n.in('petahertz'); my \Ep = * n; say "Energy
| of photon (Ep) is " ~Ep.in('attojoules'); Wavelength
| of photon (l) is 2.5nm Frequency of photon (n) is 119.92PHz
| Energy of photon (Ep) is 79.46aJ
| fph wrote:
| Also in Julia: https://painterqubits.github.io/Unitful.jl/stable/
| zzbn00 wrote:
| There is a good dimensional analysis package `ezunits` in Maxima.
| Very useful for working through equations and then sometimes
| generating the c/fortran code directly from Maxima is fine.
| simiones wrote:
| Would this work for something like linear algebra? Could it
| support multiplying two 3x3 matrices where each cell can have a
| different dimension, and only work if they are compatible?
| cosmic_quanta wrote:
| That's a great question.
|
| Haskell has arrays and matrices of homogeneous types, so it
| wouldn't work by default.
|
| If you needed this kind of functionality, you would have to
| create your own Matrix type which would look very similar to a
| 9-tuple, and then define matrix multiplication. It would then
| be possible to encode the dimensional constraints in the type
| signature, but it's already quite involved for 2x2 matrices:
| data Mat2 a b c d = MkMatrix a b c d
| matmul :: ???? -- good luck writing this type signature
| matmul (MkMatrix a11 a12 a21 a22) (MkMatrix b11 b12 b21 b22)
| = MkMatrix ((a11 * b11) + (a12 * b21))
| ((a11 * b12) + (a12 * b22)) ((a21 * b11)
| + (a22 * b21)) ((a21 * b12) + (a22 *
| b22))
|
| I can't imagine for 3x3.
| simiones wrote:
| Thanks for the sample! I was asking because it seems to me
| this is always an interesting example of a very common and
| very well defined formal operation where nevertheless type
| systems are extremely cumbersome to use, but I'm always
| curious if there is some solution that I'm missing.
|
| I wonder if there is any plausible approach that would work
| closer to how units are treated in physics calculations - not
| as types, but as special numerical values, more or less
| (values that you can multiply/divide by any other unit, but
| can only add/subtract with themselves).
| cosmic_quanta wrote:
| > ... not as types, but as special numerical values, more
| or less (values that you can multiply/divide by any other
| unit, but can only add/subtract with themselves).
|
| What's so cool about `dimensional` is that the types work
| the way you describe!
|
| Normally, in Haskell, the addition and multiplication
| functions are typed like so: (+) ::
| Double -> Double -> Double (*) :: Double -> Double
| -> Double
|
| With `dimensional`, addition looks like:
| (+) :: Quantity d Double -> Quantity d Double -> Quantity d
| Double
|
| which means you can't add quantities with incompatible
| dimensions, so far so good. But multiplication looks like:
| (*) :: Quantity d1 Double -> Quantity d2 Double -> Quantity
| (d1 * d2) Double
|
| That is, the dimensions of the result of multiplication (d1
| * d2) follow the physics meaning of multiplication. What
| can look confusing is that (d1 * d2) is a type-level
| calculation which is run at compile-time.
|
| This means that `dimensional` isn't only about 'statically
| checking' dimensions and units, but also inferring them.
|
| You can see this interactively. What is the type of 1 meter
| divided by 1 second: ghci> :t (1 *~
| meter) / (1 *~ second) Quantity (Dim Pos1 Zero Neg1
| Zero Zero Zero Zero) Double
|
| where (Dim Pos1 Zero Neg1 Zero Zero Zero Zero) is a synonym
| for the dimension of velocity, which is not checked -- it
| is inferred at compile time.
| simiones wrote:
| Nice, I didn't realize this! Thank you for going into
| this level of detail.
| boscillator wrote:
| This is something I always feel is missing from unit libraries.
| antononcube wrote:
| Yes you can do that (easily) with Wolfram Language (aka
| Mathematica.)
|
| Here is an example: mat1 = Table[
| RandomChoice[{ Quantity[RandomReal[], "Meters"],
| Quantity[RandomReal[], "Seconds"],
| Quantity[RandomReal[], "Meters/Seconds"],
| Quantity[RandomReal[], "Meters/Seconds^2"] }] , 3,
| 3]; mat1 // MatrixForm mat1 .
| Transpose[mat1]
|
| See the corresponding screenshot: https://imgur.com/aP9Ugk2
| edu_guitar wrote:
| I haven't had the opportunity to use this in research yet, but i
| liked numbat [0], as it comes with relevant common units and lets
| you define your own. It appeared on HN before [1].
|
| [0]: https://github.com/sharkdp/numbat
|
| [1]: https://news.ycombinator.com/item?id=38276430
| Hizonner wrote:
| I used that package once, to write a program to do US taxes on
| Canadian mutual funds. Yes, it seemed necessary.
| amelius wrote:
| Can it also do reference frames?
|
| Like if I have a cartesian coordinates in one reference frame,
| can I use them in a type-safe way? E.g. not add two vectors in
| two reference frames? Same for polar coordinates?
|
| Etc.
| cosmic_quanta wrote:
| The `dimensional` library doesn't provide this, no. However,
| it's easy to 'tag' data in Haskell using phantom types. For
| example, for vectors in reference frames:
| newtype Framed f a = MkFramed (Vector a) add
| :: Framed f a -> Framed f a -> Framed f a add (MkFramed
| vec1) (MkFramed vec2) = ... add vec1 and vec2
| data ReferenceFrame1 data ReferenceFrame2
| v1 :: Framed ReferenceFrame1 Double v1 = ...
| v2 :: Framed ReferenceFrame2 Double v2 = ...
| main :: IO () main = print $ v1 `add` v2 -- will not
| type check
|
| There might already be a library to do this
| amelius wrote:
| Ok, but I'd also want to convert between polar and cartesian
| coordinates.
| jonjojojon wrote:
| The modeling of unit systems with types is fraught with all sorts
| of inconsistencies and design tradeoffs. There are quantities
| that have the same dimensions, but shouldn't be compared or allow
| arithmetic between them. Many issues with Temperature scales and
| how to interpret differences in these spaces.
|
| I think a pretty well thought out approach to this is the mp-
| units library in c++ that uses the ISQ(International System of
| Quantities) [1].
|
| There are some great blog posts on their site about modeling
| units systems and dimensions. Fingers crossed it may even be
| added to the standard library in C++29.
|
| [1]: https://mpusz.github.io/mp-units/latest/
| adrian_b wrote:
| Many quantities that in the current official version of SI
| appear to have the same dimensions, in fact do not have the
| same dimensions.
|
| The illusion of having the same dimensions has been caused by a
| vote of the General Conference on Weights and Measures, which
| has been exactly as stupid as deciding by vote that 2 + 2 = 5.
| It is really a shame that something like this has happened.
|
| According to that vote, some quantities have been declared as
| adimensional, even if arbitrary units of measurements must be
| chosen for them, exactly like for any other physical quantity
| and their units enter in the definitions of many other units,
| so changing any such choice of a unit changes the values of
| many other SI units, which implies that any version of SI that
| does not explicitly include the fundamental units chosen for
| "adimensional" quantities is incomplete.
|
| The system of units of measurement required for the physical
| quantities includes fundamental units a.k.a. base units, which
| are chosen arbitrarily, without any constraints, and also
| derived units, whose values are determined in such a way as to
| eliminate all constants from the formulae that relate the
| physical quantity for which the derived unit is determined to
| other physical quantities for which units have already been
| determined.
|
| The fundamental units are of 3 kinds, which I shall call
| arithmetic units, geometric units and dynamic units.
|
| The arithmetic units are for discrete quantities, i.e.
| quantities that are expressed in integer numbers, so they have
| natural units.
|
| There are a huge number of such fundamental units for
| quantities that can be obtained by counting, e.g. the number of
| atoms of a certain kind, the number of ions of certain kind,
| the number of molecules of a certain kind, the number of
| objects of a certain kind, the number of apples, the number of
| oranges, the number of humans and so on.
|
| While "1" is the natural unit for all such quantities, in
| physics and chemistry there are many such quantities for which
| the conventional SI unit is not "1", but the number of
| Avogadro, a.k.a. one mole of substance.
|
| Besides numbers of various entities, if we neglect
| nuclear/"elementary" particle physics, there is one more
| discrete physical quantity, which is the electric charge a.k.a.
| the quantity of electricity. The natural unit for the electric
| charge is the elementary electric charge and the conventional
| SI unit, the coulomb, is defined as an exact multiple of the
| elementary electric charge.
|
| Besides the units for discrete quantities, there are 6
| fundamental units for continuous quantities, 3 geometric units
| & 3 dynamic units. The 3 geometric units are the units of
| logarithmic ratio, plane angle and solid angle (which are
| respectively associated with 1-dimensional, 2-dimensional and
| 3-dimensional spaces).
|
| The 3 fundamental geometric units are chosen by a mathematical
| convention, but they are exactly as arbitrary as the
| fundamental dynamic units. This is proven by the fact that in
| practice many different choices are used for these 3
| fundamental units. Like length can be measured in meters or in
| feet, logarithmic ratios can be measured in nepers or in
| decibels, plane angles can be measured in degrees or in right
| angles or in cycles or in radians, while solid angles can be
| measured in orthogonal trihedral angles, in whole spheres or in
| steradians. Changing the choice for any of these fundamental
| units requires value conversions for all physical quantities
| that have units derived from them, in the same way like when
| replacing Imperial units with metric units.
|
| There are many physical quantities in SI that appear to have
| the same dimensional formulae, but this happens only because
| the units of logarithmic ratio or plane angle or solid angle
| are omitted from the formulae. This omission is especially
| frequent for the unit of plane angle, which enters in a much
| greater number of dimensional formulae than it is obvious. For
| instance, the magnetic flux also includes in its dimensional
| formula the unit of plane angle, though you will almost never
| see this taken into account.
|
| The SI includes a greater number of conventional fundamental
| dynamic units than necessary, for historical reasons, which
| results in a greater number of "universal constants" than for
| an optimal system of units, which requires only 3 fundamental
| dynamic units.
|
| While at the end of the 18th century, when the metric system
| has been designed, it was convenient to choose as fundamental
| dynamic units the units of length, time and mass, for which
| distinct physical devices were chosen to materialize the units,
| the current SI system is equivalent with choosing a single
| physical device to materialize all 3 fundamental dynamic units,
| which are the units of length, time and electric voltage (yes,
| now the unit of mass is derived from the unit of electric
| voltage, not vice-versa, despite confusing definitions that can
| be encountered in many texts).
|
| The single physical device that can materialize all 3
| fundamental dynamic units is an atomic clock. The
| electromagnetic wave corresponding to its output signal
| provides the unit of length in its wavelength, the unit of time
| in its wave period, while the Josephson voltage corresponding
| to its frequency provides the unit of electric voltage. The
| conventional units of SI are obtained from the 3 units provided
| by an atomic clock by multiplication with conventional exact
| constants, in order to ensure continuity with the older units
| of measurement.
|
| A few of the so-called base units of SI are no longer true base
| units, but they are derived from the true base units by
| inserting exact conventional constants in the formulae relating
| physical quantities, e.g. the units of temperature and luminous
| intensity.
| olddustytrail wrote:
| And?
| adrian_b wrote:
| My point was that what the previous poster had mentioned
| "There are quantities that have the same dimensions, but
| shouldn't be compared or allow arithmetic between them" is
| caused by using incorrect dimensional formulae.
|
| When you use correct dimensional formulae, most of these
| cases disappear, e.g. torque and energy do not have the
| same dimensions (the cases that remain are for quantities
| of the same kind that measure different things, like the
| average and the peak value of some quantity, for which some
| arithmetic operations that combine them may be
| meaningless). The problem is that there are plenty of
| textbooks with incomplete formulae, so one may need to
| analyze them, instead of having faith that they are
| correct.
|
| Also for the discrete quantities obtained by counting one
| must define different types depending on what is counted,
| i.e. one must not use the same type for counting horses and
| for counting boxes.
| fofoz wrote:
| Similar library for Rust providing zero cost unit safety
|
| https://github.com/paholg/dimensioned
| Mikhail_K wrote:
| See also https://painterqubits.github.io/Unitful.jl/stable/
| jcgrillo wrote:
| I misread this title (the word "confidence" threw me) and was
| initially very confused when it turned out to be about
| dimensional analysis instead of uncertainty.
|
| But why not both? A number system with dimensions _and_ which
| automatically propagates measurement uncertainty through
| calculations (a la Taylor 's train wreck book) doesn't seem
| totally infeasible?
|
| I would particularly like this for expressing cad models as code
| instead of brep.
| cosmic_quanta wrote:
| There are Haskell packages for uncertainty (e.g. [0], based on
| automatic differentiation [1]). However, these packages don't
| support typed dimensions, because multiplication/division
| becomes more complex.
|
| It is my goal to provide quantities with uncertainty AND typed
| dimensions one day.
|
| [0]: https://hackage.haskell.org/package/uncertain
|
| [1]: https://hackage.haskell.org/package/ad
| jcgrillo wrote:
| Very cool. Thanks for the links, I'm not very familiar with
| the Haskell ecosystem, I mostly work in Rust and Go these
| days. I'll definitely check them out.
| ssivark wrote:
| This is the kind of thing that would be pretty straight-
| forward in Julia, I imagine. Independent libraries for
| uncertainties and units could easily be remixed together if
| the unitful quantity accepts any number type, and the
| uncertainties just define a new number type. Multiple
| dispatch should generate consistent downstream behavior "for
| free".
| antononcube wrote:
| Wolfram Language (aka Mathematica) is the best for doing
| scientific computing with physical unit dimensions.
|
| See: https://reference.wolfram.com/language/guide/Units.html
|
| > The units framework [of Wolfram Language] integrates seamlessly
| with visualization, numeric and algebraic computation functions.
| It also supports dimensional analysis, as well as purely symbolic
| operations on quantities.
| hexane360 wrote:
| Note that Mathematica units impose a very large runtime
| penalty, making them unsuitable for a lot of applications
| hyperjeff wrote:
| For those coding in Swift, there is the Physical[0] package.
|
| [0] https://github.com/hyperjeff/Physical
| aithrowawaycomm wrote:
| I have yet to see a language which does units better than F#:
| https://learn.microsoft.com/en-us/dotnet/fsharp/language-ref...
| It is one of the main reasons I use F# over other functional
| languages.
|
| Doing it within an existing type system is more trouble than it's
| worth: the algebra of units is simple, but it doesn't apply to
| other types without stretching the compiler. It is far easier to
| have a distinct subsystem with measure types specifically
| identified.
| chris_va wrote:
| I am partial to astropy, eg
|
| from astropy import units as u
|
| density = np.linspace(0, 1, 10)*u.g/u.cm**3
|
| mass = (density*1*u.m**3).to(u.kg)
| semi-extrinsic wrote:
| I will say that the OpenFOAM C++ library supports dimensions in a
| more user friendly way than this, with support for both
| vectors/matrices and for checking units when you take partial
| derivatives of your quantities.
|
| There is even native support for reading dimensioned values from
| input files, so your user can specify "speed 12.7 [ft/s]"
| irrespective of what units you use internally in the code
| processing that input file. It just gets converted (or throws an
| error).
|
| See e.g. this, from 4.2.6 onwards:
| https://doc.cfd.direct/openfoam/user-guide-v12/basic-file-fo...
___________________________________________________________________
(page generated 2024-11-21 23:00 UTC)