alpar@1: /* glpios03.c (branch-and-cut driver) */ alpar@1: alpar@1: /*********************************************************************** alpar@1: * This code is part of GLPK (GNU Linear Programming Kit). alpar@1: * alpar@1: * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, alpar@1: * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, alpar@1: * Moscow Aviation Institute, Moscow, Russia. All rights reserved. alpar@1: * E-mail: . alpar@1: * alpar@1: * GLPK is free software: you can redistribute it and/or modify it alpar@1: * under the terms of the GNU General Public License as published by alpar@1: * the Free Software Foundation, either version 3 of the License, or alpar@1: * (at your option) any later version. alpar@1: * alpar@1: * GLPK is distributed in the hope that it will be useful, but WITHOUT alpar@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY alpar@1: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public alpar@1: * License for more details. alpar@1: * alpar@1: * You should have received a copy of the GNU General Public License alpar@1: * along with GLPK. If not, see . alpar@1: ***********************************************************************/ alpar@1: alpar@1: #include "glpios.h" alpar@1: alpar@1: /*********************************************************************** alpar@1: * show_progress - display current progress of the search alpar@1: * alpar@1: * This routine displays some information about current progress of the alpar@1: * search. alpar@1: * alpar@1: * The information includes: alpar@1: * alpar@1: * the current number of iterations performed by the simplex solver; alpar@1: * alpar@1: * the objective value for the best known integer feasible solution, alpar@1: * which is upper (minimization) or lower (maximization) global bound alpar@1: * for optimal solution of the original mip problem; alpar@1: * alpar@1: * the best local bound for active nodes, which is lower (minimization) alpar@1: * or upper (maximization) global bound for optimal solution of the alpar@1: * original mip problem; alpar@1: * alpar@1: * the relative mip gap, in percents; alpar@1: * alpar@1: * the number of open (active) subproblems; alpar@1: * alpar@1: * the number of completely explored subproblems, i.e. whose nodes have alpar@1: * been removed from the tree. */ alpar@1: alpar@1: static void show_progress(glp_tree *T, int bingo) alpar@1: { int p; alpar@1: double temp; alpar@1: char best_mip[50], best_bound[50], *rho, rel_gap[50]; alpar@1: /* format the best known integer feasible solution */ alpar@1: if (T->mip->mip_stat == GLP_FEAS) alpar@1: sprintf(best_mip, "%17.9e", T->mip->mip_obj); alpar@1: else alpar@1: sprintf(best_mip, "%17s", "not found yet"); alpar@1: /* determine reference number of an active subproblem whose local alpar@1: bound is best */ alpar@1: p = ios_best_node(T); alpar@1: /* format the best bound */ alpar@1: if (p == 0) alpar@1: sprintf(best_bound, "%17s", "tree is empty"); alpar@1: else alpar@1: { temp = T->slot[p].node->bound; alpar@1: if (temp == -DBL_MAX) alpar@1: sprintf(best_bound, "%17s", "-inf"); alpar@1: else if (temp == +DBL_MAX) alpar@1: sprintf(best_bound, "%17s", "+inf"); alpar@1: else alpar@1: sprintf(best_bound, "%17.9e", temp); alpar@1: } alpar@1: /* choose the relation sign between global bounds */ alpar@1: if (T->mip->dir == GLP_MIN) alpar@1: rho = ">="; alpar@1: else if (T->mip->dir == GLP_MAX) alpar@1: rho = "<="; alpar@1: else alpar@1: xassert(T != T); alpar@1: /* format the relative mip gap */ alpar@1: temp = ios_relative_gap(T); alpar@1: if (temp == 0.0) alpar@1: sprintf(rel_gap, " 0.0%%"); alpar@1: else if (temp < 0.001) alpar@1: sprintf(rel_gap, "< 0.1%%"); alpar@1: else if (temp <= 9.999) alpar@1: sprintf(rel_gap, "%5.1f%%", 100.0 * temp); alpar@1: else alpar@1: sprintf(rel_gap, "%6s", ""); alpar@1: /* display progress of the search */ alpar@1: xprintf("+%6d: %s %s %s %s %s (%d; %d)\n", alpar@1: T->mip->it_cnt, bingo ? ">>>>>" : "mip =", best_mip, rho, alpar@1: best_bound, rel_gap, T->a_cnt, T->t_cnt - T->n_cnt); alpar@1: T->tm_lag = xtime(); alpar@1: return; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * is_branch_hopeful - check if specified branch is hopeful alpar@1: * alpar@1: * This routine checks if the specified subproblem can have an integer alpar@1: * optimal solution which is better than the best known one. alpar@1: * alpar@1: * The check is based on comparison of the local objective bound stored alpar@1: * in the subproblem descriptor and the incumbent objective value which alpar@1: * is the global objective bound. alpar@1: * alpar@1: * If there is a chance that the specified subproblem can have a better alpar@1: * integer optimal solution, the routine returns non-zero. Otherwise, if alpar@1: * the corresponding branch can pruned, zero is returned. */ alpar@1: alpar@1: static int is_branch_hopeful(glp_tree *T, int p) alpar@1: { xassert(1 <= p && p <= T->nslots); alpar@1: xassert(T->slot[p].node != NULL); alpar@1: return ios_is_hopeful(T, T->slot[p].node->bound); alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * check_integrality - check integrality of basic solution alpar@1: * alpar@1: * This routine checks if the basic solution of LP relaxation of the alpar@1: * current subproblem satisfies to integrality conditions, i.e. that all alpar@1: * variables of integer kind have integral primal values. (The solution alpar@1: * is assumed to be optimal.) alpar@1: * alpar@1: * For each variable of integer kind the routine computes the following alpar@1: * quantity: alpar@1: * alpar@1: * ii(x[j]) = min(x[j] - floor(x[j]), ceil(x[j]) - x[j]), (1) alpar@1: * alpar@1: * which is a measure of the integer infeasibility (non-integrality) of alpar@1: * x[j] (for example, ii(2.1) = 0.1, ii(3.7) = 0.3, ii(5.0) = 0). It is alpar@1: * understood that 0 <= ii(x[j]) <= 0.5, and variable x[j] is integer alpar@1: * feasible if ii(x[j]) = 0. However, due to floating-point arithmetic alpar@1: * the routine checks less restrictive condition: alpar@1: * alpar@1: * ii(x[j]) <= tol_int, (2) alpar@1: * alpar@1: * where tol_int is a given tolerance (small positive number) and marks alpar@1: * each variable which does not satisfy to (2) as integer infeasible by alpar@1: * setting its fractionality flag. alpar@1: * alpar@1: * In order to characterize integer infeasibility of the basic solution alpar@1: * in the whole the routine computes two parameters: ii_cnt, which is alpar@1: * the number of variables with the fractionality flag set, and ii_sum, alpar@1: * which is the sum of integer infeasibilities (1). */ alpar@1: alpar@1: static void check_integrality(glp_tree *T) alpar@1: { glp_prob *mip = T->mip; alpar@1: int j, type, ii_cnt = 0; alpar@1: double lb, ub, x, temp1, temp2, ii_sum = 0.0; alpar@1: /* walk through the set of columns (structural variables) */ alpar@1: for (j = 1; j <= mip->n; j++) alpar@1: { GLPCOL *col = mip->col[j]; alpar@1: T->non_int[j] = 0; alpar@1: /* if the column is not integer, skip it */ alpar@1: if (col->kind != GLP_IV) continue; alpar@1: /* if the column is non-basic, it is integer feasible */ alpar@1: if (col->stat != GLP_BS) continue; alpar@1: /* obtain the type and bounds of the column */ alpar@1: type = col->type, lb = col->lb, ub = col->ub; alpar@1: /* obtain value of the column in optimal basic solution */ alpar@1: x = col->prim; alpar@1: /* if the column's primal value is close to the lower bound, alpar@1: the column is integer feasible within given tolerance */ alpar@1: if (type == GLP_LO || type == GLP_DB || type == GLP_FX) alpar@1: { temp1 = lb - T->parm->tol_int; alpar@1: temp2 = lb + T->parm->tol_int; alpar@1: if (temp1 <= x && x <= temp2) continue; alpar@1: #if 0 alpar@1: /* the lower bound must not be violated */ alpar@1: xassert(x >= lb); alpar@1: #else alpar@1: if (x < lb) continue; alpar@1: #endif alpar@1: } alpar@1: /* if the column's primal value is close to the upper bound, alpar@1: the column is integer feasible within given tolerance */ alpar@1: if (type == GLP_UP || type == GLP_DB || type == GLP_FX) alpar@1: { temp1 = ub - T->parm->tol_int; alpar@1: temp2 = ub + T->parm->tol_int; alpar@1: if (temp1 <= x && x <= temp2) continue; alpar@1: #if 0 alpar@1: /* the upper bound must not be violated */ alpar@1: xassert(x <= ub); alpar@1: #else alpar@1: if (x > ub) continue; alpar@1: #endif alpar@1: } alpar@1: /* if the column's primal value is close to nearest integer, alpar@1: the column is integer feasible within given tolerance */ alpar@1: temp1 = floor(x + 0.5) - T->parm->tol_int; alpar@1: temp2 = floor(x + 0.5) + T->parm->tol_int; alpar@1: if (temp1 <= x && x <= temp2) continue; alpar@1: /* otherwise the column is integer infeasible */ alpar@1: T->non_int[j] = 1; alpar@1: /* increase the number of fractional-valued columns */ alpar@1: ii_cnt++; alpar@1: /* compute the sum of integer infeasibilities */ alpar@1: temp1 = x - floor(x); alpar@1: temp2 = ceil(x) - x; alpar@1: xassert(temp1 > 0.0 && temp2 > 0.0); alpar@1: ii_sum += (temp1 <= temp2 ? temp1 : temp2); alpar@1: } alpar@1: /* store ii_cnt and ii_sum to the current problem descriptor */ alpar@1: xassert(T->curr != NULL); alpar@1: T->curr->ii_cnt = ii_cnt; alpar@1: T->curr->ii_sum = ii_sum; alpar@1: /* and also display these parameters */ alpar@1: if (T->parm->msg_lev >= GLP_MSG_DBG) alpar@1: { if (ii_cnt == 0) alpar@1: xprintf("There are no fractional columns\n"); alpar@1: else if (ii_cnt == 1) alpar@1: xprintf("There is one fractional column, integer infeasibil" alpar@1: "ity is %.3e\n", ii_sum); alpar@1: else alpar@1: xprintf("There are %d fractional columns, integer infeasibi" alpar@1: "lity is %.3e\n", ii_cnt, ii_sum); alpar@1: } alpar@1: return; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * record_solution - record better integer feasible solution alpar@1: * alpar@1: * This routine records optimal basic solution of LP relaxation of the alpar@1: * current subproblem, which being integer feasible is better than the alpar@1: * best known integer feasible solution. */ alpar@1: alpar@1: static void record_solution(glp_tree *T) alpar@1: { glp_prob *mip = T->mip; alpar@1: int i, j; alpar@1: mip->mip_stat = GLP_FEAS; alpar@1: mip->mip_obj = mip->obj_val; alpar@1: for (i = 1; i <= mip->m; i++) alpar@1: { GLPROW *row = mip->row[i]; alpar@1: row->mipx = row->prim; alpar@1: } alpar@1: for (j = 1; j <= mip->n; j++) alpar@1: { GLPCOL *col = mip->col[j]; alpar@1: if (col->kind == GLP_CV) alpar@1: col->mipx = col->prim; alpar@1: else if (col->kind == GLP_IV) alpar@1: { /* value of the integer column must be integral */ alpar@1: col->mipx = floor(col->prim + 0.5); alpar@1: } alpar@1: else alpar@1: xassert(col != col); alpar@1: } alpar@1: T->sol_cnt++; alpar@1: return; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * fix_by_red_cost - fix non-basic integer columns by reduced costs alpar@1: * alpar@1: * This routine fixes some non-basic integer columns if their reduced alpar@1: * costs indicate that increasing (decreasing) the column at least by alpar@1: * one involves the objective value becoming worse than the incumbent alpar@1: * objective value. */ alpar@1: alpar@1: static void fix_by_red_cost(glp_tree *T) alpar@1: { glp_prob *mip = T->mip; alpar@1: int j, stat, fixed = 0; alpar@1: double obj, lb, ub, dj; alpar@1: /* the global bound must exist */ alpar@1: xassert(T->mip->mip_stat == GLP_FEAS); alpar@1: /* basic solution of LP relaxation must be optimal */ alpar@1: xassert(mip->pbs_stat == GLP_FEAS && mip->dbs_stat == GLP_FEAS); alpar@1: /* determine the objective function value */ alpar@1: obj = mip->obj_val; alpar@1: /* walk through the column list */ alpar@1: for (j = 1; j <= mip->n; j++) alpar@1: { GLPCOL *col = mip->col[j]; alpar@1: /* if the column is not integer, skip it */ alpar@1: if (col->kind != GLP_IV) continue; alpar@1: /* obtain bounds of j-th column */ alpar@1: lb = col->lb, ub = col->ub; alpar@1: /* and determine its status and reduced cost */ alpar@1: stat = col->stat, dj = col->dual; alpar@1: /* analyze the reduced cost */ alpar@1: switch (mip->dir) alpar@1: { case GLP_MIN: alpar@1: /* minimization */ alpar@1: if (stat == GLP_NL) alpar@1: { /* j-th column is non-basic on its lower bound */ alpar@1: if (dj < 0.0) dj = 0.0; alpar@1: if (obj + dj >= mip->mip_obj) alpar@1: glp_set_col_bnds(mip, j, GLP_FX, lb, lb), fixed++; alpar@1: } alpar@1: else if (stat == GLP_NU) alpar@1: { /* j-th column is non-basic on its upper bound */ alpar@1: if (dj > 0.0) dj = 0.0; alpar@1: if (obj - dj >= mip->mip_obj) alpar@1: glp_set_col_bnds(mip, j, GLP_FX, ub, ub), fixed++; alpar@1: } alpar@1: break; alpar@1: case GLP_MAX: alpar@1: /* maximization */ alpar@1: if (stat == GLP_NL) alpar@1: { /* j-th column is non-basic on its lower bound */ alpar@1: if (dj > 0.0) dj = 0.0; alpar@1: if (obj + dj <= mip->mip_obj) alpar@1: glp_set_col_bnds(mip, j, GLP_FX, lb, lb), fixed++; alpar@1: } alpar@1: else if (stat == GLP_NU) alpar@1: { /* j-th column is non-basic on its upper bound */ alpar@1: if (dj < 0.0) dj = 0.0; alpar@1: if (obj - dj <= mip->mip_obj) alpar@1: glp_set_col_bnds(mip, j, GLP_FX, ub, ub), fixed++; alpar@1: } alpar@1: break; alpar@1: default: alpar@1: xassert(T != T); alpar@1: } alpar@1: } alpar@1: if (T->parm->msg_lev >= GLP_MSG_DBG) alpar@1: { if (fixed == 0) alpar@1: /* nothing to say */; alpar@1: else if (fixed == 1) alpar@1: xprintf("One column has been fixed by reduced cost\n"); alpar@1: else alpar@1: xprintf("%d columns have been fixed by reduced costs\n", alpar@1: fixed); alpar@1: } alpar@1: /* fixing non-basic columns on their current bounds does not alpar@1: change the basic solution */ alpar@1: xassert(mip->pbs_stat == GLP_FEAS && mip->dbs_stat == GLP_FEAS); alpar@1: return; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * branch_on - perform branching on specified variable alpar@1: * alpar@1: * This routine performs branching on j-th column (structural variable) alpar@1: * of the current subproblem. The specified column must be of integer alpar@1: * kind and must have a fractional value in optimal basic solution of alpar@1: * LP relaxation of the current subproblem (i.e. only columns for which alpar@1: * the flag non_int[j] is set are valid candidates to branch on). alpar@1: * alpar@1: * Let x be j-th structural variable, and beta be its primal fractional alpar@1: * value in the current basic solution. Branching on j-th variable is alpar@1: * dividing the current subproblem into two new subproblems, which are alpar@1: * identical to the current subproblem with the following exception: in alpar@1: * the first subproblem that begins the down-branch x has a new upper alpar@1: * bound x <= floor(beta), and in the second subproblem that begins the alpar@1: * up-branch x has a new lower bound x >= ceil(beta). alpar@1: * alpar@1: * Depending on estimation of local bounds for down- and up-branches alpar@1: * this routine returns the following: alpar@1: * alpar@1: * 0 - both branches have been created; alpar@1: * 1 - one branch is hopeless and has been pruned, so now the current alpar@1: * subproblem is other branch; alpar@1: * 2 - both branches are hopeless and have been pruned; new subproblem alpar@1: * selection is needed to continue the search. */ alpar@1: alpar@1: static int branch_on(glp_tree *T, int j, int next) alpar@1: { glp_prob *mip = T->mip; alpar@1: IOSNPD *node; alpar@1: int m = mip->m; alpar@1: int n = mip->n; alpar@1: int type, dn_type, up_type, dn_bad, up_bad, p, ret, clone[1+2]; alpar@1: double lb, ub, beta, new_ub, new_lb, dn_lp, up_lp, dn_bnd, up_bnd; alpar@1: /* determine bounds and value of x[j] in optimal solution to LP alpar@1: relaxation of the current subproblem */ alpar@1: xassert(1 <= j && j <= n); alpar@1: type = mip->col[j]->type; alpar@1: lb = mip->col[j]->lb; alpar@1: ub = mip->col[j]->ub; alpar@1: beta = mip->col[j]->prim; alpar@1: /* determine new bounds of x[j] for down- and up-branches */ alpar@1: new_ub = floor(beta); alpar@1: new_lb = ceil(beta); alpar@1: switch (type) alpar@1: { case GLP_FR: alpar@1: dn_type = GLP_UP; alpar@1: up_type = GLP_LO; alpar@1: break; alpar@1: case GLP_LO: alpar@1: xassert(lb <= new_ub); alpar@1: dn_type = (lb == new_ub ? GLP_FX : GLP_DB); alpar@1: xassert(lb + 1.0 <= new_lb); alpar@1: up_type = GLP_LO; alpar@1: break; alpar@1: case GLP_UP: alpar@1: xassert(new_ub <= ub - 1.0); alpar@1: dn_type = GLP_UP; alpar@1: xassert(new_lb <= ub); alpar@1: up_type = (new_lb == ub ? GLP_FX : GLP_DB); alpar@1: break; alpar@1: case GLP_DB: alpar@1: xassert(lb <= new_ub && new_ub <= ub - 1.0); alpar@1: dn_type = (lb == new_ub ? GLP_FX : GLP_DB); alpar@1: xassert(lb + 1.0 <= new_lb && new_lb <= ub); alpar@1: up_type = (new_lb == ub ? GLP_FX : GLP_DB); alpar@1: break; alpar@1: default: alpar@1: xassert(type != type); alpar@1: } alpar@1: /* compute local bounds to LP relaxation for both branches */ alpar@1: ios_eval_degrad(T, j, &dn_lp, &up_lp); alpar@1: /* and improve them by rounding */ alpar@1: dn_bnd = ios_round_bound(T, dn_lp); alpar@1: up_bnd = ios_round_bound(T, up_lp); alpar@1: /* check local bounds for down- and up-branches */ alpar@1: dn_bad = !ios_is_hopeful(T, dn_bnd); alpar@1: up_bad = !ios_is_hopeful(T, up_bnd); alpar@1: if (dn_bad && up_bad) alpar@1: { if (T->parm->msg_lev >= GLP_MSG_DBG) alpar@1: xprintf("Both down- and up-branches are hopeless\n"); alpar@1: ret = 2; alpar@1: goto done; alpar@1: } alpar@1: else if (up_bad) alpar@1: { if (T->parm->msg_lev >= GLP_MSG_DBG) alpar@1: xprintf("Up-branch is hopeless\n"); alpar@1: glp_set_col_bnds(mip, j, dn_type, lb, new_ub); alpar@1: T->curr->lp_obj = dn_lp; alpar@1: if (mip->dir == GLP_MIN) alpar@1: { if (T->curr->bound < dn_bnd) alpar@1: T->curr->bound = dn_bnd; alpar@1: } alpar@1: else if (mip->dir == GLP_MAX) alpar@1: { if (T->curr->bound > dn_bnd) alpar@1: T->curr->bound = dn_bnd; alpar@1: } alpar@1: else alpar@1: xassert(mip != mip); alpar@1: ret = 1; alpar@1: goto done; alpar@1: } alpar@1: else if (dn_bad) alpar@1: { if (T->parm->msg_lev >= GLP_MSG_DBG) alpar@1: xprintf("Down-branch is hopeless\n"); alpar@1: glp_set_col_bnds(mip, j, up_type, new_lb, ub); alpar@1: T->curr->lp_obj = up_lp; alpar@1: if (mip->dir == GLP_MIN) alpar@1: { if (T->curr->bound < up_bnd) alpar@1: T->curr->bound = up_bnd; alpar@1: } alpar@1: else if (mip->dir == GLP_MAX) alpar@1: { if (T->curr->bound > up_bnd) alpar@1: T->curr->bound = up_bnd; alpar@1: } alpar@1: else alpar@1: xassert(mip != mip); alpar@1: ret = 1; alpar@1: goto done; alpar@1: } alpar@1: /* both down- and up-branches seem to be hopeful */ alpar@1: if (T->parm->msg_lev >= GLP_MSG_DBG) alpar@1: xprintf("Branching on column %d, primal value is %.9e\n", alpar@1: j, beta); alpar@1: /* determine the reference number of the current subproblem */ alpar@1: xassert(T->curr != NULL); alpar@1: p = T->curr->p; alpar@1: T->curr->br_var = j; alpar@1: T->curr->br_val = beta; alpar@1: /* freeze the current subproblem */ alpar@1: ios_freeze_node(T); alpar@1: /* create two clones of the current subproblem; the first clone alpar@1: begins the down-branch, the second one begins the up-branch */ alpar@1: ios_clone_node(T, p, 2, clone); alpar@1: if (T->parm->msg_lev >= GLP_MSG_DBG) alpar@1: xprintf("Node %d begins down branch, node %d begins up branch " alpar@1: "\n", clone[1], clone[2]); alpar@1: /* set new upper bound of j-th column in the down-branch */ alpar@1: node = T->slot[clone[1]].node; alpar@1: xassert(node != NULL); alpar@1: xassert(node->up != NULL); alpar@1: xassert(node->b_ptr == NULL); alpar@1: node->b_ptr = dmp_get_atom(T->pool, sizeof(IOSBND)); alpar@1: node->b_ptr->k = m + j; alpar@1: node->b_ptr->type = (unsigned char)dn_type; alpar@1: node->b_ptr->lb = lb; alpar@1: node->b_ptr->ub = new_ub; alpar@1: node->b_ptr->next = NULL; alpar@1: node->lp_obj = dn_lp; alpar@1: if (mip->dir == GLP_MIN) alpar@1: { if (node->bound < dn_bnd) alpar@1: node->bound = dn_bnd; alpar@1: } alpar@1: else if (mip->dir == GLP_MAX) alpar@1: { if (node->bound > dn_bnd) alpar@1: node->bound = dn_bnd; alpar@1: } alpar@1: else alpar@1: xassert(mip != mip); alpar@1: /* set new lower bound of j-th column in the up-branch */ alpar@1: node = T->slot[clone[2]].node; alpar@1: xassert(node != NULL); alpar@1: xassert(node->up != NULL); alpar@1: xassert(node->b_ptr == NULL); alpar@1: node->b_ptr = dmp_get_atom(T->pool, sizeof(IOSBND)); alpar@1: node->b_ptr->k = m + j; alpar@1: node->b_ptr->type = (unsigned char)up_type; alpar@1: node->b_ptr->lb = new_lb; alpar@1: node->b_ptr->ub = ub; alpar@1: node->b_ptr->next = NULL; alpar@1: node->lp_obj = up_lp; alpar@1: if (mip->dir == GLP_MIN) alpar@1: { if (node->bound < up_bnd) alpar@1: node->bound = up_bnd; alpar@1: } alpar@1: else if (mip->dir == GLP_MAX) alpar@1: { if (node->bound > up_bnd) alpar@1: node->bound = up_bnd; alpar@1: } alpar@1: else alpar@1: xassert(mip != mip); alpar@1: /* suggest the subproblem to be solved next */ alpar@1: xassert(T->child == 0); alpar@1: if (next == GLP_NO_BRNCH) alpar@1: T->child = 0; alpar@1: else if (next == GLP_DN_BRNCH) alpar@1: T->child = clone[1]; alpar@1: else if (next == GLP_UP_BRNCH) alpar@1: T->child = clone[2]; alpar@1: else alpar@1: xassert(next != next); alpar@1: ret = 0; alpar@1: done: return ret; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * cleanup_the_tree - prune hopeless branches from the tree alpar@1: * alpar@1: * This routine walks through the active list and checks the local alpar@1: * bound for every active subproblem. If the local bound indicates that alpar@1: * the subproblem cannot have integer optimal solution better than the alpar@1: * incumbent objective value, the routine deletes such subproblem that, alpar@1: * in turn, involves pruning the corresponding branch of the tree. */ alpar@1: alpar@1: static void cleanup_the_tree(glp_tree *T) alpar@1: { IOSNPD *node, *next_node; alpar@1: int count = 0; alpar@1: /* the global bound must exist */ alpar@1: xassert(T->mip->mip_stat == GLP_FEAS); alpar@1: /* walk through the list of active subproblems */ alpar@1: for (node = T->head; node != NULL; node = next_node) alpar@1: { /* deleting some active problem node may involve deleting its alpar@1: parents recursively; however, all its parents being created alpar@1: *before* it are always *precede* it in the node list, so alpar@1: the next problem node is never affected by such deletion */ alpar@1: next_node = node->next; alpar@1: /* if the branch is hopeless, prune it */ alpar@1: if (!is_branch_hopeful(T, node->p)) alpar@1: ios_delete_node(T, node->p), count++; alpar@1: } alpar@1: if (T->parm->msg_lev >= GLP_MSG_DBG) alpar@1: { if (count == 1) alpar@1: xprintf("One hopeless branch has been pruned\n"); alpar@1: else if (count > 1) alpar@1: xprintf("%d hopeless branches have been pruned\n", count); alpar@1: } alpar@1: return; alpar@1: } alpar@1: alpar@1: /**********************************************************************/ alpar@1: alpar@1: static void generate_cuts(glp_tree *T) alpar@1: { /* generate generic cuts with built-in generators */ alpar@1: if (!(T->parm->mir_cuts == GLP_ON || alpar@1: T->parm->gmi_cuts == GLP_ON || alpar@1: T->parm->cov_cuts == GLP_ON || alpar@1: T->parm->clq_cuts == GLP_ON)) goto done; alpar@1: #if 1 /* 20/IX-2008 */ alpar@1: { int i, max_cuts, added_cuts; alpar@1: max_cuts = T->n; alpar@1: if (max_cuts < 1000) max_cuts = 1000; alpar@1: added_cuts = 0; alpar@1: for (i = T->orig_m+1; i <= T->mip->m; i++) alpar@1: { if (T->mip->row[i]->origin == GLP_RF_CUT) alpar@1: added_cuts++; alpar@1: } alpar@1: /* xprintf("added_cuts = %d\n", added_cuts); */ alpar@1: if (added_cuts >= max_cuts) goto done; alpar@1: } alpar@1: #endif alpar@1: /* generate and add to POOL all cuts violated by x* */ alpar@1: if (T->parm->gmi_cuts == GLP_ON) alpar@1: { if (T->curr->changed < 5) alpar@1: ios_gmi_gen(T); alpar@1: } alpar@1: if (T->parm->mir_cuts == GLP_ON) alpar@1: { xassert(T->mir_gen != NULL); alpar@1: ios_mir_gen(T, T->mir_gen); alpar@1: } alpar@1: if (T->parm->cov_cuts == GLP_ON) alpar@1: { /* cover cuts works well along with mir cuts */ alpar@1: /*if (T->round <= 5)*/ alpar@1: ios_cov_gen(T); alpar@1: } alpar@1: if (T->parm->clq_cuts == GLP_ON) alpar@1: { if (T->clq_gen != NULL) alpar@1: { if (T->curr->level == 0 && T->curr->changed < 50 || alpar@1: T->curr->level > 0 && T->curr->changed < 5) alpar@1: ios_clq_gen(T, T->clq_gen); alpar@1: } alpar@1: } alpar@1: done: return; alpar@1: } alpar@1: alpar@1: /**********************************************************************/ alpar@1: alpar@1: static void remove_cuts(glp_tree *T) alpar@1: { /* remove inactive cuts (some valueable globally valid cut might alpar@1: be saved in the global cut pool) */ alpar@1: int i, cnt = 0, *num = NULL; alpar@1: xassert(T->curr != NULL); alpar@1: for (i = T->orig_m+1; i <= T->mip->m; i++) alpar@1: { if (T->mip->row[i]->origin == GLP_RF_CUT && alpar@1: T->mip->row[i]->level == T->curr->level && alpar@1: T->mip->row[i]->stat == GLP_BS) alpar@1: { if (num == NULL) alpar@1: num = xcalloc(1+T->mip->m, sizeof(int)); alpar@1: num[++cnt] = i; alpar@1: } alpar@1: } alpar@1: if (cnt > 0) alpar@1: { glp_del_rows(T->mip, cnt, num); alpar@1: #if 0 alpar@1: xprintf("%d inactive cut(s) removed\n", cnt); alpar@1: #endif alpar@1: xfree(num); alpar@1: xassert(glp_factorize(T->mip) == 0); alpar@1: } alpar@1: return; alpar@1: } alpar@1: alpar@1: /**********************************************************************/ alpar@1: alpar@1: static void display_cut_info(glp_tree *T) alpar@1: { glp_prob *mip = T->mip; alpar@1: int i, gmi = 0, mir = 0, cov = 0, clq = 0, app = 0; alpar@1: for (i = mip->m; i > 0; i--) alpar@1: { GLPROW *row; alpar@1: row = mip->row[i]; alpar@1: /* if (row->level < T->curr->level) break; */ alpar@1: if (row->origin == GLP_RF_CUT) alpar@1: { if (row->klass == GLP_RF_GMI) alpar@1: gmi++; alpar@1: else if (row->klass == GLP_RF_MIR) alpar@1: mir++; alpar@1: else if (row->klass == GLP_RF_COV) alpar@1: cov++; alpar@1: else if (row->klass == GLP_RF_CLQ) alpar@1: clq++; alpar@1: else alpar@1: app++; alpar@1: } alpar@1: } alpar@1: xassert(T->curr != NULL); alpar@1: if (gmi + mir + cov + clq + app > 0) alpar@1: { xprintf("Cuts on level %d:", T->curr->level); alpar@1: if (gmi > 0) xprintf(" gmi = %d;", gmi); alpar@1: if (mir > 0) xprintf(" mir = %d;", mir); alpar@1: if (cov > 0) xprintf(" cov = %d;", cov); alpar@1: if (clq > 0) xprintf(" clq = %d;", clq); alpar@1: if (app > 0) xprintf(" app = %d;", app); alpar@1: xprintf("\n"); alpar@1: } alpar@1: return; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * NAME alpar@1: * alpar@1: * ios_driver - branch-and-cut driver alpar@1: * alpar@1: * SYNOPSIS alpar@1: * alpar@1: * #include "glpios.h" alpar@1: * int ios_driver(glp_tree *T); alpar@1: * alpar@1: * DESCRIPTION alpar@1: * alpar@1: * The routine ios_driver is a branch-and-cut driver. It controls the alpar@1: * MIP solution process. alpar@1: * alpar@1: * RETURNS alpar@1: * alpar@1: * 0 The MIP problem instance has been successfully solved. This code alpar@1: * does not necessarily mean that the solver has found optimal alpar@1: * solution. It only means that the solution process was successful. alpar@1: * alpar@1: * GLP_EFAIL alpar@1: * The search was prematurely terminated due to the solver failure. alpar@1: * alpar@1: * GLP_EMIPGAP alpar@1: * The search was prematurely terminated, because the relative mip alpar@1: * gap tolerance has been reached. alpar@1: * alpar@1: * GLP_ETMLIM alpar@1: * The search was prematurely terminated, because the time limit has alpar@1: * been exceeded. alpar@1: * alpar@1: * GLP_ESTOP alpar@1: * The search was prematurely terminated by application. */ alpar@1: alpar@1: int ios_driver(glp_tree *T) alpar@1: { int p, curr_p, p_stat, d_stat, ret; alpar@1: #if 1 /* carry out to glp_tree */ alpar@1: int pred_p = 0; alpar@1: /* if the current subproblem has been just created due to alpar@1: branching, pred_p is the reference number of its parent alpar@1: subproblem, otherwise pred_p is zero */ alpar@1: #endif alpar@1: glp_long ttt = T->tm_beg; alpar@1: #if 0 alpar@1: ((glp_iocp *)T->parm)->msg_lev = GLP_MSG_DBG; alpar@1: #endif alpar@1: /* on entry to the B&B driver it is assumed that the active list alpar@1: contains the only active (i.e. root) subproblem, which is the alpar@1: original MIP problem to be solved */ alpar@1: loop: /* main loop starts here */ alpar@1: /* at this point the current subproblem does not exist */ alpar@1: xassert(T->curr == NULL); alpar@1: /* if the active list is empty, the search is finished */ alpar@1: if (T->head == NULL) alpar@1: { if (T->parm->msg_lev >= GLP_MSG_DBG) alpar@1: xprintf("Active list is empty!\n"); alpar@1: xassert(dmp_in_use(T->pool).lo == 0); alpar@1: ret = 0; alpar@1: goto done; alpar@1: } alpar@1: /* select some active subproblem to continue the search */ alpar@1: xassert(T->next_p == 0); alpar@1: /* let the application program select subproblem */ alpar@1: if (T->parm->cb_func != NULL) alpar@1: { xassert(T->reason == 0); alpar@1: T->reason = GLP_ISELECT; alpar@1: T->parm->cb_func(T, T->parm->cb_info); alpar@1: T->reason = 0; alpar@1: if (T->stop) alpar@1: { ret = GLP_ESTOP; alpar@1: goto done; alpar@1: } alpar@1: } alpar@1: if (T->next_p != 0) alpar@1: { /* the application program has selected something */ alpar@1: ; alpar@1: } alpar@1: else if (T->a_cnt == 1) alpar@1: { /* the only active subproblem exists, so select it */ alpar@1: xassert(T->head->next == NULL); alpar@1: T->next_p = T->head->p; alpar@1: } alpar@1: else if (T->child != 0) alpar@1: { /* select one of branching childs suggested by the branching alpar@1: heuristic */ alpar@1: T->next_p = T->child; alpar@1: } alpar@1: else alpar@1: { /* select active subproblem as specified by the backtracking alpar@1: technique option */ alpar@1: T->next_p = ios_choose_node(T); alpar@1: } alpar@1: /* the active subproblem just selected becomes current */ alpar@1: ios_revive_node(T, T->next_p); alpar@1: T->next_p = T->child = 0; alpar@1: /* invalidate pred_p, if it is not the reference number of the alpar@1: parent of the current subproblem */ alpar@1: if (T->curr->up != NULL && T->curr->up->p != pred_p) pred_p = 0; alpar@1: /* determine the reference number of the current subproblem */ alpar@1: p = T->curr->p; alpar@1: if (T->parm->msg_lev >= GLP_MSG_DBG) alpar@1: { xprintf("-----------------------------------------------------" alpar@1: "-------------------\n"); alpar@1: xprintf("Processing node %d at level %d\n", p, T->curr->level); alpar@1: } alpar@1: /* if it is the root subproblem, initialize cut generators */ alpar@1: if (p == 1) alpar@1: { if (T->parm->gmi_cuts == GLP_ON) alpar@1: { if (T->parm->msg_lev >= GLP_MSG_ALL) alpar@1: xprintf("Gomory's cuts enabled\n"); alpar@1: } alpar@1: if (T->parm->mir_cuts == GLP_ON) alpar@1: { if (T->parm->msg_lev >= GLP_MSG_ALL) alpar@1: xprintf("MIR cuts enabled\n"); alpar@1: xassert(T->mir_gen == NULL); alpar@1: T->mir_gen = ios_mir_init(T); alpar@1: } alpar@1: if (T->parm->cov_cuts == GLP_ON) alpar@1: { if (T->parm->msg_lev >= GLP_MSG_ALL) alpar@1: xprintf("Cover cuts enabled\n"); alpar@1: } alpar@1: if (T->parm->clq_cuts == GLP_ON) alpar@1: { xassert(T->clq_gen == NULL); alpar@1: if (T->parm->msg_lev >= GLP_MSG_ALL) alpar@1: xprintf("Clique cuts enabled\n"); alpar@1: T->clq_gen = ios_clq_init(T); alpar@1: } alpar@1: } alpar@1: more: /* minor loop starts here */ alpar@1: /* at this point the current subproblem needs either to be solved alpar@1: for the first time or re-optimized due to reformulation */ alpar@1: /* display current progress of the search */ alpar@1: if (T->parm->msg_lev >= GLP_MSG_DBG || alpar@1: T->parm->msg_lev >= GLP_MSG_ON && alpar@1: (double)(T->parm->out_frq - 1) <= alpar@1: 1000.0 * xdifftime(xtime(), T->tm_lag)) alpar@1: show_progress(T, 0); alpar@1: if (T->parm->msg_lev >= GLP_MSG_ALL && alpar@1: xdifftime(xtime(), ttt) >= 60.0) alpar@1: { glp_long total; alpar@1: glp_mem_usage(NULL, NULL, &total, NULL); alpar@1: xprintf("Time used: %.1f secs. Memory used: %.1f Mb.\n", alpar@1: xdifftime(xtime(), T->tm_beg), xltod(total) / 1048576.0); alpar@1: ttt = xtime(); alpar@1: } alpar@1: /* check the mip gap */ alpar@1: if (T->parm->mip_gap > 0.0 && alpar@1: ios_relative_gap(T) <= T->parm->mip_gap) alpar@1: { if (T->parm->msg_lev >= GLP_MSG_DBG) alpar@1: xprintf("Relative gap tolerance reached; search terminated " alpar@1: "\n"); alpar@1: ret = GLP_EMIPGAP; alpar@1: goto done; alpar@1: } alpar@1: /* check if the time limit has been exhausted */ alpar@1: if (T->parm->tm_lim < INT_MAX && alpar@1: (double)(T->parm->tm_lim - 1) <= alpar@1: 1000.0 * xdifftime(xtime(), T->tm_beg)) alpar@1: { if (T->parm->msg_lev >= GLP_MSG_DBG) alpar@1: xprintf("Time limit exhausted; search terminated\n"); alpar@1: ret = GLP_ETMLIM; alpar@1: goto done; alpar@1: } alpar@1: /* let the application program preprocess the subproblem */ alpar@1: if (T->parm->cb_func != NULL) alpar@1: { xassert(T->reason == 0); alpar@1: T->reason = GLP_IPREPRO; alpar@1: T->parm->cb_func(T, T->parm->cb_info); alpar@1: T->reason = 0; alpar@1: if (T->stop) alpar@1: { ret = GLP_ESTOP; alpar@1: goto done; alpar@1: } alpar@1: } alpar@1: /* perform basic preprocessing */ alpar@1: if (T->parm->pp_tech == GLP_PP_NONE) alpar@1: ; alpar@1: else if (T->parm->pp_tech == GLP_PP_ROOT) alpar@1: { if (T->curr->level == 0) alpar@1: { if (ios_preprocess_node(T, 100)) alpar@1: goto fath; alpar@1: } alpar@1: } alpar@1: else if (T->parm->pp_tech == GLP_PP_ALL) alpar@1: { if (ios_preprocess_node(T, T->curr->level == 0 ? 100 : 10)) alpar@1: goto fath; alpar@1: } alpar@1: else alpar@1: xassert(T != T); alpar@1: /* preprocessing may improve the global bound */ alpar@1: if (!is_branch_hopeful(T, p)) alpar@1: { xprintf("*** not tested yet ***\n"); alpar@1: goto fath; alpar@1: } alpar@1: /* solve LP relaxation of the current subproblem */ alpar@1: if (T->parm->msg_lev >= GLP_MSG_DBG) alpar@1: xprintf("Solving LP relaxation...\n"); alpar@1: ret = ios_solve_node(T); alpar@1: if (!(ret == 0 || ret == GLP_EOBJLL || ret == GLP_EOBJUL)) alpar@1: { if (T->parm->msg_lev >= GLP_MSG_ERR) alpar@1: xprintf("ios_driver: unable to solve current LP relaxation;" alpar@1: " glp_simplex returned %d\n", ret); alpar@1: ret = GLP_EFAIL; alpar@1: goto done; alpar@1: } alpar@1: /* analyze status of the basic solution to LP relaxation found */ alpar@1: p_stat = T->mip->pbs_stat; alpar@1: d_stat = T->mip->dbs_stat; alpar@1: if (p_stat == GLP_FEAS && d_stat == GLP_FEAS) alpar@1: { /* LP relaxation has optimal solution */ alpar@1: if (T->parm->msg_lev >= GLP_MSG_DBG) alpar@1: xprintf("Found optimal solution to LP relaxation\n"); alpar@1: } alpar@1: else if (d_stat == GLP_NOFEAS) alpar@1: { /* LP relaxation has no dual feasible solution */ alpar@1: /* since the current subproblem cannot have a larger feasible alpar@1: region than its parent, there is something wrong */ alpar@1: if (T->parm->msg_lev >= GLP_MSG_ERR) alpar@1: xprintf("ios_driver: current LP relaxation has no dual feas" alpar@1: "ible solution\n"); alpar@1: ret = GLP_EFAIL; alpar@1: goto done; alpar@1: } alpar@1: else if (p_stat == GLP_INFEAS && d_stat == GLP_FEAS) alpar@1: { /* LP relaxation has no primal solution which is better than alpar@1: the incumbent objective value */ alpar@1: xassert(T->mip->mip_stat == GLP_FEAS); alpar@1: if (T->parm->msg_lev >= GLP_MSG_DBG) alpar@1: xprintf("LP relaxation has no solution better than incumben" alpar@1: "t objective value\n"); alpar@1: /* prune the branch */ alpar@1: goto fath; alpar@1: } alpar@1: else if (p_stat == GLP_NOFEAS) alpar@1: { /* LP relaxation has no primal feasible solution */ alpar@1: if (T->parm->msg_lev >= GLP_MSG_DBG) alpar@1: xprintf("LP relaxation has no feasible solution\n"); alpar@1: /* prune the branch */ alpar@1: goto fath; alpar@1: } alpar@1: else alpar@1: { /* other cases cannot appear */ alpar@1: xassert(T->mip != T->mip); alpar@1: } alpar@1: /* at this point basic solution to LP relaxation of the current alpar@1: subproblem is optimal */ alpar@1: xassert(p_stat == GLP_FEAS && d_stat == GLP_FEAS); alpar@1: xassert(T->curr != NULL); alpar@1: T->curr->lp_obj = T->mip->obj_val; alpar@1: /* thus, it defines a local bound to integer optimal solution of alpar@1: the current subproblem */ alpar@1: { double bound = T->mip->obj_val; alpar@1: /* some local bound to the current subproblem could be already alpar@1: set before, so we should only improve it */ alpar@1: bound = ios_round_bound(T, bound); alpar@1: if (T->mip->dir == GLP_MIN) alpar@1: { if (T->curr->bound < bound) alpar@1: T->curr->bound = bound; alpar@1: } alpar@1: else if (T->mip->dir == GLP_MAX) alpar@1: { if (T->curr->bound > bound) alpar@1: T->curr->bound = bound; alpar@1: } alpar@1: else alpar@1: xassert(T->mip != T->mip); alpar@1: if (T->parm->msg_lev >= GLP_MSG_DBG) alpar@1: xprintf("Local bound is %.9e\n", bound); alpar@1: } alpar@1: /* if the local bound indicates that integer optimal solution of alpar@1: the current subproblem cannot be better than the global bound, alpar@1: prune the branch */ alpar@1: if (!is_branch_hopeful(T, p)) alpar@1: { if (T->parm->msg_lev >= GLP_MSG_DBG) alpar@1: xprintf("Current branch is hopeless and can be pruned\n"); alpar@1: goto fath; alpar@1: } alpar@1: /* let the application program generate additional rows ("lazy" alpar@1: constraints) */ alpar@1: xassert(T->reopt == 0); alpar@1: xassert(T->reinv == 0); alpar@1: if (T->parm->cb_func != NULL) alpar@1: { xassert(T->reason == 0); alpar@1: T->reason = GLP_IROWGEN; alpar@1: T->parm->cb_func(T, T->parm->cb_info); alpar@1: T->reason = 0; alpar@1: if (T->stop) alpar@1: { ret = GLP_ESTOP; alpar@1: goto done; alpar@1: } alpar@1: if (T->reopt) alpar@1: { /* some rows were added; re-optimization is needed */ alpar@1: T->reopt = T->reinv = 0; alpar@1: goto more; alpar@1: } alpar@1: if (T->reinv) alpar@1: { /* no rows were added, however, some inactive rows were alpar@1: removed */ alpar@1: T->reinv = 0; alpar@1: xassert(glp_factorize(T->mip) == 0); alpar@1: } alpar@1: } alpar@1: /* check if the basic solution is integer feasible */ alpar@1: check_integrality(T); alpar@1: /* if the basic solution satisfies to all integrality conditions, alpar@1: it is a new, better integer feasible solution */ alpar@1: if (T->curr->ii_cnt == 0) alpar@1: { if (T->parm->msg_lev >= GLP_MSG_DBG) alpar@1: xprintf("New integer feasible solution found\n"); alpar@1: if (T->parm->msg_lev >= GLP_MSG_ALL) alpar@1: display_cut_info(T); alpar@1: record_solution(T); alpar@1: if (T->parm->msg_lev >= GLP_MSG_ON) alpar@1: show_progress(T, 1); alpar@1: /* make the application program happy */ alpar@1: if (T->parm->cb_func != NULL) alpar@1: { xassert(T->reason == 0); alpar@1: T->reason = GLP_IBINGO; alpar@1: T->parm->cb_func(T, T->parm->cb_info); alpar@1: T->reason = 0; alpar@1: if (T->stop) alpar@1: { ret = GLP_ESTOP; alpar@1: goto done; alpar@1: } alpar@1: } alpar@1: /* since the current subproblem has been fathomed, prune its alpar@1: branch */ alpar@1: goto fath; alpar@1: } alpar@1: /* at this point basic solution to LP relaxation of the current alpar@1: subproblem is optimal, but integer infeasible */ alpar@1: /* try to fix some non-basic structural variables of integer kind alpar@1: on their current bounds due to reduced costs */ alpar@1: if (T->mip->mip_stat == GLP_FEAS) alpar@1: fix_by_red_cost(T); alpar@1: /* let the application program try to find some solution to the alpar@1: original MIP with a primal heuristic */ alpar@1: if (T->parm->cb_func != NULL) alpar@1: { xassert(T->reason == 0); alpar@1: T->reason = GLP_IHEUR; alpar@1: T->parm->cb_func(T, T->parm->cb_info); alpar@1: T->reason = 0; alpar@1: if (T->stop) alpar@1: { ret = GLP_ESTOP; alpar@1: goto done; alpar@1: } alpar@1: /* check if the current branch became hopeless */ alpar@1: if (!is_branch_hopeful(T, p)) alpar@1: { if (T->parm->msg_lev >= GLP_MSG_DBG) alpar@1: xprintf("Current branch became hopeless and can be prune" alpar@1: "d\n"); alpar@1: goto fath; alpar@1: } alpar@1: } alpar@1: /* try to find solution with the feasibility pump heuristic */ alpar@1: if (T->parm->fp_heur) alpar@1: { xassert(T->reason == 0); alpar@1: T->reason = GLP_IHEUR; alpar@1: ios_feas_pump(T); alpar@1: T->reason = 0; alpar@1: /* check if the current branch became hopeless */ alpar@1: if (!is_branch_hopeful(T, p)) alpar@1: { if (T->parm->msg_lev >= GLP_MSG_DBG) alpar@1: xprintf("Current branch became hopeless and can be prune" alpar@1: "d\n"); alpar@1: goto fath; alpar@1: } alpar@1: } alpar@1: /* it's time to generate cutting planes */ alpar@1: xassert(T->local != NULL); alpar@1: xassert(T->local->size == 0); alpar@1: /* let the application program generate some cuts; note that it alpar@1: can add cuts either to the local cut pool or directly to the alpar@1: current subproblem */ alpar@1: if (T->parm->cb_func != NULL) alpar@1: { xassert(T->reason == 0); alpar@1: T->reason = GLP_ICUTGEN; alpar@1: T->parm->cb_func(T, T->parm->cb_info); alpar@1: T->reason = 0; alpar@1: if (T->stop) alpar@1: { ret = GLP_ESTOP; alpar@1: goto done; alpar@1: } alpar@1: } alpar@1: /* try to generate generic cuts with built-in generators alpar@1: (as suggested by Matteo Fischetti et al. the built-in cuts alpar@1: are not generated at each branching node; an intense attempt alpar@1: of generating new cuts is only made at the root node, and then alpar@1: a moderate effort is spent after each backtracking step) */ alpar@1: if (T->curr->level == 0 || pred_p == 0) alpar@1: { xassert(T->reason == 0); alpar@1: T->reason = GLP_ICUTGEN; alpar@1: generate_cuts(T); alpar@1: T->reason = 0; alpar@1: } alpar@1: /* if the local cut pool is not empty, select useful cuts and add alpar@1: them to the current subproblem */ alpar@1: if (T->local->size > 0) alpar@1: { xassert(T->reason == 0); alpar@1: T->reason = GLP_ICUTGEN; alpar@1: ios_process_cuts(T); alpar@1: T->reason = 0; alpar@1: } alpar@1: /* clear the local cut pool */ alpar@1: ios_clear_pool(T, T->local); alpar@1: /* perform re-optimization, if necessary */ alpar@1: if (T->reopt) alpar@1: { T->reopt = 0; alpar@1: T->curr->changed++; alpar@1: goto more; alpar@1: } alpar@1: /* no cuts were generated; remove inactive cuts */ alpar@1: remove_cuts(T); alpar@1: if (T->parm->msg_lev >= GLP_MSG_ALL && T->curr->level == 0) alpar@1: display_cut_info(T); alpar@1: /* update history information used on pseudocost branching */ alpar@1: if (T->pcost != NULL) ios_pcost_update(T); alpar@1: /* it's time to perform branching */ alpar@1: xassert(T->br_var == 0); alpar@1: xassert(T->br_sel == 0); alpar@1: /* let the application program choose variable to branch on */ alpar@1: if (T->parm->cb_func != NULL) alpar@1: { xassert(T->reason == 0); alpar@1: xassert(T->br_var == 0); alpar@1: xassert(T->br_sel == 0); alpar@1: T->reason = GLP_IBRANCH; alpar@1: T->parm->cb_func(T, T->parm->cb_info); alpar@1: T->reason = 0; alpar@1: if (T->stop) alpar@1: { ret = GLP_ESTOP; alpar@1: goto done; alpar@1: } alpar@1: } alpar@1: /* if nothing has been chosen, choose some variable as specified alpar@1: by the branching technique option */ alpar@1: if (T->br_var == 0) alpar@1: T->br_var = ios_choose_var(T, &T->br_sel); alpar@1: /* perform actual branching */ alpar@1: curr_p = T->curr->p; alpar@1: ret = branch_on(T, T->br_var, T->br_sel); alpar@1: T->br_var = T->br_sel = 0; alpar@1: if (ret == 0) alpar@1: { /* both branches have been created */ alpar@1: pred_p = curr_p; alpar@1: goto loop; alpar@1: } alpar@1: else if (ret == 1) alpar@1: { /* one branch is hopeless and has been pruned, so now the alpar@1: current subproblem is other branch */ alpar@1: /* the current subproblem should be considered as a new one, alpar@1: since one bound of the branching variable was changed */ alpar@1: T->curr->solved = T->curr->changed = 0; alpar@1: goto more; alpar@1: } alpar@1: else if (ret == 2) alpar@1: { /* both branches are hopeless and have been pruned; new alpar@1: subproblem selection is needed to continue the search */ alpar@1: goto fath; alpar@1: } alpar@1: else alpar@1: xassert(ret != ret); alpar@1: fath: /* the current subproblem has been fathomed */ alpar@1: if (T->parm->msg_lev >= GLP_MSG_DBG) alpar@1: xprintf("Node %d fathomed\n", p); alpar@1: /* freeze the current subproblem */ alpar@1: ios_freeze_node(T); alpar@1: /* and prune the corresponding branch of the tree */ alpar@1: ios_delete_node(T, p); alpar@1: /* if a new integer feasible solution has just been found, other alpar@1: branches may become hopeless and therefore must be pruned */ alpar@1: if (T->mip->mip_stat == GLP_FEAS) cleanup_the_tree(T); alpar@1: /* new subproblem selection is needed due to backtracking */ alpar@1: pred_p = 0; alpar@1: goto loop; alpar@1: done: /* display progress of the search on exit from the solver */ alpar@1: if (T->parm->msg_lev >= GLP_MSG_ON) alpar@1: show_progress(T, 0); alpar@1: if (T->mir_gen != NULL) alpar@1: ios_mir_term(T->mir_gen), T->mir_gen = NULL; alpar@1: if (T->clq_gen != NULL) alpar@1: ios_clq_term(T->clq_gen), T->clq_gen = NULL; alpar@1: /* return to the calling program */ alpar@1: return ret; alpar@1: } alpar@1: alpar@1: /* eof */