BookmarkSubscribeRSS Feed
☑ This topic is solved. Need further help from the community? Please sign in and ask a new question.
luboxing
Obsidian | Level 7

Greetings!

I am faced with a slightly modified TSP problem as below:

 

There are 4 nodes with directional values:

 

FRIDX TOIDX INC
1 2 -0.23548
1 3 -0.14027
1 4 -0.22685
2 1 0.014942
2 3 -0.02719
2 4 -0.11378
3 1 0.076665
3 2 -0.06068
3 4 -0.05206
4 1 0.059285
4 2 -0.07806
4 3 0.017148

 

However INC above is not the cost in a TSP problem. TO calculate the costs, here is another table:

Trip # HIST_INC
1 -0.072240154
2 -0.075171085
3 -0.077492213

 

Where trip # represent the subtrip number traveling to all nodes above without return. That is, 4 -->2 --> 1 --> 3 requires 3 subtrips.

HIST_INC is a historical value for INC I want to align with by defining the cost as:

 

cost = sum (sq(INC - HIST)) for all 3 trips (across 4 nodes).

 

Therefore, the costs are not only determined by the starting and ending nodes and their directions, but also by the order of each subtrip within the entire iteration acorss all nodes. For example, the cost of subtrip from 4 to 1 could take any of the three values, depending on the order of the subtrip within the enture trip (without return):

 

sq(0.059285 + 0.072240154)

sq(0.059285 + 0.075171085)

sq(0.059285 + 0.077492213)

 

I hope my explanation is clear; if not I'd be glad to further clarify. I looked up PROC OPTNETWORK used it for a standard TSP, but no luck with this one.

 

Thanks in advance!

Bo

1 ACCEPTED SOLUTION

Accepted Solutions
RobPratt
SAS Super FREQ

It looks like you are using an older version that does not support comparison of tuples.  I recommend upgrading, but until then here are three workarounds:

    = (if <i,t> in {<SOURCE,0>} then 1 else if <i,t> in {<SINK,n+1>} then -1 else 0);
    = (if i = SOURCE and t = 0 then 1 else if i = SINK and t = n+1 then -1 else 0);
   = (if i = SOURCE then 1 else if i = SINK then -1 else 0);

 

View solution in original post

6 REPLIES 6
RobPratt
SAS Super FREQ

The standard TSP model does not allow time-dependent costs.  Instead, you can solve your problem as a side-constrained shortest path problem in a directed acyclic network with 18 nodes:

  • (0,0): this is a dummy source node
  • (i,t) for i in 1..4 and t in 1..4
  • (5,5): this is a dummy sink node

The arcs are from (i,t) to (j,t+1), where i in 0..4, j in 1..5, and t in 0..4, and you can compute the arc costs according to your formula.

 

The decision variables are UseArc[i,j,t], and you need a flow balance constraint for each node, as well as a side constraint sum {<i,(j),t> in ARCS} UseArc[i,j,t] >= 1 for j in 1..4.

 

This approach is a simpler version of the one illustrated in the "Network Formulation" section of https://support.sas.com/resources/papers/proceedings14/SAS101-2014.pdf.

luboxing
Obsidian | Level 7

Thanks for your quick reply, Rob! 

 

Could you help me with the syntax? I am a beginner with Proc optmodel and here is what I have so far. 

 

PROC OPTMODEL;
SET TS = 1..4;
SET IDX = 1..4;
NUM SOURCE = 0;
NUM SINK = 1 + MAX {I in IDX} I;
SET NODES = IDX UNION {SOURCE, SINK};
SET <NUM, NUM> ARCS INIT {};

NUM INCS {IDX, IDX};
NUM INCAVG {TS};
READ DATA DYINC_M INTO [I=FRIDX]{J IN IDX} <INCS[I,J]=COL('INC'||J)>;
READ DATA HISINCAVG_M INTO [DYIDX] INCAVG=INC;
VAR USEARC {ARCS} BINARY;

NUM COST {I IN IDX, J IN IDX, T IN TS} = (INCS[I,J] - INCAVG[T])**2;

MIN TOTALCOST = SUM{I IN IDX, J IN IDX} COST[I,J]*USEARC[I,J];

...

QUIT;

 

 

RobPratt
SAS Super FREQ

Here are the requested modifications to your code, together with optional PUT, PRINT, and EXPAND statements to help see what is going on:

 

