0

I am quite new in the Prolog magical universe, but it seems to be very powerful to solve my problem.

I have two basis [i0,j0,k0] and [i1,j1,k1]. Then we have an orthogonal constraint between all elements of the same basis (i0 is orthogonal to j0, j0 is orthogonal to k0, and k0 is orthogonal to i0).

I would like to find the set of new orthogonal constraint to add between elements of the first basis and the second one such that the two basis are aligned/equal. Then it could be interesting to find the minimal number of constraints (which seems to be 3 when you think about it).

I have working a start of code (but you can give me tips and tricks to have a more efficient code) but then I'm stuck when I need to express the goal of my problem ...

Let's define define ortho/1 such that all the elements in the list given to ortho are ortho one to another. Then we could define our two basis:

ortho([i0,j0,k0]).
ortho([i1,j1,k1]).

I add then the following rule to deduce ortho([X,Y]) from ortho([X,Y,Z]) predicates:

ortho(L) :- ortho(B), unique(L), unordered_subset(B,L).

This could be explained as we need to find an existing basis B such that L is a sublist of B and all elements of L are distinct. Some tests are leading to:

?- ortho([i0,j0]).
true.
?- ortho([k0,i0]).
true.
?- ortho([i0,i0]).
false.

Here is the implementation of unique/1, subset/2 and unordered_subset/2 I used in my program (Found on StackOverflow, don't know if there is better but it seems to work).

% unique/1 detect list formed by unique elements
unique([]).
unique([X|Xs]) :- \+ memberchk(X, Xs), unique(Xs).

% subset/2 (set, subset) true if subset is a subset of set in the same order
subset([], []).
subset([E|Tail], [E|NTail]):-
    subset(Tail, NTail).
subset([_|Tail], NTail):-
    subset(Tail, NTail).

% unordered_subset/2 (set, subset) true if subset is a subset of set
unordered_subset(Set, SubSet):-
    length(Set, LSet),
    between(0,LSet, LSubSet),
    length(NSubSet, LSubSet),
    permutation(SubSet, NSubSet),
    subset(Set, NSubSet).

The finality is to see if the two basis are equal. To do so, I defined equal/2 and collin/2 such that:

% collin/2 check the collinearity of two vectors
collin(A,B) :-
    ortho([X,Y,A]), ortho([X,B]), ortho([Y,B]).

% equal/2 check basis equals
equal([],[]).
equal([H1|T1],[H2|T2]) :-
    collin(H1,H2),
    equal(T1,T2).

collin/2 is based on the fact that two vectors A and B are collinear if it exists X and Y such that [X,Y,A] is a basis and X and Y are both orthogonal to B. And equal/2 is the fact that two basis are equal if the vectors taken in the good order are collinear. (By commutativity Prolog should be able to find this good order).

To find a solution to my problem, I think there is something about finding a list of pairs T of length N, with N as low as possible, such that for all pair P=X-Y in T, ortho(X,Y) implies equal([i0,j0,k0], [i1,j1,k1]).

Could you help me to express this rule ? (And eventually to have a better code :) )

Teusner
  • 1
  • 1

1 Answers1

0

Your unique/1 only works when the list has been fully populated - which violates the "fail fast" principle of performant code. Should check at the point that the individual list elements become instantiated - which can be done using freeze.

The most correct unique definition that I've seen is all_dif in https://stackoverflow.com/a/31724022/ (because it works when the list's tail is later extended).

brebs
  • 3,462
  • 2
  • 3
  • 12