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.
 
 
 
 

664 lines
26 KiB

/* glpios09.c (branching heuristics) */
/***********************************************************************
* This code is part of GLPK (GNU Linear Programming Kit).
*
* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
* 2009, 2010, 2011, 2013 Andrew Makhorin, Department for Applied
* Informatics, Moscow Aviation Institute, Moscow, Russia. All rights
* reserved. E-mail: <mao@gnu.org>.
*
* GLPK is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GLPK is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GLPK. If not, see <http://www.gnu.org/licenses/>.
***********************************************************************/
#include "env.h"
#include "glpios.h"
/***********************************************************************
* NAME
*
* ios_choose_var - select variable to branch on
*
* SYNOPSIS
*
* #include "glpios.h"
* int ios_choose_var(glp_tree *T, int *next);
*
* The routine ios_choose_var chooses a variable from the candidate
* list to branch on. Additionally the routine provides a flag stored
* in the location next to suggests which of the child subproblems
* should be solved next.
*
* RETURNS
*
* The routine ios_choose_var returns the ordinal number of the column
* choosen. */
static int branch_first(glp_tree *T, int *next);
static int branch_last(glp_tree *T, int *next);
static int branch_mostf(glp_tree *T, int *next);
static int branch_drtom(glp_tree *T, int *next);
int ios_choose_var(glp_tree *T, int *next)
{ int j;
if (T->parm->br_tech == GLP_BR_FFV)
{ /* branch on first fractional variable */
j = branch_first(T, next);
}
else if (T->parm->br_tech == GLP_BR_LFV)
{ /* branch on last fractional variable */
j = branch_last(T, next);
}
else if (T->parm->br_tech == GLP_BR_MFV)
{ /* branch on most fractional variable */
j = branch_mostf(T, next);
}
else if (T->parm->br_tech == GLP_BR_DTH)
{ /* branch using the heuristic by Dreebeck and Tomlin */
j = branch_drtom(T, next);
}
else if (T->parm->br_tech == GLP_BR_PCH)
{ /* hybrid pseudocost heuristic */
j = ios_pcost_branch(T, next);
}
else
xassert(T != T);
return j;
}
/***********************************************************************
* branch_first - choose first branching variable
*
* This routine looks up the list of structural variables and chooses
* the first one, which is of integer kind and has fractional value in
* optimal solution to the current LP relaxation.
*
* This routine also selects the branch to be solved next where integer
* infeasibility of the chosen variable is less than in other one. */
static int branch_first(glp_tree *T, int *_next)
{ int j, next;
double beta;
/* choose the column to branch on */
for (j = 1; j <= T->n; j++)
if (T->non_int[j]) break;
xassert(1 <= j && j <= T->n);
/* select the branch to be solved next */
beta = glp_get_col_prim(T->mip, j);
if (beta - floor(beta) < ceil(beta) - beta)
next = GLP_DN_BRNCH;
else
next = GLP_UP_BRNCH;
*_next = next;
return j;
}
/***********************************************************************
* branch_last - choose last branching variable
*
* This routine looks up the list of structural variables and chooses
* the last one, which is of integer kind and has fractional value in
* optimal solution to the current LP relaxation.
*
* This routine also selects the branch to be solved next where integer
* infeasibility of the chosen variable is less than in other one. */
static int branch_last(glp_tree *T, int *_next)
{ int j, next;
double beta;
/* choose the column to branch on */
for (j = T->n; j >= 1; j--)
if (T->non_int[j]) break;
xassert(1 <= j && j <= T->n);
/* select the branch to be solved next */
beta = glp_get_col_prim(T->mip, j);
if (beta - floor(beta) < ceil(beta) - beta)
next = GLP_DN_BRNCH;
else
next = GLP_UP_BRNCH;
*_next = next;
return j;
}
/***********************************************************************
* branch_mostf - choose most fractional branching variable
*
* This routine looks up the list of structural variables and chooses
* that one, which is of integer kind and has most fractional value in
* optimal solution to the current LP relaxation.
*
* This routine also selects the branch to be solved next where integer
* infeasibility of the chosen variable is less than in other one.
*
* (Alexander Martin notices that "...most infeasible is as good as
* random...".) */
static int branch_mostf(glp_tree *T, int *_next)
{ int j, jj, next;
double beta, most, temp;
/* choose the column to branch on */
jj = 0, most = DBL_MAX;
for (j = 1; j <= T->n; j++)
{ if (T->non_int[j])
{ beta = glp_get_col_prim(T->mip, j);
temp = floor(beta) + 0.5;
if (most > fabs(beta - temp))
{ jj = j, most = fabs(beta - temp);
if (beta < temp)
next = GLP_DN_BRNCH;
else
next = GLP_UP_BRNCH;
}
}
}
*_next = next;
return jj;
}
/***********************************************************************
* branch_drtom - choose branching var using Driebeck-Tomlin heuristic
*
* This routine chooses a structural variable, which is required to be
* integral and has fractional value in optimal solution of the current
* LP relaxation, using a heuristic proposed by Driebeck and Tomlin.
*
* The routine also selects the branch to be solved next, again due to
* Driebeck and Tomlin.
*
* This routine is based on the heuristic proposed in:
*
* Driebeck N.J. An algorithm for the solution of mixed-integer
* programming problems, Management Science, 12: 576-87 (1966);
*
* and improved in:
*
* Tomlin J.A. Branch and bound methods for integer and non-convex
* programming, in J.Abadie (ed.), Integer and Nonlinear Programming,
* North-Holland, Amsterdam, pp. 437-50 (1970).
*
* Must note that this heuristic is time-expensive, because computing
* one-step degradation (see the routine below) requires one BTRAN for
* each fractional-valued structural variable. */
static int branch_drtom(glp_tree *T, int *_next)
{ glp_prob *mip = T->mip;
int m = mip->m;
int n = mip->n;
unsigned char *non_int = T->non_int;
int j, jj, k, t, next, kase, len, stat, *ind;
double x, dk, alfa, delta_j, delta_k, delta_z, dz_dn, dz_up,
dd_dn, dd_up, degrad, *val;
/* basic solution of LP relaxation must be optimal */
xassert(glp_get_status(mip) == GLP_OPT);
/* allocate working arrays */
ind = xcalloc(1+n, sizeof(int));
val = xcalloc(1+n, sizeof(double));
/* nothing has been chosen so far */
jj = 0, degrad = -1.0;
/* walk through the list of columns (structural variables) */
for (j = 1; j <= n; j++)
{ /* if j-th column is not marked as fractional, skip it */
if (!non_int[j]) continue;
/* obtain (fractional) value of j-th column in basic solution
of LP relaxation */
x = glp_get_col_prim(mip, j);
/* since the value of j-th column is fractional, the column is
basic; compute corresponding row of the simplex table */
len = glp_eval_tab_row(mip, m+j, ind, val);
/* the following fragment computes a change in the objective
function: delta Z = new Z - old Z, where old Z is the
objective value in the current optimal basis, and new Z is
the objective value in the adjacent basis, for two cases:
1) if new upper bound ub' = floor(x[j]) is introduced for
j-th column (down branch);
2) if new lower bound lb' = ceil(x[j]) is introduced for
j-th column (up branch);
since in both cases the solution remaining dual feasible
becomes primal infeasible, one implicit simplex iteration
is performed to determine the change delta Z;
it is obvious that new Z, which is never better than old Z,
is a lower (minimization) or upper (maximization) bound of
the objective function for down- and up-branches. */
for (kase = -1; kase <= +1; kase += 2)
{ /* if kase < 0, the new upper bound of x[j] is introduced;
in this case x[j] should decrease in order to leave the
basis and go to its new upper bound */
/* if kase > 0, the new lower bound of x[j] is introduced;
in this case x[j] should increase in order to leave the
basis and go to its new lower bound */
/* apply the dual ratio test in order to determine which
auxiliary or structural variable should enter the basis
to keep dual feasibility */
k = glp_dual_rtest(mip, len, ind, val, kase, 1e-9);
if (k != 0) k = ind[k];
/* if no non-basic variable has been chosen, LP relaxation
of corresponding branch being primal infeasible and dual
unbounded has no primal feasible solution; in this case
the change delta Z is formally set to infinity */
if (k == 0)
{ delta_z =
(T->mip->dir == GLP_MIN ? +DBL_MAX : -DBL_MAX);
goto skip;
}
/* row of the simplex table that corresponds to non-basic
variable x[k] choosen by the dual ratio test is:
x[j] = ... + alfa * x[k] + ...
where alfa is the influence coefficient (an element of
the simplex table row) */
/* determine the coefficient alfa */
for (t = 1; t <= len; t++) if (ind[t] == k) break;
xassert(1 <= t && t <= len);
alfa = val[t];
/* since in the adjacent basis the variable x[j] becomes
non-basic, knowing its value in the current basis we can
determine its change delta x[j] = new x[j] - old x[j] */
delta_j = (kase < 0 ? floor(x) : ceil(x)) - x;
/* and knowing the coefficient alfa we can determine the
corresponding change delta x[k] = new x[k] - old x[k],
where old x[k] is a value of x[k] in the current basis,
and new x[k] is a value of x[k] in the adjacent basis */
delta_k = delta_j / alfa;
/* Tomlin noticed that if the variable x[k] is of integer
kind, its change cannot be less (eventually) than one in
the magnitude */
if (k > m && glp_get_col_kind(mip, k-m) != GLP_CV)
{ /* x[k] is structural integer variable */
if (fabs(delta_k - floor(delta_k + 0.5)) > 1e-3)
{ if (delta_k > 0.0)
delta_k = ceil(delta_k); /* +3.14 -> +4 */
else
delta_k = floor(delta_k); /* -3.14 -> -4 */
}
}
/* now determine the status and reduced cost of x[k] in the
current basis */
if (k <= m)
{ stat = glp_get_row_stat(mip, k);
dk = glp_get_row_dual(mip, k);
}
else
{ stat = glp_get_col_stat(mip, k-m);
dk = glp_get_col_dual(mip, k-m);
}
/* if the current basis is dual degenerate, some reduced
costs which are close to zero may have wrong sign due to
round-off errors, so correct the sign of d[k] */
switch (T->mip->dir)
{ case GLP_MIN:
if (stat == GLP_NL && dk < 0.0 ||
stat == GLP_NU && dk > 0.0 ||
stat == GLP_NF) dk = 0.0;
break;
case GLP_MAX:
if (stat == GLP_NL && dk > 0.0 ||
stat == GLP_NU && dk < 0.0 ||
stat == GLP_NF) dk = 0.0;
break;
default:
xassert(T != T);
}
/* now knowing the change of x[k] and its reduced cost d[k]
we can compute the corresponding change in the objective
function delta Z = new Z - old Z = d[k] * delta x[k];
note that due to Tomlin's modification new Z can be even
worse than in the adjacent basis */
delta_z = dk * delta_k;
skip: /* new Z is never better than old Z, therefore the change
delta Z is always non-negative (in case of minimization)
or non-positive (in case of maximization) */
switch (T->mip->dir)
{ case GLP_MIN: xassert(delta_z >= 0.0); break;
case GLP_MAX: xassert(delta_z <= 0.0); break;
default: xassert(T != T);
}
/* save the change in the objective fnction for down- and
up-branches, respectively */
if (kase < 0) dz_dn = delta_z; else dz_up = delta_z;
}
/* thus, in down-branch no integer feasible solution can be
better than Z + dz_dn, and in up-branch no integer feasible
solution can be better than Z + dz_up, where Z is value of
the objective function in the current basis */
/* following the heuristic by Driebeck and Tomlin we choose a
column (i.e. structural variable) which provides largest
degradation of the objective function in some of branches;
besides, we select the branch with smaller degradation to
be solved next and keep other branch with larger degradation
in the active list hoping to minimize the number of further
backtrackings */
if (degrad < fabs(dz_dn) || degrad < fabs(dz_up))
{ jj = j;
if (fabs(dz_dn) < fabs(dz_up))
{ /* select down branch to be solved next */
next = GLP_DN_BRNCH;
degrad = fabs(dz_up);
}
else
{ /* select up branch to be solved next */
next = GLP_UP_BRNCH;
degrad = fabs(dz_dn);
}
/* save the objective changes for printing */
dd_dn = dz_dn, dd_up = dz_up;
/* if down- or up-branch has no feasible solution, we does
not need to consider other candidates (in principle, the
corresponding branch could be pruned right now) */
if (degrad == DBL_MAX) break;
}
}
/* free working arrays */
xfree(ind);
xfree(val);
/* something must be chosen */
xassert(1 <= jj && jj <= n);
#if 1 /* 02/XI-2009 */
if (degrad < 1e-6 * (1.0 + 0.001 * fabs(mip->obj_val)))
{ jj = branch_mostf(T, &next);
goto done;
}
#endif
if (T->parm->msg_lev >= GLP_MSG_DBG)
{ xprintf("branch_drtom: column %d chosen to branch on\n", jj);
if (fabs(dd_dn) == DBL_MAX)
xprintf("branch_drtom: down-branch is infeasible\n");
else
xprintf("branch_drtom: down-branch bound is %.9e\n",
glp_get_obj_val(mip) + dd_dn);
if (fabs(dd_up) == DBL_MAX)
xprintf("branch_drtom: up-branch is infeasible\n");
else
xprintf("branch_drtom: up-branch bound is %.9e\n",
glp_get_obj_val(mip) + dd_up);
}
done: *_next = next;
return jj;
}
/**********************************************************************/
struct csa
{ /* common storage area */
int *dn_cnt; /* int dn_cnt[1+n]; */
/* dn_cnt[j] is the number of subproblems, whose LP relaxations
have been solved and which are down-branches for variable x[j];
dn_cnt[j] = 0 means the down pseudocost is uninitialized */
double *dn_sum; /* double dn_sum[1+n]; */
/* dn_sum[j] is the sum of per unit degradations of the objective
over all dn_cnt[j] subproblems */
int *up_cnt; /* int up_cnt[1+n]; */
/* up_cnt[j] is the number of subproblems, whose LP relaxations
have been solved and which are up-branches for variable x[j];
up_cnt[j] = 0 means the up pseudocost is uninitialized */
double *up_sum; /* double up_sum[1+n]; */
/* up_sum[j] is the sum of per unit degradations of the objective
over all up_cnt[j] subproblems */
};
void *ios_pcost_init(glp_tree *tree)
{ /* initialize working data used on pseudocost branching */
struct csa *csa;
int n = tree->n, j;
csa = xmalloc(sizeof(struct csa));
csa->dn_cnt = xcalloc(1+n, sizeof(int));
csa->dn_sum = xcalloc(1+n, sizeof(double));
csa->up_cnt = xcalloc(1+n, sizeof(int));
csa->up_sum = xcalloc(1+n, sizeof(double));
for (j = 1; j <= n; j++)
{ csa->dn_cnt[j] = csa->up_cnt[j] = 0;
csa->dn_sum[j] = csa->up_sum[j] = 0.0;
}
return csa;
}
static double eval_degrad(glp_prob *P, int j, double bnd)
{ /* compute degradation of the objective on fixing x[j] at given
value with a limited number of dual simplex iterations */
/* this routine fixes column x[j] at specified value bnd,
solves resulting LP, and returns a lower bound to degradation
of the objective, degrad >= 0 */
glp_prob *lp;
glp_smcp parm;
int ret;
double degrad;
/* the current basis must be optimal */
xassert(glp_get_status(P) == GLP_OPT);
/* create a copy of P */
lp = glp_create_prob();
glp_copy_prob(lp, P, 0);
/* fix column x[j] at specified value */
glp_set_col_bnds(lp, j, GLP_FX, bnd, bnd);
/* try to solve resulting LP */
glp_init_smcp(&parm);
parm.msg_lev = GLP_MSG_OFF;
parm.meth = GLP_DUAL;
parm.it_lim = 30;
parm.out_dly = 1000;
parm.meth = GLP_DUAL;
ret = glp_simplex(lp, &parm);
if (ret == 0 || ret == GLP_EITLIM)
{ if (glp_get_prim_stat(lp) == GLP_NOFEAS)
{ /* resulting LP has no primal feasible solution */
degrad = DBL_MAX;
}
else if (glp_get_dual_stat(lp) == GLP_FEAS)
{ /* resulting basis is optimal or at least dual feasible,
so we have the correct lower bound to degradation */
if (P->dir == GLP_MIN)
degrad = lp->obj_val - P->obj_val;
else if (P->dir == GLP_MAX)
degrad = P->obj_val - lp->obj_val;
else
xassert(P != P);
/* degradation cannot be negative by definition */
/* note that the lower bound to degradation may be close
to zero even if its exact value is zero due to round-off
errors on computing the objective value */
if (degrad < 1e-6 * (1.0 + 0.001 * fabs(P->obj_val)))
degrad = 0.0;
}
else
{ /* the final basis reported by the simplex solver is dual
infeasible, so we cannot determine a non-trivial lower
bound to degradation */
degrad = 0.0;
}
}
else
{ /* the simplex solver failed */
degrad = 0.0;
}
/* delete the copy of P */
glp_delete_prob(lp);
return degrad;
}
void ios_pcost_update(glp_tree *tree)
{ /* update history information for pseudocost branching */
/* this routine is called every time when LP relaxation of the
current subproblem has been solved to optimality with all lazy
and cutting plane constraints included */
int j;
double dx, dz, psi;
struct csa *csa = tree->pcost;
xassert(csa != NULL);
xassert(tree->curr != NULL);
/* if the current subproblem is the root, skip updating */
if (tree->curr->up == NULL) goto skip;
/* determine branching variable x[j], which was used in the
parent subproblem to create the current subproblem */
j = tree->curr->up->br_var;
xassert(1 <= j && j <= tree->n);
/* determine the change dx[j] = new x[j] - old x[j],
where new x[j] is a value of x[j] in optimal solution to LP
relaxation of the current subproblem, old x[j] is a value of
x[j] in optimal solution to LP relaxation of the parent
subproblem */
dx = tree->mip->col[j]->prim - tree->curr->up->br_val;
xassert(dx != 0.0);
/* determine corresponding change dz = new dz - old dz in the
objective function value */
dz = tree->mip->obj_val - tree->curr->up->lp_obj;
/* determine per unit degradation of the objective function */
psi = fabs(dz / dx);
/* update history information */
if (dx < 0.0)
{ /* the current subproblem is down-branch */
csa->dn_cnt[j]++;
csa->dn_sum[j] += psi;
}
else /* dx > 0.0 */
{ /* the current subproblem is up-branch */
csa->up_cnt[j]++;
csa->up_sum[j] += psi;
}
skip: return;
}
void ios_pcost_free(glp_tree *tree)
{ /* free working area used on pseudocost branching */
struct csa *csa = tree->pcost;
xassert(csa != NULL);
xfree(csa->dn_cnt);
xfree(csa->dn_sum);
xfree(csa->up_cnt);
xfree(csa->up_sum);
xfree(csa);
tree->pcost = NULL;
return;
}
static double eval_psi(glp_tree *T, int j, int brnch)
{ /* compute estimation of pseudocost of variable x[j] for down-
or up-branch */
struct csa *csa = T->pcost;
double beta, degrad, psi;
xassert(csa != NULL);
xassert(1 <= j && j <= T->n);
if (brnch == GLP_DN_BRNCH)
{ /* down-branch */
if (csa->dn_cnt[j] == 0)
{ /* initialize down pseudocost */
beta = T->mip->col[j]->prim;
degrad = eval_degrad(T->mip, j, floor(beta));
if (degrad == DBL_MAX)
{ psi = DBL_MAX;
goto done;
}
csa->dn_cnt[j] = 1;
csa->dn_sum[j] = degrad / (beta - floor(beta));
}
psi = csa->dn_sum[j] / (double)csa->dn_cnt[j];
}
else if (brnch == GLP_UP_BRNCH)
{ /* up-branch */
if (csa->up_cnt[j] == 0)
{ /* initialize up pseudocost */
beta = T->mip->col[j]->prim;
degrad = eval_degrad(T->mip, j, ceil(beta));
if (degrad == DBL_MAX)
{ psi = DBL_MAX;
goto done;
}
csa->up_cnt[j] = 1;
csa->up_sum[j] = degrad / (ceil(beta) - beta);
}
psi = csa->up_sum[j] / (double)csa->up_cnt[j];
}
else
xassert(brnch != brnch);
done: return psi;
}
static void progress(glp_tree *T)
{ /* display progress of pseudocost initialization */
struct csa *csa = T->pcost;
int j, nv = 0, ni = 0;
for (j = 1; j <= T->n; j++)
{ if (glp_ios_can_branch(T, j))
{ nv++;
if (csa->dn_cnt[j] > 0 && csa->up_cnt[j] > 0) ni++;
}
}
xprintf("Pseudocosts initialized for %d of %d variables\n",
ni, nv);
return;
}
int ios_pcost_branch(glp_tree *T, int *_next)
{ /* choose branching variable with pseudocost branching */
#if 0 /* 10/VI-2013 */
glp_long t = xtime();
#else
double t = xtime();
#endif
int j, jjj, sel;
double beta, psi, d1, d2, d, dmax;
/* initialize the working arrays */
if (T->pcost == NULL)
T->pcost = ios_pcost_init(T);
/* nothing has been chosen so far */
jjj = 0, dmax = -1.0;
/* go through the list of branching candidates */
for (j = 1; j <= T->n; j++)
{ if (!glp_ios_can_branch(T, j)) continue;
/* determine primal value of x[j] in optimal solution to LP
relaxation of the current subproblem */
beta = T->mip->col[j]->prim;
/* estimate pseudocost of x[j] for down-branch */
psi = eval_psi(T, j, GLP_DN_BRNCH);
if (psi == DBL_MAX)
{ /* down-branch has no primal feasible solution */
jjj = j, sel = GLP_DN_BRNCH;
goto done;
}
/* estimate degradation of the objective for down-branch */
d1 = psi * (beta - floor(beta));
/* estimate pseudocost of x[j] for up-branch */
psi = eval_psi(T, j, GLP_UP_BRNCH);
if (psi == DBL_MAX)
{ /* up-branch has no primal feasible solution */
jjj = j, sel = GLP_UP_BRNCH;
goto done;
}
/* estimate degradation of the objective for up-branch */
d2 = psi * (ceil(beta) - beta);
/* determine d = max(d1, d2) */
d = (d1 > d2 ? d1 : d2);
/* choose x[j] which provides maximal estimated degradation of
the objective either in down- or up-branch */
if (dmax < d)
{ dmax = d;
jjj = j;
/* continue the search from a subproblem, where degradation
is less than in other one */
sel = (d1 <= d2 ? GLP_DN_BRNCH : GLP_UP_BRNCH);
}
/* display progress of pseudocost initialization */
if (T->parm->msg_lev >= GLP_ON)
{ if (xdifftime(xtime(), t) >= 10.0)
{ progress(T);
t = xtime();
}
}
}
if (dmax == 0.0)
{ /* no degradation is indicated; choose a variable having most
fractional value */
jjj = branch_mostf(T, &sel);
}
done: *_next = sel;
return jjj;
}
/* eof */