## Logic programming example: unit conversion using Datalog

Logic programming can reduce your code size, and make deductions based on facts. I’ll illustrate this using pyDatalog, a Datalog implementation in Python 3, to create a unit conversion program. I provide it wil some basic facts about how many meters, miles and feet there are in an inch, together with two conversion rules. Using those rules, I can use Datalog to “automagically” convert between any two units, even though I haven’t explicitly told it how to perform the conversion.

```from pyDatalog import pyDatalog
from pyDatalog.pyDatalog import create_terms as terms

pyDatalog.create_terms('scale') # the long way of doing it
terms('A, B, C, V')

scale['meter', 'inch'] = 39.3700787
scale['mile', 'inch'] = 63360.0
scale['feet', 'inch'] = 12.0

scale[A, B] =  1/scale[B, A]
scale[A,B] = scale[A,C] * scale[C, B]

print(scale['inch', 'meter'] == V)
print(scale['mile', 'meter'] == V)

terms('conv')
conv[V, A, B] = V * scale[A, B]
print(conv[3, 'mile', 'meter'] == V)
print(conv[1, 'meter', 'feet'] == V)
```

There is a division bug in pyDatalog v 0.14.6, so if you want to run the example, you will need to check out changeset b1a5df9 until a new version is released.

Now let me walk through the code. First, I import the pyDatalog module, and define some convenience functions:

```from pyDatalog import pyDatalog
from pyDatalog.pyDatalog import create_terms as terms

Next, let’s create some terms: scale, which is a function (actually a term) where we specify how one unit relates to another, and A,B,C, V, which we think of as matching “variables”:

```pyDatalog.create_terms('scale') # the long way of doing it
terms('A, B, C, V')```

Then, let use give some facts about how to scale between various units:

```scale['meter', 'inch'] = 39.3700787
scale['mile', 'inch'] = 63360.0
scale['feet', 'inch'] = 12.0```

So, these facts state that 1 meter has 39.3700787 inches, and so on. They look like functions, but they are in fact terms:

```In : type(scale)
Out: pyDatalog.pyParser.Term```

The same applies for A, B, C, and V. So far, so good. But what if we want to convert, say, inches into meters instead of meters into inches? Then we need an inverse rule:

`scale[A, B] =  1/scale[B, A]`

Let’s test it:

```In : print(scale['inch', 'meter'] == V)
V
-----------------
0.025400000025908```

Datalog has used the inverse rule to “go the other way”. There is some minor magic going on here. Note that we didn’t specify which fact to use for inversion, Datalog figured it out for itself.

Now let’s cast some real magic. Specify a transition rule:

`scale[A,B] = scale[A,C] * scale[C, B]`

which states that you can get from unit A to B if there exists another unit C that serves as an intermediate conversion. Now test it:

```In : print(scale['mile', 'meter'] == V)
V
------------------
1609.3440016415307```

Good. It’s cleverness doesn’t stop there:

```In : scale['league', 'mile'] = 3.45233834

In : print(scale['league', 'meter'] == V)
V
-----------------
5555.999999116079```

Notice how there is no simple way to convert from leagues to meters. Datalog must deduce a chain of transitions in order to obtain the result.

To round things off, let’s define a convenience function, “conv”, which converts V units in A into units of B:

`conv[V, A, B] = V * scale[A, B]`

Perhaps we could have used a simple python function for that, but let’s keep with the paradigm. We can then ask questions like: what is 3 miles in meters:

```In : print(conv[3, 'mile', 'meter'] == V)
V
-----------------
4828.032004924591```

Bon apetitite. Computer programmer living in Scotland.
This entry was posted in Computers and tagged , , . Bookmark the permalink.

### 1 Response to Logic programming example: unit conversion using Datalog

1. Pierre Carbonnelle says:

With version 0.16.0, it is necessary to re-order the definition of scale as follows, in order to avoid an infinite loop :

scale[A,B] = scale[A,C] * scale[C, B]
scale[A, B] = 1/scale[B, A]

As the tutorial says : “The most general definition of the function is given first. When searching for possible answers, pyDatalog begins with the last rule defined, i.e. the more specific, and stops as soon as a valid answer is found for the function. ”

In this case, the engine should start to resolve a query with scale[A, B] = 1/scale[B, A], not with scale[A,B] = scale[A,C] * scale[C, B]