data DYINC_M;
   input FRIDX TOIDX INC;
   datalines;
1	2	-0.23548
1	3	-0.14027
1	4	-0.22685
2	1	0.014942
2	3	-0.02719
2	4	-0.11378
3	1	0.076665
3	2	-0.06068
3	4	-0.05206
4	1	0.059285
4	2	-0.07806
4	3	0.017148
;

data HISINCAVG_M;
   input DYIDX INC;
   datalines;
1	-0.072240154
2	-0.075171085
3	-0.077492213
;

PROC OPTMODEL;
   num n = 4;
   SET TS = 1..n;
   SET IDX = 1..n;
   NUM SOURCE = 0;
   NUM SINK = 1 + MAX {I in IDX} I;
   SET NODES = (IDX cross TS) UNION {<SOURCE,0>, <SINK,n+1>};
   SET ARCS = 
      setof {i in IDX} <SOURCE,i,0>
      union
      setof {i in IDX, j in IDX diff {i}, t in TS} <i,j,t>
      union
      setof {i in IDX} <i,SINK,n>
   ;
   put NODES=;
   put ARCS=;

   NUM INCS {IDX, IDX};
   NUM INCAVG {TS};
   READ DATA DYINC_M INTO [FRIDX TOIDX] INCS=INC;
   print INCS;
   READ DATA HISINCAVG_M INTO [DYIDX] INCAVG=INC;
   print INCAVG;

   VAR USEARC {ARCS} BINARY;

   NUM COST {<i,j,t> in ARCS} = if t in 1..n-1 then (INCS[I,J] - INCAVG[T])**2;
   print cost;

   MIN TOTALCOST = SUM{<i,j,t> in ARCS} COST[i,j,t]*USEARC[i,j,t];

   con FlowBalance {<i,t> in NODES}:
      sum {<(i),j,(t)> in ARCS} UseArc[i,j,t] - sum {<j,(i),(t)-1> in ARCS} UseArc[j,i,t-1]
    = (if <i,t> = <SOURCE,0> then 1 else if <i,t> = <SINK,n+1> then -1 else 0);

   con VisitOnce {j in IDX}:
      sum {<i,(j),t> in ARCS} UseArc[i,j,t] = 1;

   expand;
   solve;
   print {<i,j,t> in ARCS: UseArc[i,j,t].sol > 0.5} cost;
QUIT;

The resulting optimal solution uses the following arcs:

[1] [2] [3] COST
0 1 0 0.00000000
1 3 1 0.00462806
2 5 4 0.00000000
3 4 2 0.00053412
4 2 3 0.00000032

The corresponding path is 1->3->4->2.

 

luboxing
Obsidian | Level 7

Thanks, Rob! I did encounter one error, however:

 

57 con FlowBalance {<i,t> in NODES}:
58 sum {<(i),j,(t)> in ARCS} UseArc[i,j,t] - sum {<j,(i),(t)-1> in ARCS} UseArc[j,i,t-1]
59 = (if <i,t> = <SOURCE,0> then 1 else if <i,t> = <SINK,n+1> then -1 else 0);
_
22
76
ERROR 22-322: Syntax error, expecting one of the following: IN, NOT, ~.

ERROR 76-322: Syntax error, statement will be ignored.

 

Any ideas?

RobPratt
SAS Super FREQ

It looks like you are using an older version that does not support comparison of tuples.  I recommend upgrading, but until then here are three workarounds:

    = (if <i,t> in {<SOURCE,0>} then 1 else if <i,t> in {<SINK,n+1>} then -1 else 0);
    = (if i = SOURCE and t = 0 then 1 else if i = SINK and t = n+1 then -1 else 0);
   = (if i = SOURCE then 1 else if i = SINK then -1 else 0);

 

luboxing
Obsidian | Level 7

Thanks, Rob! It works like a charm!

My actual usage has i=0..30, j=1-30, and t=1..30. With your code it takes around 15 seconds to solve. 

SAS Innovate 2025: Call for Content

Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 25. Read more here about why you should contribute and what is in it for you!

Submit your idea!

Multiple Linear Regression in SAS

Learn how to run multiple linear regression models with and without interactions, presented by SAS user Alex Chaplin.

Find more tutorials on the SAS Users YouTube channel.

Discussion stats
  • 6 replies
  • 973 views
  • 0 likes
  • 2 in conversation