The source code and dockerfile for the GSW2024 AI Lab.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

535 lines
18 KiB

4 weeks ago
  1. /* main.c */
  2. /* Written by Andrew Makhorin <mao@gnu.org>, October 2015. */
  3. /***********************************************************************
  4. * This program is a stand-alone solver intended for solving Symmetric
  5. * Traveling Salesman Problem (TSP) with the branch-and-bound method.
  6. *
  7. * Please note that this program is only an illustrative example. It is
  8. * *not* a state-of-the-art code, so only small TSP instances (perhaps,
  9. * having up to 150-200 nodes) can be solved with this code.
  10. *
  11. * To run this program use the following command:
  12. *
  13. * tspsol tsp-file
  14. *
  15. * where tsp-file specifies an input text file containing TSP data in
  16. * TSPLIB 95 format.
  17. *
  18. * Detailed description of the input format recognized by this program
  19. * is given in the report: Gerhard Reinelt, "TSPLIB 95". This report as
  20. * well as TSPLIB, a library of sample TSP instances (and other related
  21. * problems), are freely available for research purposes at the webpage
  22. * <http://www.iwr.uni-heidelberg.de/groups/comopt/software/TSPLIB95/>.
  23. *
  24. * Symmetric Traveling Salesman Problem
  25. * ------------------------------------
  26. * Let a complete undirected graph be given:
  27. *
  28. * K = (V, E), (1)
  29. *
  30. * where V = {1, ..., n} is a set of nodes, E = V cross V is a set of
  31. * edges. Let also each edge e = (i,j) be assigned a positive number
  32. * c[i,j], which is the length of e. The Symmetric Traveling Salesman
  33. * Problem (TSP) is to find a tour in K of minimal length.
  34. *
  35. * Integer programming formulation of TSP
  36. * --------------------------------------
  37. * For a set of nodes W within V introduce the following notation:
  38. *
  39. * d(W) = {(i,j):i in W and j not in W or i not in W and j in W}, (2)
  40. *
  41. * i.e. d(W) is the set of edges which have exactly one endnode in W.
  42. * If W = {v}, i.e. W consists of the only node, we write simply d(v).
  43. *
  44. * The integer programming formulation of TSP is the following:
  45. *
  46. * minimize sum c[i,j] * x[i,j] (3)
  47. * i,j
  48. *
  49. * subject to sum x[i,j] = 2 for all v in V (4)
  50. * (i,j) in d(v)
  51. *
  52. * sum x[i,j] >= 2 for all W within V, (5)
  53. * (i,j) in d(W) W != empty, W != V
  54. *
  55. * x[i,j] in {0, 1} for all i, j (6)
  56. *
  57. * The binary variables x[i,j] have conventional meaning: if x[i,j] = 1,
  58. * the edge (i,j) is included in the tour, otherwise, if x[i,j] = 0, the
  59. * edge is not included in the tour.
  60. *
  61. * The constraints (4) are called degree constraints. They require that
  62. * for each node v in V there must be exactly two edges included in the
  63. * tour which are incident to v.
  64. *
  65. * The constraints (5) are called subtour elimination constraints. They
  66. * are used to forbid subtours. Note that the number of the subtour
  67. * elimination constraints grows exponentially on the size of the TSP
  68. * instance, so these constraints are not included explicitly in the
  69. * IP, but generated dynamically during the B&B search.
  70. ***********************************************************************/
  71. #include <errno.h>
  72. #include <math.h>
  73. #include <stdio.h>
  74. #include <stdlib.h>
  75. #include <string.h>
  76. #include <time.h>
  77. #include <glpk.h>
  78. #include "maxflow.h"
  79. #include "mincut.h"
  80. #include "misc.h"
  81. #include "tsplib.h"
  82. int n;
  83. /* number of nodes in the problem, n >= 2 */
  84. int *c; /* int c[1+n*(n-1)/2]; */
  85. /* upper triangle (without diagonal entries) of the (symmetric) matrix
  86. * C = (c[i,j]) in row-wise format, where c[i,j] specifies a length of
  87. * edge e = (i,j), 1 <= i < j <= n */
  88. int *tour; /* int tour[1+n]; */
  89. /* solution to TSP, which is a tour specified by the list of node
  90. * numbers tour[1] -> ... -> tour[nn] -> tour[1] in the order the nodes
  91. * are visited; note that any tour is a permutation of node numbers */
  92. glp_prob *P;
  93. /* integer programming problem object */
  94. /***********************************************************************
  95. * loc - determine reduced index of element of symmetric matrix
  96. *
  97. * Given indices i and j of an element of a symmetric nxn-matrix,
  98. * 1 <= i, j <= n, i != j, this routine returns the index of that
  99. * element in an array, which is the upper triangle (without diagonal
  100. * entries) of the matrix in row-wise format. */
  101. int loc(int i, int j)
  102. { xassert(1 <= i && i <= n);
  103. xassert(1 <= j && j <= n);
  104. xassert(i != j);
  105. if (i < j)
  106. return ((n - 1) + (n - i + 1)) * (i - 1) / 2 + (j - i);
  107. else
  108. return loc(j, i);
  109. }
  110. /***********************************************************************
  111. * read_data - read TSP data
  112. *
  113. * This routine reads TSP data from a specified text file in TSPLIB 95
  114. * format. */
  115. void read_data(const char *fname)
  116. { TSP *tsp;
  117. int i, j;
  118. tsp = tsp_read_data(fname);
  119. if (tsp == NULL)
  120. { xprintf("TSP data file processing error\n");
  121. exit(EXIT_FAILURE);
  122. }
  123. if (tsp->type != TSP_TSP)
  124. { xprintf("Invalid TSP data type\n");
  125. exit(EXIT_FAILURE);
  126. }
  127. n = tsp->dimension;
  128. xassert(n >= 2);
  129. if (n > 32768)
  130. { xprintf("TSP instance too large\n");
  131. exit(EXIT_FAILURE);
  132. }
  133. c = xalloc(1+loc(n-1, n), sizeof(int));
  134. for (i = 1; i <= n; i++)
  135. { for (j = i+1; j <= n; j++)
  136. c[loc(i, j)] = tsp_distance(tsp, i, j);
  137. }
  138. tsp_free_data(tsp);
  139. return;
  140. }
  141. /***********************************************************************
  142. * build_prob - build initial integer programming problem
  143. *
  144. * This routine builds the initial integer programming problem, which
  145. * includes all variables (6), objective (3) and all degree constraints
  146. * (4). Subtour elimination constraints (5) are considered "lazy" and
  147. * not included in the initial problem. */
  148. void build_prob(void)
  149. { int i, j, k, *ind;
  150. double *val;
  151. char name[50];
  152. /* create problem object */
  153. P = glp_create_prob();
  154. /* add all binary variables (6) */
  155. for (i = 1; i <= n; i++)
  156. { for (j = i+1; j <= n; j++)
  157. { k = glp_add_cols(P, 1);
  158. xassert(k == loc(i,j));
  159. sprintf(name, "x[%d,%d]", i, j);
  160. glp_set_col_name(P, k, name);
  161. glp_set_col_kind(P, k, GLP_BV);
  162. /* set objective coefficient (3) */
  163. glp_set_obj_coef(P, k, c[k]);
  164. }
  165. }
  166. /* add all degree constraints (4) */
  167. ind = xalloc(1+n, sizeof(int));
  168. val = xalloc(1+n, sizeof(double));
  169. for (i = 1; i <= n; i++)
  170. { k = glp_add_rows(P, 1);
  171. xassert(k == i);
  172. sprintf(name, "v[%d]", i);
  173. glp_set_row_name(P, i, name);
  174. glp_set_row_bnds(P, i, GLP_FX, 2, 2);
  175. k = 0;
  176. for (j = 1; j <= n; j++)
  177. { if (i != j)
  178. k++, ind[k] = loc(i,j), val[k] = 1;
  179. }
  180. xassert(k == n-1);
  181. glp_set_mat_row(P, i, n-1, ind, val);
  182. }
  183. xfree(ind);
  184. xfree(val);
  185. return;
  186. }
  187. /***********************************************************************
  188. * build_tour - build tour for corresponding solution to IP
  189. *
  190. * Given a feasible solution to IP (3)-(6) this routine builds the
  191. * corresponding solution to TSP, which is a tour specified by the list
  192. * of node numbers tour[1] -> ... -> tour[nn] -> tour[1] in the order
  193. * the nodes are to be visited */
  194. void build_tour(void)
  195. { int i, j, k, kk, *beg, *end;
  196. /* solution to MIP should be feasible */
  197. switch (glp_mip_status(P))
  198. { case GLP_FEAS:
  199. case GLP_OPT:
  200. break;
  201. default:
  202. xassert(P != P);
  203. }
  204. /* build the list of edges included in the tour */
  205. beg = xalloc(1+n, sizeof(int));
  206. end = xalloc(1+n, sizeof(int));
  207. k = 0;
  208. for (i = 1; i <= n; i++)
  209. { for (j = i+1; j <= n; j++)
  210. { double x;
  211. x = glp_mip_col_val(P, loc(i,j));
  212. xassert(x == 0 || x == 1);
  213. if (x)
  214. { k++;
  215. xassert(k <= n);
  216. beg[k] = i, end[k] = j;
  217. }
  218. }
  219. }
  220. xassert(k == n);
  221. /* reorder edges in the list as they follow in the tour */
  222. for (k = 1; k <= n; k++)
  223. { /* find k-th edge of the tour */
  224. j = (k == 1 ? 1 : end[k-1]);
  225. for (kk = k; kk <= n; kk++)
  226. { if (beg[kk] == j)
  227. break;
  228. if (end[kk] == j)
  229. { end[kk] = beg[kk], beg[kk] = j;
  230. break;
  231. }
  232. }
  233. xassert(kk <= n);
  234. /* put the edge to k-th position in the list */
  235. i = beg[k], beg[k] = beg[kk], beg[kk] = i;
  236. j = end[k], end[k] = end[kk], end[kk] = j;
  237. }
  238. /* build the tour starting from node 1 */
  239. xassert(beg[1] == 1);
  240. for (k = 1; k <= n; k++)
  241. { if (k > 1)
  242. xassert(end[k-1] == beg[k]);
  243. tour[k] = beg[k];
  244. }
  245. xassert(end[n] == 1);
  246. xfree(beg);
  247. xfree(end);
  248. return;
  249. }
  250. /***********************************************************************
  251. * tour_length - calculate tour length
  252. *
  253. * This routine calculates the length of the specified tour, which is
  254. * the sum of corresponding edge lengths. */
  255. int tour_length(const int tour[/*1+n*/])
  256. { int i, j, sum;
  257. sum = 0;
  258. for (i = 1; i <= n; i++)
  259. { j = (i < n ? i+1 : 1);
  260. sum += c[loc(tour[i], tour[j])];
  261. }
  262. return sum;
  263. }
  264. /***********************************************************************
  265. * write_tour - write tour to text file in TSPLIB format
  266. *
  267. * This routine writes the specified tour to a text file in TSPLIB
  268. * format. */
  269. void write_tour(const char *fname, const int tour[/*1+n*/])
  270. { FILE *fp;
  271. int i;
  272. xprintf("Writing TSP solution to '%s'...\n", fname);
  273. fp = fopen(fname, "w");
  274. if (fp == NULL)
  275. { xprintf("Unable to create '%s' - %s\n", fname,
  276. strerror(errno));
  277. return;
  278. }
  279. fprintf(fp, "NAME : %s\n", fname);
  280. fprintf(fp, "COMMENT : Tour length is %d\n", tour_length(tour));
  281. fprintf(fp, "TYPE : TOUR\n");
  282. fprintf(fp, "DIMENSION : %d\n", n);
  283. fprintf(fp, "TOUR_SECTION\n");
  284. for (i = 1; i <= n; i++)
  285. fprintf(fp, "%d\n", tour[i]);
  286. fprintf(fp, "-1\n");
  287. fprintf(fp, "EOF\n");
  288. fclose(fp);
  289. return;
  290. }
  291. /***********************************************************************
  292. * gen_subt_row - generate violated subtour elimination constraint
  293. *
  294. * This routine is called from the MIP solver to generate a violated
  295. * subtour elimination constraint (5).
  296. *
  297. * Constraints of this class has the form:
  298. *
  299. * sum x[i,j] >= 2, i in W, j in V \ W,
  300. *
  301. * for all W, where W is a proper nonempty subset of V, V is the set of
  302. * nodes of the given graph.
  303. *
  304. * In order to find a violated constraint of this class this routine
  305. * finds a min cut in a capacitated network, which has the same sets of
  306. * nodes and edges as the original graph, and where capacities of edges
  307. * are values of variables x[i,j] in a basic solution to LP relaxation
  308. * of the current subproblem. */
  309. void gen_subt(glp_tree *T)
  310. { int i, j, ne, nz, *beg, *end, *cap, *cut, *ind;
  311. double sum, *val;
  312. /* MIP preprocessor should not be used */
  313. xassert(glp_ios_get_prob(T) == P);
  314. /* if some variable x[i,j] is zero in basic solution, then the
  315. * capacity of corresponding edge in the associated network is
  316. * zero, so we may not include such edge in the network */
  317. /* count number of edges having non-zero capacity */
  318. ne = 0;
  319. for (i = 1; i <= n; i++)
  320. { for (j = i+1; j <= n; j++)
  321. { if (glp_get_col_prim(P, loc(i,j)) >= .001)
  322. ne++;
  323. }
  324. }
  325. /* build the capacitated network */
  326. beg = xalloc(1+ne, sizeof(int));
  327. end = xalloc(1+ne, sizeof(int));
  328. cap = xalloc(1+ne, sizeof(int));
  329. nz = 0;
  330. for (i = 1; i <= n; i++)
  331. { for (j = i+1; j <= n; j++)
  332. { if (glp_get_col_prim(P, loc(i,j)) >= .001)
  333. { nz++;
  334. xassert(nz <= ne);
  335. beg[nz] = i, end[nz] = j;
  336. /* scale all edge capacities to make them integral */
  337. cap[nz] = ceil(1000 * glp_get_col_prim(P, loc(i,j)));
  338. }
  339. }
  340. }
  341. xassert(nz == ne);
  342. /* find minimal cut in the capacitated network */
  343. cut = xalloc(1+n, sizeof(int));
  344. min_cut(n, ne, beg, end, cap, cut);
  345. /* determine the number of non-zero coefficients in the subtour
  346. * elimination constraint and calculate its left-hand side which
  347. * is the (unscaled) capacity of corresponding min cut */
  348. ne = 0, sum = 0;
  349. for (i = 1; i <= n; i++)
  350. { for (j = i+1; j <= n; j++)
  351. { if (cut[i] && !cut[j] || !cut[i] && cut[j])
  352. { ne++;
  353. sum += glp_get_col_prim(P, loc(i,j));
  354. }
  355. }
  356. }
  357. /* if the (unscaled) capacity of min cut is less than 2, the
  358. * corresponding subtour elimination constraint is violated */
  359. if (sum <= 1.999)
  360. { /* build the list of non-zero coefficients */
  361. ind = xalloc(1+ne, sizeof(int));
  362. val = xalloc(1+ne, sizeof(double));
  363. nz = 0;
  364. for (i = 1; i <= n; i++)
  365. { for (j = i+1; j <= n; j++)
  366. { if (cut[i] && !cut[j] || !cut[i] && cut[j])
  367. { nz++;
  368. xassert(nz <= ne);
  369. ind[nz] = loc(i,j);
  370. val[nz] = 1;
  371. }
  372. }
  373. }
  374. xassert(nz == ne);
  375. /* add violated tour elimination constraint to the current
  376. * subproblem */
  377. i = glp_add_rows(P, 1);
  378. glp_set_row_bnds(P, i, GLP_LO, 2, 0);
  379. glp_set_mat_row(P, i, nz, ind, val);
  380. xfree(ind);
  381. xfree(val);
  382. }
  383. /* free working arrays */
  384. xfree(beg);
  385. xfree(end);
  386. xfree(cap);
  387. xfree(cut);
  388. return;
  389. }
  390. /***********************************************************************
  391. * cb_func - application callback routine
  392. *
  393. * This routine is called from the MIP solver at various points of
  394. * the branch-and-cut algorithm. */
  395. void cb_func(glp_tree *T, void *info)
  396. { xassert(info == info);
  397. switch (glp_ios_reason(T))
  398. { case GLP_IROWGEN:
  399. /* generate one violated subtour elimination constraint */
  400. gen_subt(T);
  401. break;
  402. }
  403. return;
  404. }
  405. /***********************************************************************
  406. * main - TSP solver main program
  407. *
  408. * This main program parses command-line arguments, reads specified TSP
  409. * instance from a text file, and calls the MIP solver to solve it. */
  410. int main(int argc, char *argv[])
  411. { int j;
  412. char *in_file = NULL, *out_file = NULL;
  413. time_t start;
  414. glp_iocp iocp;
  415. /* parse command-line arguments */
  416. # define p(str) (strcmp(argv[j], str) == 0)
  417. for (j = 1; j < argc; j++)
  418. { if (p("--output") || p("-o"))
  419. { j++;
  420. if (j == argc || argv[j][0] == '\0' || argv[j][0] == '-')
  421. { xprintf("No solution output file specified\n");
  422. exit(EXIT_FAILURE);
  423. }
  424. if (out_file != NULL)
  425. { xprintf("Only one solution output file allowed\n");
  426. exit(EXIT_FAILURE);
  427. }
  428. out_file = argv[j];
  429. }
  430. else if (p("--help") || p("-h"))
  431. { xprintf("Usage: %s [options...] tsp-file\n", argv[0]);
  432. xprintf("\n");
  433. xprintf("Options:\n");
  434. xprintf(" -o filename, --output filename\n");
  435. xprintf(" write solution to filename\n")
  436. ;
  437. xprintf(" -h, --help display this help information"
  438. " and exit\n");
  439. exit(EXIT_SUCCESS);
  440. }
  441. else if (argv[j][0] == '-' ||
  442. (argv[j][0] == '-' && argv[j][1] == '-'))
  443. { xprintf("Invalid option '%s'; try %s --help\n", argv[j],
  444. argv[0]);
  445. exit(EXIT_FAILURE);
  446. }
  447. else
  448. { if (in_file != NULL)
  449. { xprintf("Only one input file allowed\n");
  450. exit(EXIT_FAILURE);
  451. }
  452. in_file = argv[j];
  453. }
  454. }
  455. if (in_file == NULL)
  456. { xprintf("No input file specified; try %s --help\n", argv[0]);
  457. exit(EXIT_FAILURE);
  458. }
  459. # undef p
  460. /* display program banner */
  461. xprintf("TSP Solver for GLPK %s\n", glp_version());
  462. /* remove output solution file specified in command-line */
  463. if (out_file != NULL)
  464. remove(out_file);
  465. /* read TSP instance from input data file */
  466. read_data(in_file);
  467. /* build initial IP problem */
  468. start = time(NULL);
  469. build_prob();
  470. tour = xalloc(1+n, sizeof(int));
  471. /* solve LP relaxation of initial IP problem */
  472. xprintf("Solving initial LP relaxation...\n");
  473. xassert(glp_simplex(P, NULL) == 0);
  474. xassert(glp_get_status(P) == GLP_OPT);
  475. /* solve IP problem with "lazy" constraints */
  476. glp_init_iocp(&iocp);
  477. iocp.br_tech = GLP_BR_MFV; /* most fractional variable */
  478. iocp.bt_tech = GLP_BT_BLB; /* best local bound */
  479. iocp.sr_heur = GLP_OFF; /* disable simple rounding heuristic */
  480. iocp.gmi_cuts = GLP_ON; /* enable Gomory cuts */
  481. iocp.cb_func = cb_func;
  482. glp_intopt(P, &iocp);
  483. build_tour();
  484. /* display some statistics */
  485. xprintf("Time used: %.1f secs\n", difftime(time(NULL), start));
  486. { size_t tpeak;
  487. glp_mem_usage(NULL, NULL, NULL, &tpeak);
  488. xprintf("Memory used: %.1f Mb (%.0f bytes)\n",
  489. (double)tpeak / 1048576.0, (double)tpeak);
  490. }
  491. /* write solution to output file, if required */
  492. if (out_file != NULL)
  493. write_tour(out_file, tour);
  494. /* deallocate working objects */
  495. xfree(c);
  496. xfree(tour);
  497. glp_delete_prob(P);
  498. /* check that no memory blocks are still allocated */
  499. { int count;
  500. size_t total;
  501. glp_mem_usage(&count, NULL, &total, NULL);
  502. if (count != 0)
  503. xerror("Error: %d memory block(s) were lost\n", count);
  504. xassert(total == 0);
  505. }
  506. return 0;
  507. }
  508. /* eof */