1 /* glpapi13.c (branch-and-bound interface routines) */
2
3 /***********************************************************************
4 * This code is part of GLPK (GNU Linear Programming Kit).
5 * Copyright (C) 2000-2018 Free Software Foundation, Inc.
6 * Written by Andrew Makhorin <mao@gnu.org>.
7 *
8 * GLPK is free software: you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * GLPK is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
16 * License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with GLPK. If not, see <http://www.gnu.org/licenses/>.
20 ***********************************************************************/
21
22 #include "env.h"
23 #include "ios.h"
24
25 /***********************************************************************
26 * NAME
27 *
28 * glp_ios_reason - determine reason for calling the callback routine
29 *
30 * SYNOPSIS
31 *
32 * glp_ios_reason(glp_tree *tree);
33 *
34 * RETURNS
35 *
36 * The routine glp_ios_reason returns a code, which indicates why the
37 * user-defined callback routine is being called. */
38
glp_ios_reason(glp_tree * tree)39 int glp_ios_reason(glp_tree *tree)
40 { return
41 tree->reason;
42 }
43
44 /***********************************************************************
45 * NAME
46 *
47 * glp_ios_get_prob - access the problem object
48 *
49 * SYNOPSIS
50 *
51 * glp_prob *glp_ios_get_prob(glp_tree *tree);
52 *
53 * DESCRIPTION
54 *
55 * The routine glp_ios_get_prob can be called from the user-defined
56 * callback routine to access the problem object, which is used by the
57 * MIP solver. It is the original problem object passed to the routine
58 * glp_intopt if the MIP presolver is not used; otherwise it is an
59 * internal problem object built by the presolver. If the current
60 * subproblem exists, LP segment of the problem object corresponds to
61 * its LP relaxation.
62 *
63 * RETURNS
64 *
65 * The routine glp_ios_get_prob returns a pointer to the problem object
66 * used by the MIP solver. */
67
glp_ios_get_prob(glp_tree * tree)68 glp_prob *glp_ios_get_prob(glp_tree *tree)
69 { return
70 tree->mip;
71 }
72
73 /***********************************************************************
74 * NAME
75 *
76 * glp_ios_tree_size - determine size of the branch-and-bound tree
77 *
78 * SYNOPSIS
79 *
80 * void glp_ios_tree_size(glp_tree *tree, int *a_cnt, int *n_cnt,
81 * int *t_cnt);
82 *
83 * DESCRIPTION
84 *
85 * The routine glp_ios_tree_size stores the following three counts which
86 * characterize the current size of the branch-and-bound tree:
87 *
88 * a_cnt is the current number of active nodes, i.e. the current size of
89 * the active list;
90 *
91 * n_cnt is the current number of all (active and inactive) nodes;
92 *
93 * t_cnt is the total number of nodes including those which have been
94 * already removed from the tree. This count is increased whenever
95 * a new node appears in the tree and never decreased.
96 *
97 * If some of the parameters a_cnt, n_cnt, t_cnt is a null pointer, the
98 * corresponding count is not stored. */
99
glp_ios_tree_size(glp_tree * tree,int * a_cnt,int * n_cnt,int * t_cnt)100 void glp_ios_tree_size(glp_tree *tree, int *a_cnt, int *n_cnt,
101 int *t_cnt)
102 { if (a_cnt != NULL) *a_cnt = tree->a_cnt;
103 if (n_cnt != NULL) *n_cnt = tree->n_cnt;
104 if (t_cnt != NULL) *t_cnt = tree->t_cnt;
105 return;
106 }
107
108 /***********************************************************************
109 * NAME
110 *
111 * glp_ios_curr_node - determine current active subproblem
112 *
113 * SYNOPSIS
114 *
115 * int glp_ios_curr_node(glp_tree *tree);
116 *
117 * RETURNS
118 *
119 * The routine glp_ios_curr_node returns the reference number of the
120 * current active subproblem. However, if the current subproblem does
121 * not exist, the routine returns zero. */
122
glp_ios_curr_node(glp_tree * tree)123 int glp_ios_curr_node(glp_tree *tree)
124 { IOSNPD *node;
125 /* obtain pointer to the current subproblem */
126 node = tree->curr;
127 /* return its reference number */
128 return node == NULL ? 0 : node->p;
129 }
130
131 /***********************************************************************
132 * NAME
133 *
134 * glp_ios_next_node - determine next active subproblem
135 *
136 * SYNOPSIS
137 *
138 * int glp_ios_next_node(glp_tree *tree, int p);
139 *
140 * RETURNS
141 *
142 * If the parameter p is zero, the routine glp_ios_next_node returns
143 * the reference number of the first active subproblem. However, if the
144 * tree is empty, zero is returned.
145 *
146 * If the parameter p is not zero, it must specify the reference number
147 * of some active subproblem, in which case the routine returns the
148 * reference number of the next active subproblem. However, if there is
149 * no next active subproblem in the list, zero is returned.
150 *
151 * All subproblems in the active list are ordered chronologically, i.e.
152 * subproblem A precedes subproblem B if A was created before B. */
153
glp_ios_next_node(glp_tree * tree,int p)154 int glp_ios_next_node(glp_tree *tree, int p)
155 { IOSNPD *node;
156 if (p == 0)
157 { /* obtain pointer to the first active subproblem */
158 node = tree->head;
159 }
160 else
161 { /* obtain pointer to the specified subproblem */
162 if (!(1 <= p && p <= tree->nslots))
163 err: xerror("glp_ios_next_node: p = %d; invalid subproblem refer"
164 "ence number\n", p);
165 node = tree->slot[p].node;
166 if (node == NULL) goto err;
167 /* the specified subproblem must be active */
168 if (node->count != 0)
169 xerror("glp_ios_next_node: p = %d; subproblem not in the ac"
170 "tive list\n", p);
171 /* obtain pointer to the next active subproblem */
172 node = node->next;
173 }
174 /* return the reference number */
175 return node == NULL ? 0 : node->p;
176 }
177
178 /***********************************************************************
179 * NAME
180 *
181 * glp_ios_prev_node - determine previous active subproblem
182 *
183 * SYNOPSIS
184 *
185 * int glp_ios_prev_node(glp_tree *tree, int p);
186 *
187 * RETURNS
188 *
189 * If the parameter p is zero, the routine glp_ios_prev_node returns
190 * the reference number of the last active subproblem. However, if the
191 * tree is empty, zero is returned.
192 *
193 * If the parameter p is not zero, it must specify the reference number
194 * of some active subproblem, in which case the routine returns the
195 * reference number of the previous active subproblem. However, if there
196 * is no previous active subproblem in the list, zero is returned.
197 *
198 * All subproblems in the active list are ordered chronologically, i.e.
199 * subproblem A precedes subproblem B if A was created before B. */
200
glp_ios_prev_node(glp_tree * tree,int p)201 int glp_ios_prev_node(glp_tree *tree, int p)
202 { IOSNPD *node;
203 if (p == 0)
204 { /* obtain pointer to the last active subproblem */
205 node = tree->tail;
206 }
207 else
208 { /* obtain pointer to the specified subproblem */
209 if (!(1 <= p && p <= tree->nslots))
210 err: xerror("glp_ios_prev_node: p = %d; invalid subproblem refer"
211 "ence number\n", p);
212 node = tree->slot[p].node;
213 if (node == NULL) goto err;
214 /* the specified subproblem must be active */
215 if (node->count != 0)
216 xerror("glp_ios_prev_node: p = %d; subproblem not in the ac"
217 "tive list\n", p);
218 /* obtain pointer to the previous active subproblem */
219 node = node->prev;
220 }
221 /* return the reference number */
222 return node == NULL ? 0 : node->p;
223 }
224
225 /***********************************************************************
226 * NAME
227 *
228 * glp_ios_up_node - determine parent subproblem
229 *
230 * SYNOPSIS
231 *
232 * int glp_ios_up_node(glp_tree *tree, int p);
233 *
234 * RETURNS
235 *
236 * The parameter p must specify the reference number of some (active or
237 * inactive) subproblem, in which case the routine iet_get_up_node
238 * returns the reference number of its parent subproblem. However, if
239 * the specified subproblem is the root of the tree and, therefore, has
240 * no parent, the routine returns zero. */
241
glp_ios_up_node(glp_tree * tree,int p)242 int glp_ios_up_node(glp_tree *tree, int p)
243 { IOSNPD *node;
244 /* obtain pointer to the specified subproblem */
245 if (!(1 <= p && p <= tree->nslots))
246 err: xerror("glp_ios_up_node: p = %d; invalid subproblem reference "
247 "number\n", p);
248 node = tree->slot[p].node;
249 if (node == NULL) goto err;
250 /* obtain pointer to the parent subproblem */
251 node = node->up;
252 /* return the reference number */
253 return node == NULL ? 0 : node->p;
254 }
255
256 /***********************************************************************
257 * NAME
258 *
259 * glp_ios_node_level - determine subproblem level
260 *
261 * SYNOPSIS
262 *
263 * int glp_ios_node_level(glp_tree *tree, int p);
264 *
265 * RETURNS
266 *
267 * The routine glp_ios_node_level returns the level of the subproblem,
268 * whose reference number is p, in the branch-and-bound tree. (The root
269 * subproblem has level 0, and the level of any other subproblem is the
270 * level of its parent plus one.) */
271
glp_ios_node_level(glp_tree * tree,int p)272 int glp_ios_node_level(glp_tree *tree, int p)
273 { IOSNPD *node;
274 /* obtain pointer to the specified subproblem */
275 if (!(1 <= p && p <= tree->nslots))
276 err: xerror("glp_ios_node_level: p = %d; invalid subproblem referen"
277 "ce number\n", p);
278 node = tree->slot[p].node;
279 if (node == NULL) goto err;
280 /* return the node level */
281 return node->level;
282 }
283
284 /***********************************************************************
285 * NAME
286 *
287 * glp_ios_node_bound - determine subproblem local bound
288 *
289 * SYNOPSIS
290 *
291 * double glp_ios_node_bound(glp_tree *tree, int p);
292 *
293 * RETURNS
294 *
295 * The routine glp_ios_node_bound returns the local bound for (active or
296 * inactive) subproblem, whose reference number is p.
297 *
298 * COMMENTS
299 *
300 * The local bound for subproblem p is an lower (minimization) or upper
301 * (maximization) bound for integer optimal solution to this subproblem
302 * (not to the original problem). This bound is local in the sense that
303 * only subproblems in the subtree rooted at node p cannot have better
304 * integer feasible solutions.
305 *
306 * On creating a subproblem (due to the branching step) its local bound
307 * is inherited from its parent and then may get only stronger (never
308 * weaker). For the root subproblem its local bound is initially set to
309 * -DBL_MAX (minimization) or +DBL_MAX (maximization) and then improved
310 * as the root LP relaxation has been solved.
311 *
312 * Note that the local bound is not necessarily the optimal objective
313 * value to corresponding LP relaxation; it may be stronger. */
314
glp_ios_node_bound(glp_tree * tree,int p)315 double glp_ios_node_bound(glp_tree *tree, int p)
316 { IOSNPD *node;
317 /* obtain pointer to the specified subproblem */
318 if (!(1 <= p && p <= tree->nslots))
319 err: xerror("glp_ios_node_bound: p = %d; invalid subproblem referen"
320 "ce number\n", p);
321 node = tree->slot[p].node;
322 if (node == NULL) goto err;
323 /* return the node local bound */
324 return node->bound;
325 }
326
327 /***********************************************************************
328 * NAME
329 *
330 * glp_ios_best_node - find active subproblem with best local bound
331 *
332 * SYNOPSIS
333 *
334 * int glp_ios_best_node(glp_tree *tree);
335 *
336 * RETURNS
337 *
338 * The routine glp_ios_best_node returns the reference number of the
339 * active subproblem, whose local bound is best (i.e. smallest in case
340 * of minimization or largest in case of maximization). However, if the
341 * tree is empty, the routine returns zero.
342 *
343 * COMMENTS
344 *
345 * The best local bound is an lower (minimization) or upper
346 * (maximization) bound for integer optimal solution to the original
347 * MIP problem. */
348
glp_ios_best_node(glp_tree * tree)349 int glp_ios_best_node(glp_tree *tree)
350 { return
351 ios_best_node(tree);
352 }
353
354 /***********************************************************************
355 * NAME
356 *
357 * glp_ios_mip_gap - compute relative MIP gap
358 *
359 * SYNOPSIS
360 *
361 * double glp_ios_mip_gap(glp_tree *tree);
362 *
363 * DESCRIPTION
364 *
365 * The routine glp_ios_mip_gap computes the relative MIP gap with the
366 * following formula:
367 *
368 * gap = |best_mip - best_bnd| / (|best_mip| + DBL_EPSILON),
369 *
370 * where best_mip is the best integer feasible solution found so far,
371 * best_bnd is the best (global) bound. If no integer feasible solution
372 * has been found yet, gap is set to DBL_MAX.
373 *
374 * RETURNS
375 *
376 * The routine glp_ios_mip_gap returns the relative MIP gap. */
377
glp_ios_mip_gap(glp_tree * tree)378 double glp_ios_mip_gap(glp_tree *tree)
379 { return
380 ios_relative_gap(tree);
381 }
382
383 /***********************************************************************
384 * NAME
385 *
386 * glp_ios_node_data - access subproblem application-specific data
387 *
388 * SYNOPSIS
389 *
390 * void *glp_ios_node_data(glp_tree *tree, int p);
391 *
392 * DESCRIPTION
393 *
394 * The routine glp_ios_node_data allows the application accessing a
395 * memory block allocated for the subproblem (which may be active or
396 * inactive), whose reference number is p.
397 *
398 * The size of the block is defined by the control parameter cb_size
399 * passed to the routine glp_intopt. The block is initialized by binary
400 * zeros on creating corresponding subproblem, and its contents is kept
401 * until the subproblem will be removed from the tree.
402 *
403 * The application may use these memory blocks to store specific data
404 * for each subproblem.
405 *
406 * RETURNS
407 *
408 * The routine glp_ios_node_data returns a pointer to the memory block
409 * for the specified subproblem. Note that if cb_size = 0, the routine
410 * returns a null pointer. */
411
glp_ios_node_data(glp_tree * tree,int p)412 void *glp_ios_node_data(glp_tree *tree, int p)
413 { IOSNPD *node;
414 /* obtain pointer to the specified subproblem */
415 if (!(1 <= p && p <= tree->nslots))
416 err: xerror("glp_ios_node_level: p = %d; invalid subproblem referen"
417 "ce number\n", p);
418 node = tree->slot[p].node;
419 if (node == NULL) goto err;
420 /* return pointer to the application-specific data */
421 return node->data;
422 }
423
424 /***********************************************************************
425 * NAME
426 *
427 * glp_ios_row_attr - retrieve additional row attributes
428 *
429 * SYNOPSIS
430 *
431 * void glp_ios_row_attr(glp_tree *tree, int i, glp_attr *attr);
432 *
433 * DESCRIPTION
434 *
435 * The routine glp_ios_row_attr retrieves additional attributes of row
436 * i and stores them in the structure glp_attr. */
437
glp_ios_row_attr(glp_tree * tree,int i,glp_attr * attr)438 void glp_ios_row_attr(glp_tree *tree, int i, glp_attr *attr)
439 { GLPROW *row;
440 if (!(1 <= i && i <= tree->mip->m))
441 xerror("glp_ios_row_attr: i = %d; row number out of range\n",
442 i);
443 row = tree->mip->row[i];
444 attr->level = row->level;
445 attr->origin = row->origin;
446 attr->klass = row->klass;
447 return;
448 }
449
450 /**********************************************************************/
451
glp_ios_pool_size(glp_tree * tree)452 int glp_ios_pool_size(glp_tree *tree)
453 { /* determine current size of the cut pool */
454 if (tree->reason != GLP_ICUTGEN)
455 xerror("glp_ios_pool_size: operation not allowed\n");
456 xassert(tree->local != NULL);
457 #ifdef NEW_LOCAL /* 02/II-2018 */
458 return tree->local->m;
459 #else
460 return tree->local->size;
461 #endif
462 }
463
464 /**********************************************************************/
465
glp_ios_add_row(glp_tree * tree,const char * name,int klass,int flags,int len,const int ind[],const double val[],int type,double rhs)466 int glp_ios_add_row(glp_tree *tree,
467 const char *name, int klass, int flags, int len, const int ind[],
468 const double val[], int type, double rhs)
469 { /* add row (constraint) to the cut pool */
470 int num;
471 if (tree->reason != GLP_ICUTGEN)
472 xerror("glp_ios_add_row: operation not allowed\n");
473 xassert(tree->local != NULL);
474 num = ios_add_row(tree, tree->local, name, klass, flags, len,
475 ind, val, type, rhs);
476 return num;
477 }
478
479 /**********************************************************************/
480
glp_ios_del_row(glp_tree * tree,int i)481 void glp_ios_del_row(glp_tree *tree, int i)
482 { /* remove row (constraint) from the cut pool */
483 if (tree->reason != GLP_ICUTGEN)
484 xerror("glp_ios_del_row: operation not allowed\n");
485 ios_del_row(tree, tree->local, i);
486 return;
487 }
488
489 /**********************************************************************/
490
glp_ios_clear_pool(glp_tree * tree)491 void glp_ios_clear_pool(glp_tree *tree)
492 { /* remove all rows (constraints) from the cut pool */
493 if (tree->reason != GLP_ICUTGEN)
494 xerror("glp_ios_clear_pool: operation not allowed\n");
495 ios_clear_pool(tree, tree->local);
496 return;
497 }
498
499 /***********************************************************************
500 * NAME
501 *
502 * glp_ios_can_branch - check if can branch upon specified variable
503 *
504 * SYNOPSIS
505 *
506 * int glp_ios_can_branch(glp_tree *tree, int j);
507 *
508 * RETURNS
509 *
510 * If j-th variable (column) can be used to branch upon, the routine
511 * glp_ios_can_branch returns non-zero, otherwise zero. */
512
glp_ios_can_branch(glp_tree * tree,int j)513 int glp_ios_can_branch(glp_tree *tree, int j)
514 { if (!(1 <= j && j <= tree->mip->n))
515 xerror("glp_ios_can_branch: j = %d; column number out of range"
516 "\n", j);
517 return tree->non_int[j];
518 }
519
520 /***********************************************************************
521 * NAME
522 *
523 * glp_ios_branch_upon - choose variable to branch upon
524 *
525 * SYNOPSIS
526 *
527 * void glp_ios_branch_upon(glp_tree *tree, int j, int sel);
528 *
529 * DESCRIPTION
530 *
531 * The routine glp_ios_branch_upon can be called from the user-defined
532 * callback routine in response to the reason GLP_IBRANCH to choose a
533 * branching variable, whose ordinal number is j. Should note that only
534 * variables, for which the routine glp_ios_can_branch returns non-zero,
535 * can be used to branch upon.
536 *
537 * The parameter sel is a flag that indicates which branch (subproblem)
538 * should be selected next to continue the search:
539 *
540 * GLP_DN_BRNCH - select down-branch;
541 * GLP_UP_BRNCH - select up-branch;
542 * GLP_NO_BRNCH - use general selection technique. */
543
glp_ios_branch_upon(glp_tree * tree,int j,int sel)544 void glp_ios_branch_upon(glp_tree *tree, int j, int sel)
545 { if (!(1 <= j && j <= tree->mip->n))
546 xerror("glp_ios_branch_upon: j = %d; column number out of rang"
547 "e\n", j);
548 if (!(sel == GLP_DN_BRNCH || sel == GLP_UP_BRNCH ||
549 sel == GLP_NO_BRNCH))
550 xerror("glp_ios_branch_upon: sel = %d: invalid branch selectio"
551 "n flag\n", sel);
552 if (!(tree->non_int[j]))
553 xerror("glp_ios_branch_upon: j = %d; variable cannot be used t"
554 "o branch upon\n", j);
555 if (tree->br_var != 0)
556 xerror("glp_ios_branch_upon: branching variable already chosen"
557 "\n");
558 tree->br_var = j;
559 tree->br_sel = sel;
560 return;
561 }
562
563 /***********************************************************************
564 * NAME
565 *
566 * glp_ios_select_node - select subproblem to continue the search
567 *
568 * SYNOPSIS
569 *
570 * void glp_ios_select_node(glp_tree *tree, int p);
571 *
572 * DESCRIPTION
573 *
574 * The routine glp_ios_select_node can be called from the user-defined
575 * callback routine in response to the reason GLP_ISELECT to select an
576 * active subproblem, whose reference number is p. The search will be
577 * continued from the subproblem selected. */
578
glp_ios_select_node(glp_tree * tree,int p)579 void glp_ios_select_node(glp_tree *tree, int p)
580 { IOSNPD *node;
581 /* obtain pointer to the specified subproblem */
582 if (!(1 <= p && p <= tree->nslots))
583 err: xerror("glp_ios_select_node: p = %d; invalid subproblem refere"
584 "nce number\n", p);
585 node = tree->slot[p].node;
586 if (node == NULL) goto err;
587 /* the specified subproblem must be active */
588 if (node->count != 0)
589 xerror("glp_ios_select_node: p = %d; subproblem not in the act"
590 "ive list\n", p);
591 /* no subproblem must be selected yet */
592 if (tree->next_p != 0)
593 xerror("glp_ios_select_node: subproblem already selected\n");
594 /* select the specified subproblem to continue the search */
595 tree->next_p = p;
596 return;
597 }
598
599 /***********************************************************************
600 * NAME
601 *
602 * glp_ios_heur_sol - provide solution found by heuristic
603 *
604 * SYNOPSIS
605 *
606 * int glp_ios_heur_sol(glp_tree *tree, const double x[]);
607 *
608 * DESCRIPTION
609 *
610 * The routine glp_ios_heur_sol can be called from the user-defined
611 * callback routine in response to the reason GLP_IHEUR to provide an
612 * integer feasible solution found by a primal heuristic.
613 *
614 * Primal values of *all* variables (columns) found by the heuristic
615 * should be placed in locations x[1], ..., x[n], where n is the number
616 * of columns in the original problem object. Note that the routine
617 * glp_ios_heur_sol *does not* check primal feasibility of the solution
618 * provided.
619 *
620 * Using the solution passed in the array x the routine computes value
621 * of the objective function. If the objective value is better than the
622 * best known integer feasible solution, the routine computes values of
623 * auxiliary variables (rows) and stores all solution components in the
624 * problem object.
625 *
626 * RETURNS
627 *
628 * If the provided solution is accepted, the routine glp_ios_heur_sol
629 * returns zero. Otherwise, if the provided solution is rejected, the
630 * routine returns non-zero. */
631
glp_ios_heur_sol(glp_tree * tree,const double x[])632 int glp_ios_heur_sol(glp_tree *tree, const double x[])
633 { glp_prob *mip = tree->mip;
634 int m = tree->orig_m;
635 int n = tree->n;
636 int i, j;
637 double obj;
638 xassert(mip->m >= m);
639 xassert(mip->n == n);
640 /* check values of integer variables and compute value of the
641 objective function */
642 obj = mip->c0;
643 for (j = 1; j <= n; j++)
644 { GLPCOL *col = mip->col[j];
645 if (col->kind == GLP_IV)
646 { /* provided value must be integral */
647 if (x[j] != floor(x[j])) return 1;
648 }
649 obj += col->coef * x[j];
650 }
651 /* check if the provided solution is better than the best known
652 integer feasible solution */
653 if (mip->mip_stat == GLP_FEAS)
654 { switch (mip->dir)
655 { case GLP_MIN:
656 if (obj >= tree->mip->mip_obj) return 1;
657 break;
658 case GLP_MAX:
659 if (obj <= tree->mip->mip_obj) return 1;
660 break;
661 default:
662 xassert(mip != mip);
663 }
664 }
665 /* it is better; store it in the problem object */
666 if (tree->parm->msg_lev >= GLP_MSG_ON)
667 xprintf("Solution found by heuristic: %.12g\n", obj);
668 mip->mip_stat = GLP_FEAS;
669 mip->mip_obj = obj;
670 for (j = 1; j <= n; j++)
671 mip->col[j]->mipx = x[j];
672 for (i = 1; i <= m; i++)
673 { GLPROW *row = mip->row[i];
674 GLPAIJ *aij;
675 row->mipx = 0.0;
676 for (aij = row->ptr; aij != NULL; aij = aij->r_next)
677 row->mipx += aij->val * aij->col->mipx;
678 }
679 #if 1 /* 11/VII-2013 */
680 ios_process_sol(tree);
681 #endif
682 return 0;
683 }
684
685 /***********************************************************************
686 * NAME
687 *
688 * glp_ios_terminate - terminate the solution process.
689 *
690 * SYNOPSIS
691 *
692 * void glp_ios_terminate(glp_tree *tree);
693 *
694 * DESCRIPTION
695 *
696 * The routine glp_ios_terminate sets a flag indicating that the MIP
697 * solver should prematurely terminate the search. */
698
glp_ios_terminate(glp_tree * tree)699 void glp_ios_terminate(glp_tree *tree)
700 { if (tree->parm->msg_lev >= GLP_MSG_DBG)
701 xprintf("The search is prematurely terminated due to applicati"
702 "on request\n");
703 tree->stop = 1;
704 return;
705 }
706
707 /* eof */
708