Search code examples
vectorprologclpclpr

How to interface Prolog CLP(R) with real vectors?


I'm using Prolog to solve simple geometrical equations. For example, I can define all points p3 on a line passing trough two points p1 and p2 as:

   line((X1, Y1, Z1), (X2, Y2, Z2), T, (X3, Y3, Z3)) :-
        {(X2 - X1) * T = X3},
        {(Y2 - Y1) * T = Y3},
        {(Z2 - Z1) * T = Z3}.

And then a predicate like line((0, 0, 0), (1, 1, 1), _, (2, 2, 2)) is true.

But what I'd really want is to write down something like this:

line(P1, P2, T, P3) :- {(P2 - P1) * T = P3}.

Where P1, P2, and P3 are real vectors.

What's the best way of arriving at something similar? The best I found so far is to rewrite my own add, subtract and multiply predicates, but that's not as conveniant.


Solution

  • Here is a solution where you still have to write a bit of code for each operator you want to handle, but which still provides nice syntax at the point of use.

    Let's start with a notion of evaluating an arithmetic expression on vectors to a vector. This essentially applies arithmetic operations component-wise. (But you could add a dot product or whatever you like.)

    :- use_module(library(clpr)).
    
    vectorexpr_value((X,Y,Z), (X,Y,Z)).
    vectorexpr_value(V * T, (X,Y,Z)) :-
        vectorexpr_value(V, (XV,YV,ZV)),
        { X = XV * T },
        { Y = YV * T },
        { Z = ZV * T }.
    vectorexpr_value(L + R, (X,Y,Z)) :-
        vectorexpr_value(L, (XL,YL,ZL)),
        vectorexpr_value(R, (XR,YR,ZR)),
        { X = XL + XR },
        { Y = YL + YR },
        { Z = ZL + ZR }.
    vectorexpr_value(L - R, (X,Y,Z)) :-
        vectorexpr_value(L, (XL,YL,ZL)),
        vectorexpr_value(R, (XR,YR,ZR)),
        { X = XL - XR },
        { Y = YL - YR },
        { Z = ZL - ZR }.
    

    So for example:

    ?- vectorexpr_value(A + B, Result).
    A =  (_1784, _1790, _1792),
    B =  (_1808, _1814, _1816),
    Result =  (_1832, _1838, _1840),
    {_1808=_1832-_1784},
    {_1814=_1838-_1790},
    {_1816=_1840-_1792} .
    

    Given this, we can now define "equality" of vector expressions by "evaluating" both of them and asserting pointwise equality on the results. To make this look nice, we can define an operator for it:

    :- op(700, xfx, ===).
    

    This defines === as an infix operator with the same priority as the other equality operators =, =:=, etc. Prolog doesn't allow you to overload operators, so we made up a new one. You can think of the three = signs in the operator as expressing equality in three dimensions.

    Here is the corresponding predicate definition:

    ExprL === ExprR :-
        vectorexpr_value(ExprL, (XL,YL,ZL)),
        vectorexpr_value(ExprR, (XR,YR,ZR)),
        { XL = XR },
        { YL = YR },
        { ZL = ZR }.
    

    And we can now define line/4 almost as you wanted:

    line(P1, P2, T, P3) :-
        (P2 - P1) * T === P3.
    

    Tests:

    ?- line((0,0,0), (1,1,1), Alpha, (2,2,2)).
    Alpha = 2.0 ;
    false.
    
    ?- line((0,0,0), (1,1,1), Alpha, (2,3,4)).
    false.