1
votes

I am following along the clpfd exercises here: http://www.pathwayslms.com/swipltuts/clpfd/clpfd.html

I have the following solution for example 5 number 1

trains([[1,2,0,1], % from station, to station, departs at, arrives at
       [2,3,4,5],
       [2,3,0,1],
       [3,4,5,6],
       [3,4,2,3],
       [3,4,8,9]]).

threepath(A, D, Ps) :-
    Ps = [[A,B,_T0,T1],[B,C,T2,T3],[C,D,T4,_T5]],
    T2 #> T1,
    T4 #> T3,
    trains(Ts),
    tuples_in(Ps, Ts).


allpaths(From,To,Route):-
  trains(Ts),
  length(Ts,Max_Trains),
  between(2,Max_Trains,Length_Of_Route),
  Route=[_,_|_],length(Route,Length_Of_Route),
  maplist(same_length([_,_,_,_]),Route),
  create_chain(Route,TimeChain,1),
  train_chain(Route),
  chain(TimeChain, #<),
  tuples_in(Route,Ts),
  nth1(1,Route,[From|_]),
  nth1(Length_Of_Route,Route,[_,To|_]).

create_chain([],[],_).
create_chain(List_of_Lists,[First,Second|Chain],1):-
  List_of_Lists =[Item|T],
  Item =[_T1,_T2,First,Second],
  create_chain(T,Chain,2).
create_chain(List_of_Lists,[First,Second|Chain],2):-
  List_of_Lists =[Item|T],
  Item =[_T1,_T2,First,Second],
  create_chain(T,Chain,1).

train_chain([_]).
train_chain(List_Of_Lists):-
  List_Of_Lists =[First,Second|T],
  First = [_One,Two,_,_],
  Second =[Two,_Three,_,_],
  train_chain([Second|T]).

I am not sure if I have done this in the 'intended' way but it works and seems okay. I basically create lists of variables that are across trains and then apply the constraint to. I am not sure if I have done this in the 'intended' way because I am only using clpfd for the times of the trains not the trains.

Now for the next exercise with employees, the constraints are on individual employees, not across the employees, unlike the train exercise.
I don't understand how to use tuples_in in this case, It seems simple to complete the exercise with findall. How would it be implemented using constraints and tuples_in and what are the advantages?

employees([
 [1, 75, 0, 30, 25],
 [2, 83, 0, 45, 25],
 [3, 90, 1, 45, 50],
 [4, 45, 3, 75, 25],
 [5, 89, 0, 52, 50]
 ]).

promotions(Es,Promotions):-
  employees(Es),
  findall([Id,Score,V,T,RT],
    (member([Id,Score,V,T,RT],Es),Score>80,V=<1,T>RT),
  Promotions).
2

2 Answers

1
votes

I think should be as simple as

ex2(EmployeeId) :-
 employees(Es),
 tuples_in([[EmployeeId, LastReviewScore, NumOfSafetyViolations, TimeInGrade, RequiredTimeForPromotion]], Es),
 LastReviewScore #> 80, NumOfSafetyViolations #=< 1, TimeInGrade #> RequiredTimeForPromotion,
 label([EmployeeId]).

The advantage, is that the relation among values that are used to identify candidates can be left unspecified until something interesting is known about the problem.

The simple example doesn't show such advantages... I have found tuple_in useful solving crosswords, where it allowed to drastically shorten the solution time.

1
votes

I'm glad I finally can answer something on StackOverflow. Here's my solution for the first question. I'm using Accumulator pattern, which you can find on chaper 6 of LearnPrologNow, they also have friendly slides here, just go for chapter 6: slides

First I call a helper predicate, which overloads the same name but with 4 arguments, the 3rd one is the Time that I arrive at From. For initialization this number is -1.

findpath(From, To, Path) :- findpath(From, To, -1, Path).

Then I do recursion on findpath/4,

% base case: from X1 to Xn there is direct train
findpath(X1, Xn, Time, [[X1, Xn, T0, T1]]) :- 
            trains(Ts),
            T0 #> Time,
            tuples_in([[X1, Xn, T0, T1]], Ts).

% go to a midpoint X2, leave X2 -> Xn to recursion
findpath(X1, Xn, Time, [P|Ps]) :-
            trains(Ts),
            P = [X1, X2, T0, T1],
            T0 #> Time, 
            tuples_in([P], Ts),
            findpath(X2, Xn, T1, Ps).

But the same with you, I'm not sure whether this makes the best use of clpfd. I call tuples_in/2 for every element of Ps, and I doubt that's more than necessary.