Search code examples
prologtraveling-salesmanconstraint-programmingclpfdeclipse-clp

ECLiPSe CLP - TSP with Time WIndow. How do i calculate the cost?


I'm working on a variant of a TSP, where each node has a Time Window, but i've some problems calculating the cost function. I'm using a succesors model, so i have a list where each variable rapresents the next destination, Xi = j is the link from node i to node j.

My code looks like this:

:-lib(ic).
:-lib(branch_and_bound).
:-lib(propia).
:-[nodes].

tsp(Next, Cost):-
  Cost::1..10000,
  Next::1..10000,

  %Constraints
  alldifferent(Next),
  different_from_index(Next),
  circuit(Next),
  create_objective(Next, Cost),
  minimize(labeling(Next), Cost).

Where different_from_index is a constraint between the index of a Next's variable and it's value: Next[i] != i, and create_objective is the predicate that define the objective function. First of all the create_objective predicate creates a list of the link's costs, so it would be easy to get the cost with a simple sumlist predicate. But i need to define a time window for each node and i thought something like this:

time_window([], _, _, 0).
time_window([HCost | TCost], Next, Start, Cost):-
  element(Start, Next, Destination),
  time_window(TCost, Next, Destination, Cost1),
  Cost #= Cost1 + HCost,
  node(Destination, Min, Max) infers most,
  Cost #>= Min, Cost #=< Max.

where the [HCost | TCost] is the list of the costs mentioned before, but sorted and reversed (so i have as n element of the list the first link, ad n-1 the second and so on). Furthermore the node predicate is contained in prolog file loaded at the begin. Unfortunately this doesn't seem to work: it doesn't return false neither wrong solution. After some times of computing i receive this message:

[eclipse 2]: tsp(Next, Cost).
bb_min: search did not instantiate cost variable
Aborting execution ...
Abort

I understand the error, but i don't know how to fix it. I successfully did it with a simplier model and a similar time_window predicate, but in this case it not seems appropriate.

Can anyone help me out? Thanks in advice.


Solution

  • Let's start with the following basic TSP program. It takes as input a distance matrix Dist, and maintains a successor array Next, and a distance array Legs which contains the distance travelled from each node to its successor. We minimise Cost, which is the total distance travelled.

    :- lib(ic).
    :- lib(branch_and_bound).
    
    tsp(Dist, Next, Cost) :-
        dim(Dist, [N,N]),       % get distance matrix size
    
        dim(Next, [N]),         % successor variables
        Next #:: 1..N,
        circuit(Next),          % they must form a circuit
    
        dim(Legs, [N]),         % Legs[I] is distance I->Next[I]
        ( for(I,1,N), param(Dist,Next,Legs) do
            element(I, Next, J),
            element(J, Dist[I], DistIJ),
            element(I, Legs, DistIJ)
        ),
        Cost #= sum(Legs),
    
        % search and minimize
        minimize(search(Legs,0,smallest,indomain_min,complete,[]), Cost).
    

    To enable time window handling, add an array Time giving the arrival time at each node, which can then be constrained according to requirements. Arrival time at the successor of node I can be computed as arrival time at I plus travel time from I to its successor (for simplicity, assume that distance = time, and that we start at node 1 at time 0). This leads to

    tsp(Dist, Next, Time, Cost) :-
        dim(Dist, [N,N]),       % get distance matrix size
    
        dim(Next, [N]),         % successor variables
        Next #:: 1..N,
        circuit(Next),          % they must form a circuit
    
        dim(Legs, [N]),         % Legs[I] is distance I->Next[I]
        dim(Time, [N]),         % Time[I] is arrival time at I
        ( for(I,1,N), param(Dist,Next,Legs,Time) do
            element(I, Next, J),
            element(J, Dist[I], DistIJ),
            element(I, Legs, DistIJ),
    
            ( I==1 -> TimeAtI = 0 ; element(I, Time, TimeAtI) ),
            element(J, Time, TimeAtJ),
            TimeAtJ #= TimeAtI + DistIJ
        ),
        Cost #= sum(Legs),  % total distance travelled
    
        % search and minimize
        minimize(search(Legs,0,smallest,indomain_min,complete,[]), Cost).
    

    Sample run:

    ?- data(b6, Dist), tsp(Dist, Next, Time, Cost).
    Found a solution with cost 2495
    Found a solution with cost 2441
    Found a solution with cost 2336
    Found no solution with cost 1525.0 .. 2335.0
    
    Dist = []([](0, 153, 510, 706, 966, 581),
              [](153, 0, 422, 664, 997, 598),
              [](510, 422, 0, 289, 744, 390),
              [](706, 664, 289, 0, 491, 265),
              [](966, 997, 744, 491, 0, 400),
              [](581, 598, 390, 265, 400, 0))
    Next = [](2, 3, 4, 5, 6, 1)
    Time = [](2336, 153, 575, 864, 1355, 1755)
    Cost = 2336
    Yes (0.00s cpu)