Skip to content

Commit 3e32c29

Browse files
Merge pull request #10 from abhosekar/master
tsp4 subtour identification improved using networkx
2 parents 9634aa7 + c151534 commit 3e32c29

File tree

1 file changed

+16
-47
lines changed

1 file changed

+16
-47
lines changed

models/tsp4/tsp4.py

Lines changed: 16 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from __future__ import annotations
2222

2323
from pathlib import Path
24+
import networkx as nx
2425

2526
import gamspy.math as gams_math
2627
from gamspy import (
@@ -167,9 +168,6 @@ def main():
167168
# Parameter
168169
cutcoeff = Parameter(m, name="cutcoeff", domain=[cc, i, j])
169170
rhs = Parameter(m, name="rhs", domain=cc)
170-
nosubtours = Parameter(
171-
m, name="nosubtours", description="number of subtours"
172-
)
173171

174172
# Equation
175173
cut = Equation(m, name="cut", domain=cc, description="dynamic cuts")
@@ -190,51 +188,22 @@ def main():
190188
curcut[cc].where[Ord(cc) == 1] = True
191189

192190
for ccc_loop in ccc.toList():
193-
# initialize
194-
fromi[i].where[Ord(i) == 1] = True # turn first element on
195-
tt[t].where[Ord(t) == 1] = True # turn first element on
196-
tour[i, j, t] = False
197-
visited[i] = False
198-
199-
for _ in i.toList():
200-
nextj[j].where[x.l[fromi, j] > 0.5] = (
201-
True # check x.l(fromi,j) = 1 would be dangerous
202-
)
203-
tour[fromi, nextj, tt] = True # store in table
204-
visited[fromi] = True # mark city 'fromi' as visited
205-
fromi[j] = nextj[j]
206-
207-
if nextj.toList()[0] in visited.toList(): # if already visited...
208-
tt[t] = tt[t - 1]
209-
for ix_loop in ix.toList():
210-
if (
211-
ix_loop in visited.toList()
212-
): # find starting point of new subtour
213-
continue
214-
fromi[ix_loop] = True
215-
216-
nosubtours[...] = Sum(t, gams_math.Max(0, Smax(tour[i, j, t], 1)))
217-
218-
if nosubtours.toValue() == 1: # done: no subtours
191+
G = nx.DiGraph()
192+
G.add_nodes_from(i.getUELs())
193+
x_filtered = x.records[x.records['level'] > 0.5].loc[:, :"level"]
194+
edges = list(zip(x_filtered['ii'], x_filtered['jj']))
195+
G.add_edges_from(edges)
196+
S = [G.subgraph(c).copy() for c in nx.strongly_connected_components(G)]
197+
198+
nosubtours = len(S)
199+
if nosubtours == 1: # done: no subtours
219200
break
220201

221-
# introduce cut
222-
for idx, t_loop in enumerate(t.toList()):
223-
if idx + 1 > nosubtours.toValue():
224-
continue
202+
for s in S:
225203
rhs[curcut] = -1
226-
227-
for i_loop, j_loop, t_loop2, _ in tour.records.itertuples(
228-
index=False
229-
):
230-
if t_loop2 != t_loop:
231-
continue
232-
233-
cutcoeff[curcut, i_loop, j_loop].where[
234-
x.l[i_loop, j_loop] > 0.5
235-
] = 1
236-
# not needed due to nature of assignment constraints
237-
# cutcoeff(curcut, i, j)$(x.l[i,j] < 0.5) = -1
204+
for u, v in s.edges():
205+
if x.l[u, v].records > 0.5:
206+
cutcoeff[curcut, i, j].where[i.sameAs(u) & j.sameAs(v)] = 1
238207
rhs[curcut] = rhs[curcut] + 1
239208
allcuts[curcut] = True # include this cut in set
240209
curcut[cc] = curcut[cc - 1]
@@ -244,10 +213,10 @@ def main():
244213
"Cut: ",
245214
ccc_loop,
246215
"\t\t # of subtours remaining: ",
247-
nosubtours.toValue() - 1,
216+
nosubtours - 1,
248217
)
249218

250-
if nosubtours.toValue() != 1:
219+
if nosubtours != 1:
251220
raise GamspyException("Too many cuts needed")
252221

253222
print("No subtours remaining. Solution found!!\n")

0 commit comments

Comments
 (0)