Search code examples
prologclpfd

Prolog CLP order of operations?


Hi hopefully someone can help me. I was just wondering if my code below was sufficient in setting up a matrix of 12 x 12 and, assuming the 'constrain(M)' calls all the correct constraints which are defined in rules lower down, labelling each of the rows? It's failing at the moment and I've traced my constraints so I know they all work but didn't know whether it was because I'm calling them outside of the main predicate?

matrix(M) :-
M = [R1,R2,R3,R4,R5,R6,R7,R8,R9,R10,R11,R12],
R1 = [A,B,C,D,E,F,G,H,I,J,K,L],
R2 = [A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2],
R3 = [A3,B3,C3,D3,E3,F3,G3,H3,I3,J3,K3,L3],
R4 = [A4,B4,C4,D4,E4,F4,G4,H4,I4,J4,K4,L4],
R5 = [A5,B5,C5,D5,E5,F5,G5,H5,I5,J5,K5,L5],
R6 = [A6,B6,C6,D6,E6,F6,G6,H6,I6,J6,K6,L6],
R7 = [A7,B7,C7,D7,E7,F7,G7,H7,I7,J7,K7,L7],
R8 = [A8,B8,C8,D8,E8,F8,G8,H8,I8,J8,K8,L8],
R9 = [A9,B9,C9,D9,E9,F9,G9,H9,I9,J9,K9,L9],
R10 = [A10,B10,C10,D10,E10,F10,G10,H10,I10,J10,K10,L10],
R11 = [A11,B11,C11,D11,E11,F11,G11,H11,I11,J11,K11,L11],
R12 = [A12,B12,C12,D12,E12,F12,G12,H12,I12,J12,K12,L12],

constrain(M),

labeling([],R1),
labeling([],R2),
labeling([],R3),
labeling([],R4),
labeling([],R5),
labeling([],R6),
labeling([],R7),
labeling([],R8),
labeling([],R9),
labeling([],R10),
labeling([],R11),
labeling([],R12).

Solution

  • You should always separate the constraint posting from the actual search (labeling/2).

    The reason is clear: It can often be extremely expensive to search for concrete solutions. Posting the constraints, on the other hand, is often very fast.

    If, as in your case, the two parts are uncleanly mixed, you cannot tell easily which part is responsible if there are unexpected problems such as nontermination.

    In your case, the only thing you should improve in the main predicate is enforcing said separation between constraint posting and search.

    The mistake that causes unexpected failure is most likely contained in one of the rules you did not post here. You can find out which rules are involved in the failure by systematically replacing the goals in which they are called by true. Thus, there's no need for tracing: You can debug CLP(FD) programs declaratively in this way.

    EDIT: Here is more information about the separation between posting constraints and the search for concrete solutions. As introduced in GUPU, we will use the notion of core relation, which has the following properties:

    1. By convention, its name ends with an underscore _.
    2. Also by convention, its last argument is the list of variables that need to be labeled.
    3. It posts the CLP(FD) constraints. This is also called the (constraint) modeling part or (constraint) model.
    4. It doesn't use labeling/2.

    The search part is usually performed by label/1 or labeling/2.

    Suppose you have a predicate where you intermingle these two aspects, such as in your current case:

    matrix(M) :-
            constraints_hold(M),
            ... relate M to variables Vs ...
            labeling(Strategy, Vs).
    

    Obviously, for the reasons explained above, the call of labeling/2 is the part we want to remove from this predicate. Of course, as you observe, we still want to somehow access the variables that are supposed to be labeled.

    We do this as follows:

    • We introduce a new argument to the core relation to pass around the list of finite domain variables that need to be labeled.

    • By convention, we reflect the additional argument by appending an underscore (_) to the predicate name.

    So, we obtain the following core relation:

    matrix_(M, Vs) :-
            constraints_hold(M),
            ... relate M to variables Vs ...
    

    The only missing part (which you haven't done yet, but which you should have done in any case), is stating the relation between the object of interest (in this case: the matrix) and the finite domain variables. This is the part I leave as a simple exercise for you. Hint: append/2.

    Once you have done all this, you can solve the whole task by combining the core relation and labeling/2 in a single query or predicate:

    ?- matrix_(M, Vs), labeling(Strategy, Vs).
    

    Note that this separation between core relation and search:

    • makes it extremely easy to try different labeling strategies without recompiling your program.
    • allows you to determine important procedural properties of the core relation without needing to search for concrete solutions.

    Use the introduction and explanation of this important separation as an indicator when judging the quality of any text about CLP(FD) constraints.