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.

Let’s start with the completed code first:

from pyDatalog import pyDatalog
from pyDatalog.pyDatalog import create_terms as terms
from pyDatalog.pyDatalog import ask

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
from pyDatalog.pyDatalog import ask

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 [3]: type(scale)
Out[3]: 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 [5]: 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 [6]: print(scale['mile', 'meter'] == V)
V
------------------
1609.3440016415307

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

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

In [8]: 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 [9]: print(conv[3, 'mile', 'meter'] == V)
V
-----------------
4828.032004924591

Bon apetitite.

About mcturra2000

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. 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]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s