Hello everybody,
I have recently got down a new interesting rabbit hole with unit aware arithmetics.
In short, doing operations with metre, inch, seconds…
After finding out the excellent mp-units
c++ library, I have decided to simply copy their system built on units and quantities.
Basically, in addition to units, you can also add “quantities” informations to your operation making them more safe and instructive.
I have managed to make a pack called units
which you can use very simply by wrapping any arithmetic operation with the qeval/1
wrapper:
:- use_module(library(units)).
% simple numeric operations
?- qeval(10*km =:= 2*5*km).
% conversions to common units
?- qeval(1 * h =:= 3600 * s).
?- qeval(1 * km + 1 * m =:= 1001 * m).
% derived quantities
?- qeval(1 * km / (1 * s) =:= 1000 * m / s).
?- qeval(2 * km / h * (2 * h) =:= 4 * km).
?- qeval(2 * km / (2 * km / h) =:= 1 * h).
?- qeval(2 * m * (3 * m) =:= 6 * m^2).
?- qeval(10 * km / (5 * km) =:= 2).
?- qeval(1000 / (1 * s) =:= 1 * kHz).
% assignement and comparison
?- qeval(A is 10*m), qeval(A < 20*km).
A = 10 * kind(isq:length)[si:metre].
You can convert from one unit to another with the in/3
predicate and convert between quantities with the as/3
predicate:
?- qeval(Speed is (m/s in inch/h) as isq:speed).
Speed = 141732.28346456692 * isq:speed[international:inch/si:hour].
The README goes a bit more in depth in the different features of the library.
Unfortunately, the library needs a very new swi-prolog (>= ‘9.3.15’) because I use the new SSU syntax for DCG, but you can already try the pack by downloading it through the pack system:
?- pack_install(units).
One thing that I am quite proud of is that the library is also compatible with clpBNR !
?- qeval({A*metre == B*inch}), A = 1.
A = 1,
B = 5000r127.
Please, try it and let me know what you think.
If you have any improvements or suggestions, I am listening.
If you want to help, drop me a message here or on github.
There are still a lot of quantities and units that I haven’t added from the original mp-units
library.
And of course, lot of documentation to write ^^
There are loads of implementation details I would love to discuss.
For example, tabling was a life saver in this implementation for memoizing and improving the determinism of many predicates.
Moreover, some predicates does term rewriting which can results in cycles, but all of that goes away with tabling !
I am quite proud of my neat normalization predicate which works with factors only (so no additions):
?- units:normalize(a*b*c*a*A*1/(b*f), E).
E = A*a^2*c/f.
Another predicate I am proud of is the predicate for finding a common unit for 2 different units:
?- units:common_unit(si:kilo(metre), F1, international:inch, F2, U).
F1 = 10^3,
F2 = 9144/(12*3*10000),
U = si:metre.
It uses iterative deepening for finding the shortest amount of transformation to get to a common unit.