Date ons 20 november 2019

Units is a well established typecheck that is underused in todays programming worlds. We have famous disasters from using the wrong units, a Mars lander crashed due to such a bug. I think it is a misstake not to have units in the type system of the language. So I asked myself what is the unit functionality that would make me drool. The answer to this is to handle the units as si units with integer exponents and use clpfd to construct constraints on them.

So taking the exponent of a unit value of the format

Value : Unit

We use the tr/2. This function do translate algebraic expressions of units like

?- tr(1:dm,Length).
Length = 0.1 : [1,0,0,0,0,0,0]

We see that we represents the unit as a list of individual SI units exponents, the list is [m,kg,s,A,K,cd,mol]. We can do algebra as well like

?- tr(1:m / 2:s,Velocity).
Velocity = 1 : [1,0,-1,0,0,0,0]

Also we can issue a combined unit and also use common derived units

?- tr(1:'N'*m,Energy).
Energy = 1 : [2,1,-2,0,0,0,0]

We also have the \(===\) operator. It will unify the algebraic value of lhs and rhs and use clpfd constraint on lhs and rhs of the expression e.g.

'==='(X,Y) :-
  tr(X,X1:X2),
  tr(Y,Y1:Y2),
  X1=Y1,
  X2 #= Y2.

A multiplication is translated as

tr(X*Y,Z1:Z2) :-
  tr(X,X1:X2),
  tr(Y,Y1:Y2),
  Z2 #= X2 + Y2,
  (number(X1),number(Y1)) -> Z1 is X1 * Y1 ; true).

Exponentiation is translated to:

tr(X**N,Z1:Z2) :-
  tr(X,X1:X2),
  Z2 #= X2 * N,
  (number(N),number(X1)) -> Z1 is X1 ** N ; true).

Sum gives:

tr(X+Y,Z1:Z2) :-
  tr(X,X1:X2),
  tr(Y,Y1:Y2),
  X2 #= Y2,
  Z2 #= X2,      
  (number(X1),number(Y1)) -> Z1 is X1 + Y1 ; true).

With this we can do cool operations as making sure a has no unit

unitexp(X,A,AA,Z) :-
  AA = A:_,
  XX u= AA*X
  equalUnits(XX,1),
  Z is exp(XX).

:::prolog
?- unitexp(1:m,2,A,Z).

A = 2 : [-1,0,0,0,0,0,0]
Z = 7.38905609893065 : [0,0,0,0,0,0,0]

The most advanced constraint solving technique one can use is to find pretty printing of units. Sometimes Volt is a better unit than it's SI unit counterpart so we could ask, what is the representation of a unit with the fewest unit symbols? The algorithm I'm using is to first try one symbol then two then three and so on. here is the code for two symbols

try2(X,S1,N1,S2,N2) :-
tr(X,_:Y),
gen(S1),
gen(S2),
S2 \= S1,
tr(1:S1**N1*S2**N2,_:Y),
t([N1,N2])

t/1 is basically a labeling that fails if labels send an exception. gen/1 generates all unit symbols that we allow for the pretty printing and you see how we try to constrain the solution to yield solution values for N1,N2. Here is a demo how it can work (we use pr/1 to pretty print a unit expression):

?- pr(1:'N'*m*s**(-1)).
1:W

?- pr(1:'N'*m*s**(-2)).
1:s**-2 J

We could improve it further if we for example try to find all answers with two units and pick the one with the lowest sum of abolute exponentials. If we implement this we get,

?- pr(1:'N'*m*s**(-2)).
1:s**-1 W

This library works with swi prolog and you can find the source code in the guile-log's source tree (LGPL v2 or v3).

'languge/prolog/modules/example/units.pl'

First use the clpfd module then ensure_load the file and have a play if using swipl.

Using the === operator is as simple as

?- X : km/h === 1 mile/h.
X = 1.609344

X === 1 mile/h.
X = 0.44704:[1, 0, -1, 0, 0, 0, 0].

Also we have constants/2 That can give physical constants with their units.

?- constants(e0,X),pr(X).
8.854187812786998e-18:m**-1 F
X = 8.854187812786998e-18:[-3, -1, 4, 2, 0, 0, 0]

A drawback for a system that handle units is that libraries is harder to write and you could expect that you need to write multiple versoins of the same function. Nah this system is powerful enough to make it generic, have a look at this

limit(Min,Max,X,Y) :- Y === max(Min:_,min(Max:_,X)).

?- limit(1,10,2:m,Y).
Y = 2:[1, 0, 0, 0, 0, 0, 0].

?- limit(1,10,20:m,Y).
Y = 10:[1, 0, 0, 0, 0, 0, 0].

?- limit(1,10,0:m,Y).
Y = 1:[1, 0, 0, 0, 0, 0, 0].

Have fun!!


Comments

comments powered by Disqus