alpar@1: /* glpios06.c (MIR cut generator) */ 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: #define _MIR_DEBUG 0 alpar@1: alpar@1: #define MAXAGGR 5 alpar@1: /* maximal number of rows which can be aggregated */ alpar@1: alpar@1: struct MIR alpar@1: { /* MIR cut generator working area */ alpar@1: /*--------------------------------------------------------------*/ alpar@1: /* global information valid for the root subproblem */ alpar@1: int m; alpar@1: /* number of rows (in the root subproblem) */ alpar@1: int n; alpar@1: /* number of columns */ alpar@1: char *skip; /* char skip[1+m]; */ alpar@1: /* skip[i], 1 <= i <= m, is a flag that means that row i should alpar@1: not be used because (1) it is not suitable, or (2) because it alpar@1: has been used in the aggregated constraint */ alpar@1: char *isint; /* char isint[1+m+n]; */ alpar@1: /* isint[k], 1 <= k <= m+n, is a flag that means that variable alpar@1: x[k] is integer (otherwise, continuous) */ alpar@1: double *lb; /* double lb[1+m+n]; */ alpar@1: /* lb[k], 1 <= k <= m+n, is lower bound of x[k]; -DBL_MAX means alpar@1: that x[k] has no lower bound */ alpar@1: int *vlb; /* int vlb[1+m+n]; */ alpar@1: /* vlb[k] = k', 1 <= k <= m+n, is the number of integer variable, alpar@1: which defines variable lower bound x[k] >= lb[k] * x[k']; zero alpar@1: means that x[k] has simple lower bound */ alpar@1: double *ub; /* double ub[1+m+n]; */ alpar@1: /* ub[k], 1 <= k <= m+n, is upper bound of x[k]; +DBL_MAX means alpar@1: that x[k] has no upper bound */ alpar@1: int *vub; /* int vub[1+m+n]; */ alpar@1: /* vub[k] = k', 1 <= k <= m+n, is the number of integer variable, alpar@1: which defines variable upper bound x[k] <= ub[k] * x[k']; zero alpar@1: means that x[k] has simple upper bound */ alpar@1: /*--------------------------------------------------------------*/ alpar@1: /* current (fractional) point to be separated */ alpar@1: double *x; /* double x[1+m+n]; */ alpar@1: /* x[k] is current value of auxiliary (1 <= k <= m) or structural alpar@1: (m+1 <= k <= m+n) variable */ alpar@1: /*--------------------------------------------------------------*/ alpar@1: /* aggregated constraint sum a[k] * x[k] = b, which is a linear alpar@1: combination of original constraints transformed to equalities alpar@1: by introducing auxiliary variables */ alpar@1: int agg_cnt; alpar@1: /* number of rows (original constraints) used to build aggregated alpar@1: constraint, 1 <= agg_cnt <= MAXAGGR */ alpar@1: int *agg_row; /* int agg_row[1+MAXAGGR]; */ alpar@1: /* agg_row[k], 1 <= k <= agg_cnt, is the row number used to build alpar@1: aggregated constraint */ alpar@1: IOSVEC *agg_vec; /* IOSVEC agg_vec[1:m+n]; */ alpar@1: /* sparse vector of aggregated constraint coefficients, a[k] */ alpar@1: double agg_rhs; alpar@1: /* right-hand side of the aggregated constraint, b */ alpar@1: /*--------------------------------------------------------------*/ alpar@1: /* bound substitution flags for modified constraint */ alpar@1: char *subst; /* char subst[1+m+n]; */ alpar@1: /* subst[k], 1 <= k <= m+n, is a bound substitution flag used for alpar@1: variable x[k]: alpar@1: '?' - x[k] is missing in modified constraint alpar@1: 'L' - x[k] = (lower bound) + x'[k] alpar@1: 'U' - x[k] = (upper bound) - x'[k] */ alpar@1: /*--------------------------------------------------------------*/ alpar@1: /* modified constraint sum a'[k] * x'[k] = b', where x'[k] >= 0, alpar@1: derived from aggregated constraint by substituting bounds; alpar@1: note that due to substitution of variable bounds there may be alpar@1: additional terms in the modified constraint */ alpar@1: IOSVEC *mod_vec; /* IOSVEC mod_vec[1:m+n]; */ alpar@1: /* sparse vector of modified constraint coefficients, a'[k] */ alpar@1: double mod_rhs; alpar@1: /* right-hand side of the modified constraint, b' */ alpar@1: /*--------------------------------------------------------------*/ alpar@1: /* cutting plane sum alpha[k] * x[k] <= beta */ alpar@1: IOSVEC *cut_vec; /* IOSVEC cut_vec[1:m+n]; */ alpar@1: /* sparse vector of cutting plane coefficients, alpha[k] */ alpar@1: double cut_rhs; alpar@1: /* right-hand size of the cutting plane, beta */ alpar@1: }; alpar@1: alpar@1: /*********************************************************************** alpar@1: * NAME alpar@1: * alpar@1: * ios_mir_init - initialize MIR cut generator alpar@1: * alpar@1: * SYNOPSIS alpar@1: * alpar@1: * #include "glpios.h" alpar@1: * void *ios_mir_init(glp_tree *tree); alpar@1: * alpar@1: * DESCRIPTION alpar@1: * alpar@1: * The routine ios_mir_init initializes the MIR cut generator assuming alpar@1: * that the current subproblem is the root subproblem. alpar@1: * alpar@1: * RETURNS alpar@1: * alpar@1: * The routine ios_mir_init returns a pointer to the MIR cut generator alpar@1: * working area. */ alpar@1: alpar@1: static void set_row_attrib(glp_tree *tree, struct MIR *mir) alpar@1: { /* set global row attributes */ alpar@1: glp_prob *mip = tree->mip; alpar@1: int m = mir->m; alpar@1: int k; alpar@1: for (k = 1; k <= m; k++) alpar@1: { GLPROW *row = mip->row[k]; alpar@1: mir->skip[k] = 0; alpar@1: mir->isint[k] = 0; alpar@1: switch (row->type) alpar@1: { case GLP_FR: alpar@1: mir->lb[k] = -DBL_MAX, mir->ub[k] = +DBL_MAX; break; alpar@1: case GLP_LO: alpar@1: mir->lb[k] = row->lb, mir->ub[k] = +DBL_MAX; break; alpar@1: case GLP_UP: alpar@1: mir->lb[k] = -DBL_MAX, mir->ub[k] = row->ub; break; alpar@1: case GLP_DB: alpar@1: mir->lb[k] = row->lb, mir->ub[k] = row->ub; break; alpar@1: case GLP_FX: alpar@1: mir->lb[k] = mir->ub[k] = row->lb; break; alpar@1: default: alpar@1: xassert(row != row); alpar@1: } alpar@1: mir->vlb[k] = mir->vub[k] = 0; alpar@1: } alpar@1: return; alpar@1: } alpar@1: alpar@1: static void set_col_attrib(glp_tree *tree, struct MIR *mir) alpar@1: { /* set global column attributes */ alpar@1: glp_prob *mip = tree->mip; alpar@1: int m = mir->m; alpar@1: int n = mir->n; alpar@1: int k; alpar@1: for (k = m+1; k <= m+n; k++) alpar@1: { GLPCOL *col = mip->col[k-m]; alpar@1: switch (col->kind) alpar@1: { case GLP_CV: alpar@1: mir->isint[k] = 0; break; alpar@1: case GLP_IV: alpar@1: mir->isint[k] = 1; break; alpar@1: default: alpar@1: xassert(col != col); alpar@1: } alpar@1: switch (col->type) alpar@1: { case GLP_FR: alpar@1: mir->lb[k] = -DBL_MAX, mir->ub[k] = +DBL_MAX; break; alpar@1: case GLP_LO: alpar@1: mir->lb[k] = col->lb, mir->ub[k] = +DBL_MAX; break; alpar@1: case GLP_UP: alpar@1: mir->lb[k] = -DBL_MAX, mir->ub[k] = col->ub; break; alpar@1: case GLP_DB: alpar@1: mir->lb[k] = col->lb, mir->ub[k] = col->ub; break; alpar@1: case GLP_FX: alpar@1: mir->lb[k] = mir->ub[k] = col->lb; break; alpar@1: default: alpar@1: xassert(col != col); alpar@1: } alpar@1: mir->vlb[k] = mir->vub[k] = 0; alpar@1: } alpar@1: return; alpar@1: } alpar@1: alpar@1: static void set_var_bounds(glp_tree *tree, struct MIR *mir) alpar@1: { /* set variable bounds */ alpar@1: glp_prob *mip = tree->mip; alpar@1: int m = mir->m; alpar@1: GLPAIJ *aij; alpar@1: int i, k1, k2; alpar@1: double a1, a2; alpar@1: for (i = 1; i <= m; i++) alpar@1: { /* we need the row to be '>= 0' or '<= 0' */ alpar@1: if (!(mir->lb[i] == 0.0 && mir->ub[i] == +DBL_MAX || alpar@1: mir->lb[i] == -DBL_MAX && mir->ub[i] == 0.0)) continue; alpar@1: /* take first term */ alpar@1: aij = mip->row[i]->ptr; alpar@1: if (aij == NULL) continue; alpar@1: k1 = m + aij->col->j, a1 = aij->val; alpar@1: /* take second term */ alpar@1: aij = aij->r_next; alpar@1: if (aij == NULL) continue; alpar@1: k2 = m + aij->col->j, a2 = aij->val; alpar@1: /* there must be only two terms */ alpar@1: if (aij->r_next != NULL) continue; alpar@1: /* interchange terms, if needed */ alpar@1: if (!mir->isint[k1] && mir->isint[k2]) alpar@1: ; alpar@1: else if (mir->isint[k1] && !mir->isint[k2]) alpar@1: { k2 = k1, a2 = a1; alpar@1: k1 = m + aij->col->j, a1 = aij->val; alpar@1: } alpar@1: else alpar@1: { /* both terms are either continuous or integer */ alpar@1: continue; alpar@1: } alpar@1: /* x[k2] should be double-bounded */ alpar@1: if (mir->lb[k2] == -DBL_MAX || mir->ub[k2] == +DBL_MAX || alpar@1: mir->lb[k2] == mir->ub[k2]) continue; alpar@1: /* change signs, if necessary */ alpar@1: if (mir->ub[i] == 0.0) a1 = - a1, a2 = - a2; alpar@1: /* now the row has the form a1 * x1 + a2 * x2 >= 0, where x1 alpar@1: is continuous, x2 is integer */ alpar@1: if (a1 > 0.0) alpar@1: { /* x1 >= - (a2 / a1) * x2 */ alpar@1: if (mir->vlb[k1] == 0) alpar@1: { /* set variable lower bound for x1 */ alpar@1: mir->lb[k1] = - a2 / a1; alpar@1: mir->vlb[k1] = k2; alpar@1: /* the row should not be used */ alpar@1: mir->skip[i] = 1; alpar@1: } alpar@1: } alpar@1: else /* a1 < 0.0 */ alpar@1: { /* x1 <= - (a2 / a1) * x2 */ alpar@1: if (mir->vub[k1] == 0) alpar@1: { /* set variable upper bound for x1 */ alpar@1: mir->ub[k1] = - a2 / a1; alpar@1: mir->vub[k1] = k2; alpar@1: /* the row should not be used */ alpar@1: mir->skip[i] = 1; alpar@1: } alpar@1: } alpar@1: } alpar@1: return; alpar@1: } alpar@1: alpar@1: static void mark_useless_rows(glp_tree *tree, struct MIR *mir) alpar@1: { /* mark rows which should not be used */ alpar@1: glp_prob *mip = tree->mip; alpar@1: int m = mir->m; alpar@1: GLPAIJ *aij; alpar@1: int i, k, nv; alpar@1: for (i = 1; i <= m; i++) alpar@1: { /* free rows should not be used */ alpar@1: if (mir->lb[i] == -DBL_MAX && mir->ub[i] == +DBL_MAX) alpar@1: { mir->skip[i] = 1; alpar@1: continue; alpar@1: } alpar@1: nv = 0; alpar@1: for (aij = mip->row[i]->ptr; aij != NULL; aij = aij->r_next) alpar@1: { k = m + aij->col->j; alpar@1: /* rows with free variables should not be used */ alpar@1: if (mir->lb[k] == -DBL_MAX && mir->ub[k] == +DBL_MAX) alpar@1: { mir->skip[i] = 1; alpar@1: break; alpar@1: } alpar@1: /* rows with integer variables having infinite (lower or alpar@1: upper) bound should not be used */ alpar@1: if (mir->isint[k] && mir->lb[k] == -DBL_MAX || alpar@1: mir->isint[k] && mir->ub[k] == +DBL_MAX) alpar@1: { mir->skip[i] = 1; alpar@1: break; alpar@1: } alpar@1: /* count non-fixed variables */ alpar@1: if (!(mir->vlb[k] == 0 && mir->vub[k] == 0 && alpar@1: mir->lb[k] == mir->ub[k])) nv++; alpar@1: } alpar@1: /* rows with all variables fixed should not be used */ alpar@1: if (nv == 0) alpar@1: { mir->skip[i] = 1; alpar@1: continue; alpar@1: } alpar@1: } alpar@1: return; alpar@1: } alpar@1: alpar@1: void *ios_mir_init(glp_tree *tree) alpar@1: { /* initialize MIR cut generator */ alpar@1: glp_prob *mip = tree->mip; alpar@1: int m = mip->m; alpar@1: int n = mip->n; alpar@1: struct MIR *mir; alpar@1: #if _MIR_DEBUG alpar@1: xprintf("ios_mir_init: warning: debug mode enabled\n"); alpar@1: #endif alpar@1: /* allocate working area */ alpar@1: mir = xmalloc(sizeof(struct MIR)); alpar@1: mir->m = m; alpar@1: mir->n = n; alpar@1: mir->skip = xcalloc(1+m, sizeof(char)); alpar@1: mir->isint = xcalloc(1+m+n, sizeof(char)); alpar@1: mir->lb = xcalloc(1+m+n, sizeof(double)); alpar@1: mir->vlb = xcalloc(1+m+n, sizeof(int)); alpar@1: mir->ub = xcalloc(1+m+n, sizeof(double)); alpar@1: mir->vub = xcalloc(1+m+n, sizeof(int)); alpar@1: mir->x = xcalloc(1+m+n, sizeof(double)); alpar@1: mir->agg_row = xcalloc(1+MAXAGGR, sizeof(int)); alpar@1: mir->agg_vec = ios_create_vec(m+n); alpar@1: mir->subst = xcalloc(1+m+n, sizeof(char)); alpar@1: mir->mod_vec = ios_create_vec(m+n); alpar@1: mir->cut_vec = ios_create_vec(m+n); alpar@1: /* set global row attributes */ alpar@1: set_row_attrib(tree, mir); alpar@1: /* set global column attributes */ alpar@1: set_col_attrib(tree, mir); alpar@1: /* set variable bounds */ alpar@1: set_var_bounds(tree, mir); alpar@1: /* mark rows which should not be used */ alpar@1: mark_useless_rows(tree, mir); alpar@1: return mir; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * NAME alpar@1: * alpar@1: * ios_mir_gen - generate MIR cuts alpar@1: * alpar@1: * SYNOPSIS alpar@1: * alpar@1: * #include "glpios.h" alpar@1: * void ios_mir_gen(glp_tree *tree, void *gen, IOSPOOL *pool); alpar@1: * alpar@1: * DESCRIPTION alpar@1: * alpar@1: * The routine ios_mir_gen generates MIR cuts for the current point and alpar@1: * adds them to the cut pool. */ alpar@1: alpar@1: static void get_current_point(glp_tree *tree, struct MIR *mir) alpar@1: { /* obtain current point */ alpar@1: glp_prob *mip = tree->mip; alpar@1: int m = mir->m; alpar@1: int n = mir->n; alpar@1: int k; alpar@1: for (k = 1; k <= m; k++) alpar@1: mir->x[k] = mip->row[k]->prim; alpar@1: for (k = m+1; k <= m+n; k++) alpar@1: mir->x[k] = mip->col[k-m]->prim; alpar@1: return; alpar@1: } alpar@1: alpar@1: #if _MIR_DEBUG alpar@1: static void check_current_point(struct MIR *mir) alpar@1: { /* check current point */ alpar@1: int m = mir->m; alpar@1: int n = mir->n; alpar@1: int k, kk; alpar@1: double lb, ub, eps; alpar@1: for (k = 1; k <= m+n; k++) alpar@1: { /* determine lower bound */ alpar@1: lb = mir->lb[k]; alpar@1: kk = mir->vlb[k]; alpar@1: if (kk != 0) alpar@1: { xassert(lb != -DBL_MAX); alpar@1: xassert(!mir->isint[k]); alpar@1: xassert(mir->isint[kk]); alpar@1: lb *= mir->x[kk]; alpar@1: } alpar@1: /* check lower bound */ alpar@1: if (lb != -DBL_MAX) alpar@1: { eps = 1e-6 * (1.0 + fabs(lb)); alpar@1: xassert(mir->x[k] >= lb - eps); alpar@1: } alpar@1: /* determine upper bound */ alpar@1: ub = mir->ub[k]; alpar@1: kk = mir->vub[k]; alpar@1: if (kk != 0) alpar@1: { xassert(ub != +DBL_MAX); alpar@1: xassert(!mir->isint[k]); alpar@1: xassert(mir->isint[kk]); alpar@1: ub *= mir->x[kk]; alpar@1: } alpar@1: /* check upper bound */ alpar@1: if (ub != +DBL_MAX) alpar@1: { eps = 1e-6 * (1.0 + fabs(ub)); alpar@1: xassert(mir->x[k] <= ub + eps); alpar@1: } alpar@1: } alpar@1: return; alpar@1: } alpar@1: #endif alpar@1: alpar@1: static void initial_agg_row(glp_tree *tree, struct MIR *mir, int i) alpar@1: { /* use original i-th row as initial aggregated constraint */ alpar@1: glp_prob *mip = tree->mip; alpar@1: int m = mir->m; alpar@1: GLPAIJ *aij; alpar@1: xassert(1 <= i && i <= m); alpar@1: xassert(!mir->skip[i]); alpar@1: /* mark i-th row in order not to use it in the same aggregated alpar@1: constraint */ alpar@1: mir->skip[i] = 2; alpar@1: mir->agg_cnt = 1; alpar@1: mir->agg_row[1] = i; alpar@1: /* use x[i] - sum a[i,j] * x[m+j] = 0, where x[i] is auxiliary alpar@1: variable of row i, x[m+j] are structural variables */ alpar@1: ios_clear_vec(mir->agg_vec); alpar@1: ios_set_vj(mir->agg_vec, i, 1.0); alpar@1: for (aij = mip->row[i]->ptr; aij != NULL; aij = aij->r_next) alpar@1: ios_set_vj(mir->agg_vec, m + aij->col->j, - aij->val); alpar@1: mir->agg_rhs = 0.0; alpar@1: #if _MIR_DEBUG alpar@1: ios_check_vec(mir->agg_vec); alpar@1: #endif alpar@1: return; alpar@1: } alpar@1: alpar@1: #if _MIR_DEBUG alpar@1: static void check_agg_row(struct MIR *mir) alpar@1: { /* check aggregated constraint */ alpar@1: int m = mir->m; alpar@1: int n = mir->n; alpar@1: int j, k; alpar@1: double r, big; alpar@1: /* compute the residual r = sum a[k] * x[k] - b and determine alpar@1: big = max(1, |a[k]|, |b|) */ alpar@1: r = 0.0, big = 1.0; alpar@1: for (j = 1; j <= mir->agg_vec->nnz; j++) alpar@1: { k = mir->agg_vec->ind[j]; alpar@1: xassert(1 <= k && k <= m+n); alpar@1: r += mir->agg_vec->val[j] * mir->x[k]; alpar@1: if (big < fabs(mir->agg_vec->val[j])) alpar@1: big = fabs(mir->agg_vec->val[j]); alpar@1: } alpar@1: r -= mir->agg_rhs; alpar@1: if (big < fabs(mir->agg_rhs)) alpar@1: big = fabs(mir->agg_rhs); alpar@1: /* the residual must be close to zero */ alpar@1: xassert(fabs(r) <= 1e-6 * big); alpar@1: return; alpar@1: } alpar@1: #endif alpar@1: alpar@1: static void subst_fixed_vars(struct MIR *mir) alpar@1: { /* substitute fixed variables into aggregated constraint */ alpar@1: int m = mir->m; alpar@1: int n = mir->n; alpar@1: int j, k; alpar@1: for (j = 1; j <= mir->agg_vec->nnz; j++) alpar@1: { k = mir->agg_vec->ind[j]; alpar@1: xassert(1 <= k && k <= m+n); alpar@1: if (mir->vlb[k] == 0 && mir->vub[k] == 0 && alpar@1: mir->lb[k] == mir->ub[k]) alpar@1: { /* x[k] is fixed */ alpar@1: mir->agg_rhs -= mir->agg_vec->val[j] * mir->lb[k]; alpar@1: mir->agg_vec->val[j] = 0.0; alpar@1: } alpar@1: } alpar@1: /* remove terms corresponding to fixed variables */ alpar@1: ios_clean_vec(mir->agg_vec, DBL_EPSILON); alpar@1: #if _MIR_DEBUG alpar@1: ios_check_vec(mir->agg_vec); alpar@1: #endif alpar@1: return; alpar@1: } alpar@1: alpar@1: static void bound_subst_heur(struct MIR *mir) alpar@1: { /* bound substitution heuristic */ alpar@1: int m = mir->m; alpar@1: int n = mir->n; alpar@1: int j, k, kk; alpar@1: double d1, d2; alpar@1: for (j = 1; j <= mir->agg_vec->nnz; j++) alpar@1: { k = mir->agg_vec->ind[j]; alpar@1: xassert(1 <= k && k <= m+n); alpar@1: if (mir->isint[k]) continue; /* skip integer variable */ alpar@1: /* compute distance from x[k] to its lower bound */ alpar@1: kk = mir->vlb[k]; alpar@1: if (kk == 0) alpar@1: { if (mir->lb[k] == -DBL_MAX) alpar@1: d1 = DBL_MAX; alpar@1: else alpar@1: d1 = mir->x[k] - mir->lb[k]; alpar@1: } alpar@1: else alpar@1: { xassert(1 <= kk && kk <= m+n); alpar@1: xassert(mir->isint[kk]); alpar@1: xassert(mir->lb[k] != -DBL_MAX); alpar@1: d1 = mir->x[k] - mir->lb[k] * mir->x[kk]; alpar@1: } alpar@1: /* compute distance from x[k] to its upper bound */ alpar@1: kk = mir->vub[k]; alpar@1: if (kk == 0) alpar@1: { if (mir->vub[k] == +DBL_MAX) alpar@1: d2 = DBL_MAX; alpar@1: else alpar@1: d2 = mir->ub[k] - mir->x[k]; alpar@1: } alpar@1: else alpar@1: { xassert(1 <= kk && kk <= m+n); alpar@1: xassert(mir->isint[kk]); alpar@1: xassert(mir->ub[k] != +DBL_MAX); alpar@1: d2 = mir->ub[k] * mir->x[kk] - mir->x[k]; alpar@1: } alpar@1: /* x[k] cannot be free */ alpar@1: xassert(d1 != DBL_MAX || d2 != DBL_MAX); alpar@1: /* choose the bound which is closer to x[k] */ alpar@1: xassert(mir->subst[k] == '?'); alpar@1: if (d1 <= d2) alpar@1: mir->subst[k] = 'L'; alpar@1: else alpar@1: mir->subst[k] = 'U'; alpar@1: } alpar@1: return; alpar@1: } alpar@1: alpar@1: static void build_mod_row(struct MIR *mir) alpar@1: { /* substitute bounds and build modified constraint */ alpar@1: int m = mir->m; alpar@1: int n = mir->n; alpar@1: int j, jj, k, kk; alpar@1: /* initially modified constraint is aggregated constraint */ alpar@1: ios_copy_vec(mir->mod_vec, mir->agg_vec); alpar@1: mir->mod_rhs = mir->agg_rhs; alpar@1: #if _MIR_DEBUG alpar@1: ios_check_vec(mir->mod_vec); alpar@1: #endif alpar@1: /* substitute bounds for continuous variables; note that due to alpar@1: substitution of variable bounds additional terms may appear in alpar@1: modified constraint */ alpar@1: for (j = mir->mod_vec->nnz; j >= 1; j--) alpar@1: { k = mir->mod_vec->ind[j]; alpar@1: xassert(1 <= k && k <= m+n); alpar@1: if (mir->isint[k]) continue; /* skip integer variable */ alpar@1: if (mir->subst[k] == 'L') alpar@1: { /* x[k] = (lower bound) + x'[k] */ alpar@1: xassert(mir->lb[k] != -DBL_MAX); alpar@1: kk = mir->vlb[k]; alpar@1: if (kk == 0) alpar@1: { /* x[k] = lb[k] + x'[k] */ alpar@1: mir->mod_rhs -= mir->mod_vec->val[j] * mir->lb[k]; alpar@1: } alpar@1: else alpar@1: { /* x[k] = lb[k] * x[kk] + x'[k] */ alpar@1: xassert(mir->isint[kk]); alpar@1: jj = mir->mod_vec->pos[kk]; alpar@1: if (jj == 0) alpar@1: { ios_set_vj(mir->mod_vec, kk, 1.0); alpar@1: jj = mir->mod_vec->pos[kk]; alpar@1: mir->mod_vec->val[jj] = 0.0; alpar@1: } alpar@1: mir->mod_vec->val[jj] += alpar@1: mir->mod_vec->val[j] * mir->lb[k]; alpar@1: } alpar@1: } alpar@1: else if (mir->subst[k] == 'U') alpar@1: { /* x[k] = (upper bound) - x'[k] */ alpar@1: xassert(mir->ub[k] != +DBL_MAX); alpar@1: kk = mir->vub[k]; alpar@1: if (kk == 0) alpar@1: { /* x[k] = ub[k] - x'[k] */ alpar@1: mir->mod_rhs -= mir->mod_vec->val[j] * mir->ub[k]; alpar@1: } alpar@1: else alpar@1: { /* x[k] = ub[k] * x[kk] - x'[k] */ alpar@1: xassert(mir->isint[kk]); alpar@1: jj = mir->mod_vec->pos[kk]; alpar@1: if (jj == 0) alpar@1: { ios_set_vj(mir->mod_vec, kk, 1.0); alpar@1: jj = mir->mod_vec->pos[kk]; alpar@1: mir->mod_vec->val[jj] = 0.0; alpar@1: } alpar@1: mir->mod_vec->val[jj] += alpar@1: mir->mod_vec->val[j] * mir->ub[k]; alpar@1: } alpar@1: mir->mod_vec->val[j] = - mir->mod_vec->val[j]; alpar@1: } alpar@1: else alpar@1: xassert(k != k); alpar@1: } alpar@1: #if _MIR_DEBUG alpar@1: ios_check_vec(mir->mod_vec); alpar@1: #endif alpar@1: /* substitute bounds for integer variables */ alpar@1: for (j = 1; j <= mir->mod_vec->nnz; j++) alpar@1: { k = mir->mod_vec->ind[j]; alpar@1: xassert(1 <= k && k <= m+n); alpar@1: if (!mir->isint[k]) continue; /* skip continuous variable */ alpar@1: xassert(mir->subst[k] == '?'); alpar@1: xassert(mir->vlb[k] == 0 && mir->vub[k] == 0); alpar@1: xassert(mir->lb[k] != -DBL_MAX && mir->ub[k] != +DBL_MAX); alpar@1: if (fabs(mir->lb[k]) <= fabs(mir->ub[k])) alpar@1: { /* x[k] = lb[k] + x'[k] */ alpar@1: mir->subst[k] = 'L'; alpar@1: mir->mod_rhs -= mir->mod_vec->val[j] * mir->lb[k]; alpar@1: } alpar@1: else alpar@1: { /* x[k] = ub[k] - x'[k] */ alpar@1: mir->subst[k] = 'U'; alpar@1: mir->mod_rhs -= mir->mod_vec->val[j] * mir->ub[k]; alpar@1: mir->mod_vec->val[j] = - mir->mod_vec->val[j]; alpar@1: } alpar@1: } alpar@1: #if _MIR_DEBUG alpar@1: ios_check_vec(mir->mod_vec); alpar@1: #endif alpar@1: return; alpar@1: } alpar@1: alpar@1: #if _MIR_DEBUG alpar@1: static void check_mod_row(struct MIR *mir) alpar@1: { /* check modified constraint */ alpar@1: int m = mir->m; alpar@1: int n = mir->n; alpar@1: int j, k, kk; alpar@1: double r, big, x; alpar@1: /* compute the residual r = sum a'[k] * x'[k] - b' and determine alpar@1: big = max(1, |a[k]|, |b|) */ alpar@1: r = 0.0, big = 1.0; alpar@1: for (j = 1; j <= mir->mod_vec->nnz; j++) alpar@1: { k = mir->mod_vec->ind[j]; alpar@1: xassert(1 <= k && k <= m+n); alpar@1: if (mir->subst[k] == 'L') alpar@1: { /* x'[k] = x[k] - (lower bound) */ alpar@1: xassert(mir->lb[k] != -DBL_MAX); alpar@1: kk = mir->vlb[k]; alpar@1: if (kk == 0) alpar@1: x = mir->x[k] - mir->lb[k]; alpar@1: else alpar@1: x = mir->x[k] - mir->lb[k] * mir->x[kk]; alpar@1: } alpar@1: else if (mir->subst[k] == 'U') alpar@1: { /* x'[k] = (upper bound) - x[k] */ alpar@1: xassert(mir->ub[k] != +DBL_MAX); alpar@1: kk = mir->vub[k]; alpar@1: if (kk == 0) alpar@1: x = mir->ub[k] - mir->x[k]; alpar@1: else alpar@1: x = mir->ub[k] * mir->x[kk] - mir->x[k]; alpar@1: } alpar@1: else alpar@1: xassert(k != k); alpar@1: r += mir->mod_vec->val[j] * x; alpar@1: if (big < fabs(mir->mod_vec->val[j])) alpar@1: big = fabs(mir->mod_vec->val[j]); alpar@1: } alpar@1: r -= mir->mod_rhs; alpar@1: if (big < fabs(mir->mod_rhs)) alpar@1: big = fabs(mir->mod_rhs); alpar@1: /* the residual must be close to zero */ alpar@1: xassert(fabs(r) <= 1e-6 * big); alpar@1: return; alpar@1: } alpar@1: #endif alpar@1: alpar@1: /*********************************************************************** alpar@1: * mir_ineq - construct MIR inequality alpar@1: * alpar@1: * Given the single constraint mixed integer set alpar@1: * alpar@1: * |N| alpar@1: * X = {(x,s) in Z x R : sum a[j] * x[j] <= b + s}, alpar@1: * + + j in N alpar@1: * alpar@1: * this routine constructs the mixed integer rounding (MIR) inequality alpar@1: * alpar@1: * sum alpha[j] * x[j] <= beta + gamma * s, alpar@1: * j in N alpar@1: * alpar@1: * which is valid for X. alpar@1: * alpar@1: * If the MIR inequality has been successfully constructed, the routine alpar@1: * returns zero. Otherwise, if b is close to nearest integer, there may alpar@1: * be numeric difficulties due to big coefficients; so in this case the alpar@1: * routine returns non-zero. */ alpar@1: alpar@1: static int mir_ineq(const int n, const double a[], const double b, alpar@1: double alpha[], double *beta, double *gamma) alpar@1: { int j; alpar@1: double f, t; alpar@1: if (fabs(b - floor(b + .5)) < 0.01) alpar@1: return 1; alpar@1: f = b - floor(b); alpar@1: for (j = 1; j <= n; j++) alpar@1: { t = (a[j] - floor(a[j])) - f; alpar@1: if (t <= 0.0) alpar@1: alpha[j] = floor(a[j]); alpar@1: else alpar@1: alpha[j] = floor(a[j]) + t / (1.0 - f); alpar@1: } alpar@1: *beta = floor(b); alpar@1: *gamma = 1.0 / (1.0 - f); alpar@1: return 0; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * cmir_ineq - construct c-MIR inequality alpar@1: * alpar@1: * Given the mixed knapsack set alpar@1: * alpar@1: * MK |N| alpar@1: * X = {(x,s) in Z x R : sum a[j] * x[j] <= b + s, alpar@1: * + + j in N alpar@1: * alpar@1: * x[j] <= u[j]}, alpar@1: * alpar@1: * a subset C of variables to be complemented, and a divisor delta > 0, alpar@1: * this routine constructs the complemented MIR (c-MIR) inequality alpar@1: * alpar@1: * sum alpha[j] * x[j] <= beta + gamma * s, alpar@1: * j in N alpar@1: * MK alpar@1: * which is valid for X . alpar@1: * alpar@1: * If the c-MIR inequality has been successfully constructed, the alpar@1: * routine returns zero. Otherwise, if there is a risk of numerical alpar@1: * difficulties due to big coefficients (see comments to the routine alpar@1: * mir_ineq), the routine cmir_ineq returns non-zero. */ alpar@1: alpar@1: static int cmir_ineq(const int n, const double a[], const double b, alpar@1: const double u[], const char cset[], const double delta, alpar@1: double alpha[], double *beta, double *gamma) alpar@1: { int j; alpar@1: double *aa, bb; alpar@1: aa = alpha, bb = b; alpar@1: for (j = 1; j <= n; j++) alpar@1: { aa[j] = a[j] / delta; alpar@1: if (cset[j]) alpar@1: aa[j] = - aa[j], bb -= a[j] * u[j]; alpar@1: } alpar@1: bb /= delta; alpar@1: if (mir_ineq(n, aa, bb, alpha, beta, gamma)) return 1; alpar@1: for (j = 1; j <= n; j++) alpar@1: { if (cset[j]) alpar@1: alpha[j] = - alpha[j], *beta += alpha[j] * u[j]; alpar@1: } alpar@1: *gamma /= delta; alpar@1: return 0; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * cmir_sep - c-MIR separation heuristic alpar@1: * alpar@1: * Given the mixed knapsack set alpar@1: * alpar@1: * MK |N| alpar@1: * X = {(x,s) in Z x R : sum a[j] * x[j] <= b + s, alpar@1: * + + j in N alpar@1: * alpar@1: * x[j] <= u[j]} alpar@1: * alpar@1: * * * alpar@1: * and a fractional point (x , s ), this routine tries to construct alpar@1: * c-MIR inequality alpar@1: * alpar@1: * sum alpha[j] * x[j] <= beta + gamma * s, alpar@1: * j in N alpar@1: * MK alpar@1: * which is valid for X and has (desirably maximal) violation at the alpar@1: * fractional point given. This is attained by choosing an appropriate alpar@1: * set C of variables to be complemented and a divisor delta > 0, which alpar@1: * together define corresponding c-MIR inequality. alpar@1: * alpar@1: * If a violated c-MIR inequality has been successfully constructed, alpar@1: * the routine returns its violation: alpar@1: * alpar@1: * * * alpar@1: * sum alpha[j] * x [j] - beta - gamma * s , alpar@1: * j in N alpar@1: * alpar@1: * which is positive. In case of failure the routine returns zero. */ alpar@1: alpar@1: struct vset { int j; double v; }; alpar@1: alpar@1: static int cmir_cmp(const void *p1, const void *p2) alpar@1: { const struct vset *v1 = p1, *v2 = p2; alpar@1: if (v1->v < v2->v) return -1; alpar@1: if (v1->v > v2->v) return +1; alpar@1: return 0; alpar@1: } alpar@1: alpar@1: static double cmir_sep(const int n, const double a[], const double b, alpar@1: const double u[], const double x[], const double s, alpar@1: double alpha[], double *beta, double *gamma) alpar@1: { int fail, j, k, nv, v; alpar@1: double delta, eps, d_try[1+3], r, r_best; alpar@1: char *cset; alpar@1: struct vset *vset; alpar@1: /* allocate working arrays */ alpar@1: cset = xcalloc(1+n, sizeof(char)); alpar@1: vset = xcalloc(1+n, sizeof(struct vset)); alpar@1: /* choose initial C */ alpar@1: for (j = 1; j <= n; j++) alpar@1: cset[j] = (char)(x[j] >= 0.5 * u[j]); alpar@1: /* choose initial delta */ alpar@1: r_best = delta = 0.0; alpar@1: for (j = 1; j <= n; j++) alpar@1: { xassert(a[j] != 0.0); alpar@1: /* if x[j] is close to its bounds, skip it */ alpar@1: eps = 1e-9 * (1.0 + fabs(u[j])); alpar@1: if (x[j] < eps || x[j] > u[j] - eps) continue; alpar@1: /* try delta = |a[j]| to construct c-MIR inequality */ alpar@1: fail = cmir_ineq(n, a, b, u, cset, fabs(a[j]), alpha, beta, alpar@1: gamma); alpar@1: if (fail) continue; alpar@1: /* compute violation */ alpar@1: r = - (*beta) - (*gamma) * s; alpar@1: for (k = 1; k <= n; k++) r += alpha[k] * x[k]; alpar@1: if (r_best < r) r_best = r, delta = fabs(a[j]); alpar@1: } alpar@1: if (r_best < 0.001) r_best = 0.0; alpar@1: if (r_best == 0.0) goto done; alpar@1: xassert(delta > 0.0); alpar@1: /* try to increase violation by dividing delta by 2, 4, and 8, alpar@1: respectively */ alpar@1: d_try[1] = delta / 2.0; alpar@1: d_try[2] = delta / 4.0; alpar@1: d_try[3] = delta / 8.0; alpar@1: for (j = 1; j <= 3; j++) alpar@1: { /* construct c-MIR inequality */ alpar@1: fail = cmir_ineq(n, a, b, u, cset, d_try[j], alpha, beta, alpar@1: gamma); alpar@1: if (fail) continue; alpar@1: /* compute violation */ alpar@1: r = - (*beta) - (*gamma) * s; alpar@1: for (k = 1; k <= n; k++) r += alpha[k] * x[k]; alpar@1: if (r_best < r) r_best = r, delta = d_try[j]; alpar@1: } alpar@1: /* build subset of variables lying strictly between their bounds alpar@1: and order it by nondecreasing values of |x[j] - u[j]/2| */ alpar@1: nv = 0; alpar@1: for (j = 1; j <= n; j++) alpar@1: { /* if x[j] is close to its bounds, skip it */ alpar@1: eps = 1e-9 * (1.0 + fabs(u[j])); alpar@1: if (x[j] < eps || x[j] > u[j] - eps) continue; alpar@1: /* add x[j] to the subset */ alpar@1: nv++; alpar@1: vset[nv].j = j; alpar@1: vset[nv].v = fabs(x[j] - 0.5 * u[j]); alpar@1: } alpar@1: qsort(&vset[1], nv, sizeof(struct vset), cmir_cmp); alpar@1: /* try to increase violation by successively complementing each alpar@1: variable in the subset */ alpar@1: for (v = 1; v <= nv; v++) alpar@1: { j = vset[v].j; alpar@1: /* replace x[j] by its complement or vice versa */ alpar@1: cset[j] = (char)!cset[j]; alpar@1: /* construct c-MIR inequality */ alpar@1: fail = cmir_ineq(n, a, b, u, cset, delta, alpha, beta, gamma); alpar@1: /* restore the variable */ alpar@1: cset[j] = (char)!cset[j]; alpar@1: /* do not replace the variable in case of failure */ alpar@1: if (fail) continue; alpar@1: /* compute violation */ alpar@1: r = - (*beta) - (*gamma) * s; alpar@1: for (k = 1; k <= n; k++) r += alpha[k] * x[k]; alpar@1: if (r_best < r) r_best = r, cset[j] = (char)!cset[j]; alpar@1: } alpar@1: /* construct the best c-MIR inequality chosen */ alpar@1: fail = cmir_ineq(n, a, b, u, cset, delta, alpha, beta, gamma); alpar@1: xassert(!fail); alpar@1: done: /* free working arrays */ alpar@1: xfree(cset); alpar@1: xfree(vset); alpar@1: /* return to the calling routine */ alpar@1: return r_best; alpar@1: } alpar@1: alpar@1: static double generate(struct MIR *mir) alpar@1: { /* try to generate violated c-MIR cut for modified constraint */ alpar@1: int m = mir->m; alpar@1: int n = mir->n; alpar@1: int j, k, kk, nint; alpar@1: double s, *u, *x, *alpha, r_best = 0.0, b, beta, gamma; alpar@1: ios_copy_vec(mir->cut_vec, mir->mod_vec); alpar@1: mir->cut_rhs = mir->mod_rhs; alpar@1: /* remove small terms, which can appear due to substitution of alpar@1: variable bounds */ alpar@1: ios_clean_vec(mir->cut_vec, DBL_EPSILON); alpar@1: #if _MIR_DEBUG alpar@1: ios_check_vec(mir->cut_vec); alpar@1: #endif alpar@1: /* remove positive continuous terms to obtain MK relaxation */ alpar@1: for (j = 1; j <= mir->cut_vec->nnz; j++) alpar@1: { k = mir->cut_vec->ind[j]; alpar@1: xassert(1 <= k && k <= m+n); alpar@1: if (!mir->isint[k] && mir->cut_vec->val[j] > 0.0) alpar@1: mir->cut_vec->val[j] = 0.0; alpar@1: } alpar@1: ios_clean_vec(mir->cut_vec, 0.0); alpar@1: #if _MIR_DEBUG alpar@1: ios_check_vec(mir->cut_vec); alpar@1: #endif alpar@1: /* move integer terms to the beginning of the sparse vector and alpar@1: determine the number of integer variables */ alpar@1: nint = 0; alpar@1: for (j = 1; j <= mir->cut_vec->nnz; j++) alpar@1: { k = mir->cut_vec->ind[j]; alpar@1: xassert(1 <= k && k <= m+n); alpar@1: if (mir->isint[k]) alpar@1: { double temp; alpar@1: nint++; alpar@1: /* interchange elements [nint] and [j] */ alpar@1: kk = mir->cut_vec->ind[nint]; alpar@1: mir->cut_vec->pos[k] = nint; alpar@1: mir->cut_vec->pos[kk] = j; alpar@1: mir->cut_vec->ind[nint] = k; alpar@1: mir->cut_vec->ind[j] = kk; alpar@1: temp = mir->cut_vec->val[nint]; alpar@1: mir->cut_vec->val[nint] = mir->cut_vec->val[j]; alpar@1: mir->cut_vec->val[j] = temp; alpar@1: } alpar@1: } alpar@1: #if _MIR_DEBUG alpar@1: ios_check_vec(mir->cut_vec); alpar@1: #endif alpar@1: /* if there is no integer variable, nothing to generate */ alpar@1: if (nint == 0) goto done; alpar@1: /* allocate working arrays */ alpar@1: u = xcalloc(1+nint, sizeof(double)); alpar@1: x = xcalloc(1+nint, sizeof(double)); alpar@1: alpha = xcalloc(1+nint, sizeof(double)); alpar@1: /* determine u and x */ alpar@1: for (j = 1; j <= nint; j++) alpar@1: { k = mir->cut_vec->ind[j]; alpar@1: xassert(m+1 <= k && k <= m+n); alpar@1: xassert(mir->isint[k]); alpar@1: u[j] = mir->ub[k] - mir->lb[k]; alpar@1: xassert(u[j] >= 1.0); alpar@1: if (mir->subst[k] == 'L') alpar@1: x[j] = mir->x[k] - mir->lb[k]; alpar@1: else if (mir->subst[k] == 'U') alpar@1: x[j] = mir->ub[k] - mir->x[k]; alpar@1: else alpar@1: xassert(k != k); alpar@1: xassert(x[j] >= -0.001); alpar@1: if (x[j] < 0.0) x[j] = 0.0; alpar@1: } alpar@1: /* compute s = - sum of continuous terms */ alpar@1: s = 0.0; alpar@1: for (j = nint+1; j <= mir->cut_vec->nnz; j++) alpar@1: { double x; alpar@1: k = mir->cut_vec->ind[j]; alpar@1: xassert(1 <= k && k <= m+n); alpar@1: /* must be continuous */ alpar@1: xassert(!mir->isint[k]); alpar@1: if (mir->subst[k] == 'L') alpar@1: { xassert(mir->lb[k] != -DBL_MAX); alpar@1: kk = mir->vlb[k]; alpar@1: if (kk == 0) alpar@1: x = mir->x[k] - mir->lb[k]; alpar@1: else alpar@1: x = mir->x[k] - mir->lb[k] * mir->x[kk]; alpar@1: } alpar@1: else if (mir->subst[k] == 'U') alpar@1: { xassert(mir->ub[k] != +DBL_MAX); alpar@1: kk = mir->vub[k]; alpar@1: if (kk == 0) alpar@1: x = mir->ub[k] - mir->x[k]; alpar@1: else alpar@1: x = mir->ub[k] * mir->x[kk] - mir->x[k]; alpar@1: } alpar@1: else alpar@1: xassert(k != k); alpar@1: xassert(x >= -0.001); alpar@1: if (x < 0.0) x = 0.0; alpar@1: s -= mir->cut_vec->val[j] * x; alpar@1: } alpar@1: xassert(s >= 0.0); alpar@1: /* apply heuristic to obtain most violated c-MIR inequality */ alpar@1: b = mir->cut_rhs; alpar@1: r_best = cmir_sep(nint, mir->cut_vec->val, b, u, x, s, alpha, alpar@1: &beta, &gamma); alpar@1: if (r_best == 0.0) goto skip; alpar@1: xassert(r_best > 0.0); alpar@1: /* convert to raw cut */ alpar@1: /* sum alpha[j] * x[j] <= beta + gamma * s */ alpar@1: for (j = 1; j <= nint; j++) alpar@1: mir->cut_vec->val[j] = alpha[j]; alpar@1: for (j = nint+1; j <= mir->cut_vec->nnz; j++) alpar@1: { k = mir->cut_vec->ind[j]; alpar@1: if (k <= m+n) mir->cut_vec->val[j] *= gamma; alpar@1: } alpar@1: mir->cut_rhs = beta; alpar@1: #if _MIR_DEBUG alpar@1: ios_check_vec(mir->cut_vec); alpar@1: #endif alpar@1: skip: /* free working arrays */ alpar@1: xfree(u); alpar@1: xfree(x); alpar@1: xfree(alpha); alpar@1: done: return r_best; alpar@1: } alpar@1: alpar@1: #if _MIR_DEBUG alpar@1: static void check_raw_cut(struct MIR *mir, double r_best) alpar@1: { /* check raw cut before back bound substitution */ alpar@1: int m = mir->m; alpar@1: int n = mir->n; alpar@1: int j, k, kk; alpar@1: double r, big, x; alpar@1: /* compute the residual r = sum a[k] * x[k] - b and determine alpar@1: big = max(1, |a[k]|, |b|) */ alpar@1: r = 0.0, big = 1.0; alpar@1: for (j = 1; j <= mir->cut_vec->nnz; j++) alpar@1: { k = mir->cut_vec->ind[j]; alpar@1: xassert(1 <= k && k <= m+n); alpar@1: if (mir->subst[k] == 'L') alpar@1: { xassert(mir->lb[k] != -DBL_MAX); alpar@1: kk = mir->vlb[k]; alpar@1: if (kk == 0) alpar@1: x = mir->x[k] - mir->lb[k]; alpar@1: else alpar@1: x = mir->x[k] - mir->lb[k] * mir->x[kk]; alpar@1: } alpar@1: else if (mir->subst[k] == 'U') alpar@1: { xassert(mir->ub[k] != +DBL_MAX); alpar@1: kk = mir->vub[k]; alpar@1: if (kk == 0) alpar@1: x = mir->ub[k] - mir->x[k]; alpar@1: else alpar@1: x = mir->ub[k] * mir->x[kk] - mir->x[k]; alpar@1: } alpar@1: else alpar@1: xassert(k != k); alpar@1: r += mir->cut_vec->val[j] * x; alpar@1: if (big < fabs(mir->cut_vec->val[j])) alpar@1: big = fabs(mir->cut_vec->val[j]); alpar@1: } alpar@1: r -= mir->cut_rhs; alpar@1: if (big < fabs(mir->cut_rhs)) alpar@1: big = fabs(mir->cut_rhs); alpar@1: /* the residual must be close to r_best */ alpar@1: xassert(fabs(r - r_best) <= 1e-6 * big); alpar@1: return; alpar@1: } alpar@1: #endif alpar@1: alpar@1: static void back_subst(struct MIR *mir) alpar@1: { /* back substitution of original bounds */ alpar@1: int m = mir->m; alpar@1: int n = mir->n; alpar@1: int j, jj, k, kk; alpar@1: /* at first, restore bounds of integer variables (because on alpar@1: restoring variable bounds of continuous variables we need alpar@1: original, not shifted, bounds of integer variables) */ alpar@1: for (j = 1; j <= mir->cut_vec->nnz; j++) alpar@1: { k = mir->cut_vec->ind[j]; alpar@1: xassert(1 <= k && k <= m+n); alpar@1: if (!mir->isint[k]) continue; /* skip continuous */ alpar@1: if (mir->subst[k] == 'L') alpar@1: { /* x'[k] = x[k] - lb[k] */ alpar@1: xassert(mir->lb[k] != -DBL_MAX); alpar@1: xassert(mir->vlb[k] == 0); alpar@1: mir->cut_rhs += mir->cut_vec->val[j] * mir->lb[k]; alpar@1: } alpar@1: else if (mir->subst[k] == 'U') alpar@1: { /* x'[k] = ub[k] - x[k] */ alpar@1: xassert(mir->ub[k] != +DBL_MAX); alpar@1: xassert(mir->vub[k] == 0); alpar@1: mir->cut_rhs -= mir->cut_vec->val[j] * mir->ub[k]; alpar@1: mir->cut_vec->val[j] = - mir->cut_vec->val[j]; alpar@1: } alpar@1: else alpar@1: xassert(k != k); alpar@1: } alpar@1: /* now restore bounds of continuous variables */ alpar@1: for (j = 1; j <= mir->cut_vec->nnz; j++) alpar@1: { k = mir->cut_vec->ind[j]; alpar@1: xassert(1 <= k && k <= m+n); alpar@1: if (mir->isint[k]) continue; /* skip integer */ alpar@1: if (mir->subst[k] == 'L') alpar@1: { /* x'[k] = x[k] - (lower bound) */ alpar@1: xassert(mir->lb[k] != -DBL_MAX); alpar@1: kk = mir->vlb[k]; alpar@1: if (kk == 0) alpar@1: { /* x'[k] = x[k] - lb[k] */ alpar@1: mir->cut_rhs += mir->cut_vec->val[j] * mir->lb[k]; alpar@1: } alpar@1: else alpar@1: { /* x'[k] = x[k] - lb[k] * x[kk] */ alpar@1: jj = mir->cut_vec->pos[kk]; alpar@1: #if 0 alpar@1: xassert(jj != 0); alpar@1: #else alpar@1: if (jj == 0) alpar@1: { ios_set_vj(mir->cut_vec, kk, 1.0); alpar@1: jj = mir->cut_vec->pos[kk]; alpar@1: xassert(jj != 0); alpar@1: mir->cut_vec->val[jj] = 0.0; alpar@1: } alpar@1: #endif alpar@1: mir->cut_vec->val[jj] -= mir->cut_vec->val[j] * alpar@1: mir->lb[k]; alpar@1: } alpar@1: } alpar@1: else if (mir->subst[k] == 'U') alpar@1: { /* x'[k] = (upper bound) - x[k] */ alpar@1: xassert(mir->ub[k] != +DBL_MAX); alpar@1: kk = mir->vub[k]; alpar@1: if (kk == 0) alpar@1: { /* x'[k] = ub[k] - x[k] */ alpar@1: mir->cut_rhs -= mir->cut_vec->val[j] * mir->ub[k]; alpar@1: } alpar@1: else alpar@1: { /* x'[k] = ub[k] * x[kk] - x[k] */ alpar@1: jj = mir->cut_vec->pos[kk]; alpar@1: if (jj == 0) alpar@1: { ios_set_vj(mir->cut_vec, kk, 1.0); alpar@1: jj = mir->cut_vec->pos[kk]; alpar@1: xassert(jj != 0); alpar@1: mir->cut_vec->val[jj] = 0.0; alpar@1: } alpar@1: mir->cut_vec->val[jj] += mir->cut_vec->val[j] * alpar@1: mir->ub[k]; alpar@1: } alpar@1: mir->cut_vec->val[j] = - mir->cut_vec->val[j]; alpar@1: } alpar@1: else alpar@1: xassert(k != k); alpar@1: } alpar@1: #if _MIR_DEBUG alpar@1: ios_check_vec(mir->cut_vec); alpar@1: #endif alpar@1: return; alpar@1: } alpar@1: alpar@1: #if _MIR_DEBUG alpar@1: static void check_cut_row(struct MIR *mir, double r_best) alpar@1: { /* check the cut after back bound substitution or elimination of alpar@1: auxiliary variables */ alpar@1: int m = mir->m; alpar@1: int n = mir->n; alpar@1: int j, k; alpar@1: double r, big; alpar@1: /* compute the residual r = sum a[k] * x[k] - b and determine alpar@1: big = max(1, |a[k]|, |b|) */ alpar@1: r = 0.0, big = 1.0; alpar@1: for (j = 1; j <= mir->cut_vec->nnz; j++) alpar@1: { k = mir->cut_vec->ind[j]; alpar@1: xassert(1 <= k && k <= m+n); alpar@1: r += mir->cut_vec->val[j] * mir->x[k]; alpar@1: if (big < fabs(mir->cut_vec->val[j])) alpar@1: big = fabs(mir->cut_vec->val[j]); alpar@1: } alpar@1: r -= mir->cut_rhs; alpar@1: if (big < fabs(mir->cut_rhs)) alpar@1: big = fabs(mir->cut_rhs); alpar@1: /* the residual must be close to r_best */ alpar@1: xassert(fabs(r - r_best) <= 1e-6 * big); alpar@1: return; alpar@1: } alpar@1: #endif alpar@1: alpar@1: static void subst_aux_vars(glp_tree *tree, struct MIR *mir) alpar@1: { /* final substitution to eliminate auxiliary variables */ alpar@1: glp_prob *mip = tree->mip; alpar@1: int m = mir->m; alpar@1: int n = mir->n; alpar@1: GLPAIJ *aij; alpar@1: int j, k, kk, jj; alpar@1: for (j = mir->cut_vec->nnz; j >= 1; j--) alpar@1: { k = mir->cut_vec->ind[j]; alpar@1: xassert(1 <= k && k <= m+n); alpar@1: if (k > m) continue; /* skip structurals */ alpar@1: for (aij = mip->row[k]->ptr; aij != NULL; aij = aij->r_next) alpar@1: { kk = m + aij->col->j; /* structural */ alpar@1: jj = mir->cut_vec->pos[kk]; alpar@1: if (jj == 0) alpar@1: { ios_set_vj(mir->cut_vec, kk, 1.0); alpar@1: jj = mir->cut_vec->pos[kk]; alpar@1: mir->cut_vec->val[jj] = 0.0; alpar@1: } alpar@1: mir->cut_vec->val[jj] += mir->cut_vec->val[j] * aij->val; alpar@1: } alpar@1: mir->cut_vec->val[j] = 0.0; alpar@1: } alpar@1: ios_clean_vec(mir->cut_vec, 0.0); alpar@1: return; alpar@1: } alpar@1: alpar@1: static void add_cut(glp_tree *tree, struct MIR *mir) alpar@1: { /* add constructed cut inequality to the cut pool */ alpar@1: int m = mir->m; alpar@1: int n = mir->n; alpar@1: int j, k, len; alpar@1: int *ind = xcalloc(1+n, sizeof(int)); alpar@1: double *val = xcalloc(1+n, sizeof(double)); alpar@1: len = 0; alpar@1: for (j = mir->cut_vec->nnz; j >= 1; j--) alpar@1: { k = mir->cut_vec->ind[j]; alpar@1: xassert(m+1 <= k && k <= m+n); alpar@1: len++, ind[len] = k - m, val[len] = mir->cut_vec->val[j]; alpar@1: } alpar@1: #if 0 alpar@1: ios_add_cut_row(tree, pool, GLP_RF_MIR, len, ind, val, GLP_UP, alpar@1: mir->cut_rhs); alpar@1: #else alpar@1: glp_ios_add_row(tree, NULL, GLP_RF_MIR, 0, len, ind, val, GLP_UP, alpar@1: mir->cut_rhs); alpar@1: #endif alpar@1: xfree(ind); alpar@1: xfree(val); alpar@1: return; alpar@1: } alpar@1: alpar@1: static int aggregate_row(glp_tree *tree, struct MIR *mir) alpar@1: { /* try to aggregate another row */ alpar@1: glp_prob *mip = tree->mip; alpar@1: int m = mir->m; alpar@1: int n = mir->n; alpar@1: GLPAIJ *aij; alpar@1: IOSVEC *v; alpar@1: int ii, j, jj, k, kk, kappa = 0, ret = 0; alpar@1: double d1, d2, d, d_max = 0.0; alpar@1: /* choose appropriate structural variable in the aggregated row alpar@1: to be substituted */ alpar@1: for (j = 1; j <= mir->agg_vec->nnz; j++) alpar@1: { k = mir->agg_vec->ind[j]; alpar@1: xassert(1 <= k && k <= m+n); alpar@1: if (k <= m) continue; /* skip auxiliary var */ alpar@1: if (mir->isint[k]) continue; /* skip integer var */ alpar@1: if (fabs(mir->agg_vec->val[j]) < 0.001) continue; alpar@1: /* compute distance from x[k] to its lower bound */ alpar@1: kk = mir->vlb[k]; alpar@1: if (kk == 0) alpar@1: { if (mir->lb[k] == -DBL_MAX) alpar@1: d1 = DBL_MAX; alpar@1: else alpar@1: d1 = mir->x[k] - mir->lb[k]; alpar@1: } alpar@1: else alpar@1: { xassert(1 <= kk && kk <= m+n); alpar@1: xassert(mir->isint[kk]); alpar@1: xassert(mir->lb[k] != -DBL_MAX); alpar@1: d1 = mir->x[k] - mir->lb[k] * mir->x[kk]; alpar@1: } alpar@1: /* compute distance from x[k] to its upper bound */ alpar@1: kk = mir->vub[k]; alpar@1: if (kk == 0) alpar@1: { if (mir->vub[k] == +DBL_MAX) alpar@1: d2 = DBL_MAX; alpar@1: else alpar@1: d2 = mir->ub[k] - mir->x[k]; alpar@1: } alpar@1: else alpar@1: { xassert(1 <= kk && kk <= m+n); alpar@1: xassert(mir->isint[kk]); alpar@1: xassert(mir->ub[k] != +DBL_MAX); alpar@1: d2 = mir->ub[k] * mir->x[kk] - mir->x[k]; alpar@1: } alpar@1: /* x[k] cannot be free */ alpar@1: xassert(d1 != DBL_MAX || d2 != DBL_MAX); alpar@1: /* d = min(d1, d2) */ alpar@1: d = (d1 <= d2 ? d1 : d2); alpar@1: xassert(d != DBL_MAX); alpar@1: /* should not be close to corresponding bound */ alpar@1: if (d < 0.001) continue; alpar@1: if (d_max < d) d_max = d, kappa = k; alpar@1: } alpar@1: if (kappa == 0) alpar@1: { /* nothing chosen */ alpar@1: ret = 1; alpar@1: goto done; alpar@1: } alpar@1: /* x[kappa] has been chosen */ alpar@1: xassert(m+1 <= kappa && kappa <= m+n); alpar@1: xassert(!mir->isint[kappa]); alpar@1: /* find another row, which have not been used yet, to eliminate alpar@1: x[kappa] from the aggregated row */ alpar@1: for (ii = 1; ii <= m; ii++) alpar@1: { if (mir->skip[ii]) continue; alpar@1: for (aij = mip->row[ii]->ptr; aij != NULL; aij = aij->r_next) alpar@1: if (aij->col->j == kappa - m) break; alpar@1: if (aij != NULL && fabs(aij->val) >= 0.001) break; alpar@1: } alpar@1: if (ii > m) alpar@1: { /* nothing found */ alpar@1: ret = 2; alpar@1: goto done; alpar@1: } alpar@1: /* row ii has been found; include it in the aggregated list */ alpar@1: mir->agg_cnt++; alpar@1: xassert(mir->agg_cnt <= MAXAGGR); alpar@1: mir->agg_row[mir->agg_cnt] = ii; alpar@1: mir->skip[ii] = 2; alpar@1: /* v := new row */ alpar@1: v = ios_create_vec(m+n); alpar@1: ios_set_vj(v, ii, 1.0); alpar@1: for (aij = mip->row[ii]->ptr; aij != NULL; aij = aij->r_next) alpar@1: ios_set_vj(v, m + aij->col->j, - aij->val); alpar@1: #if _MIR_DEBUG alpar@1: ios_check_vec(v); alpar@1: #endif alpar@1: /* perform gaussian elimination to remove x[kappa] */ alpar@1: j = mir->agg_vec->pos[kappa]; alpar@1: xassert(j != 0); alpar@1: jj = v->pos[kappa]; alpar@1: xassert(jj != 0); alpar@1: ios_linear_comb(mir->agg_vec, alpar@1: - mir->agg_vec->val[j] / v->val[jj], v); alpar@1: ios_delete_vec(v); alpar@1: ios_set_vj(mir->agg_vec, kappa, 0.0); alpar@1: #if _MIR_DEBUG alpar@1: ios_check_vec(mir->agg_vec); alpar@1: #endif alpar@1: done: return ret; alpar@1: } alpar@1: alpar@1: void ios_mir_gen(glp_tree *tree, void *gen) alpar@1: { /* main routine to generate MIR cuts */ alpar@1: glp_prob *mip = tree->mip; alpar@1: struct MIR *mir = gen; alpar@1: int m = mir->m; alpar@1: int n = mir->n; alpar@1: int i; alpar@1: double r_best; alpar@1: xassert(mip->m >= m); alpar@1: xassert(mip->n == n); alpar@1: /* obtain current point */ alpar@1: get_current_point(tree, mir); alpar@1: #if _MIR_DEBUG alpar@1: /* check current point */ alpar@1: check_current_point(mir); alpar@1: #endif alpar@1: /* reset bound substitution flags */ alpar@1: memset(&mir->subst[1], '?', m+n); alpar@1: /* try to generate a set of violated MIR cuts */ alpar@1: for (i = 1; i <= m; i++) alpar@1: { if (mir->skip[i]) continue; alpar@1: /* use original i-th row as initial aggregated constraint */ alpar@1: initial_agg_row(tree, mir, i); alpar@1: loop: ; alpar@1: #if _MIR_DEBUG alpar@1: /* check aggregated row */ alpar@1: check_agg_row(mir); alpar@1: #endif alpar@1: /* substitute fixed variables into aggregated constraint */ alpar@1: subst_fixed_vars(mir); alpar@1: #if _MIR_DEBUG alpar@1: /* check aggregated row */ alpar@1: check_agg_row(mir); alpar@1: #endif alpar@1: #if _MIR_DEBUG alpar@1: /* check bound substitution flags */ alpar@1: { int k; alpar@1: for (k = 1; k <= m+n; k++) alpar@1: xassert(mir->subst[k] == '?'); alpar@1: } alpar@1: #endif alpar@1: /* apply bound substitution heuristic */ alpar@1: bound_subst_heur(mir); alpar@1: /* substitute bounds and build modified constraint */ alpar@1: build_mod_row(mir); alpar@1: #if _MIR_DEBUG alpar@1: /* check modified row */ alpar@1: check_mod_row(mir); alpar@1: #endif alpar@1: /* try to generate violated c-MIR cut for modified row */ alpar@1: r_best = generate(mir); alpar@1: if (r_best > 0.0) alpar@1: { /* success */ alpar@1: #if _MIR_DEBUG alpar@1: /* check raw cut before back bound substitution */ alpar@1: check_raw_cut(mir, r_best); alpar@1: #endif alpar@1: /* back substitution of original bounds */ alpar@1: back_subst(mir); alpar@1: #if _MIR_DEBUG alpar@1: /* check the cut after back bound substitution */ alpar@1: check_cut_row(mir, r_best); alpar@1: #endif alpar@1: /* final substitution to eliminate auxiliary variables */ alpar@1: subst_aux_vars(tree, mir); alpar@1: #if _MIR_DEBUG alpar@1: /* check the cut after elimination of auxiliaries */ alpar@1: check_cut_row(mir, r_best); alpar@1: #endif alpar@1: /* add constructed cut inequality to the cut pool */ alpar@1: add_cut(tree, mir); alpar@1: } alpar@1: /* reset bound substitution flags */ alpar@1: { int j, k; alpar@1: for (j = 1; j <= mir->mod_vec->nnz; j++) alpar@1: { k = mir->mod_vec->ind[j]; alpar@1: xassert(1 <= k && k <= m+n); alpar@1: xassert(mir->subst[k] != '?'); alpar@1: mir->subst[k] = '?'; alpar@1: } alpar@1: } alpar@1: if (r_best == 0.0) alpar@1: { /* failure */ alpar@1: if (mir->agg_cnt < MAXAGGR) alpar@1: { /* try to aggregate another row */ alpar@1: if (aggregate_row(tree, mir) == 0) goto loop; alpar@1: } alpar@1: } alpar@1: /* unmark rows used in the aggregated constraint */ alpar@1: { int k, ii; alpar@1: for (k = 1; k <= mir->agg_cnt; k++) alpar@1: { ii = mir->agg_row[k]; alpar@1: xassert(1 <= ii && ii <= m); alpar@1: xassert(mir->skip[ii] == 2); alpar@1: mir->skip[ii] = 0; alpar@1: } alpar@1: } alpar@1: } alpar@1: return; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * NAME alpar@1: * alpar@1: * ios_mir_term - terminate MIR cut generator alpar@1: * alpar@1: * SYNOPSIS alpar@1: * alpar@1: * #include "glpios.h" alpar@1: * void ios_mir_term(void *gen); alpar@1: * alpar@1: * DESCRIPTION alpar@1: * alpar@1: * The routine ios_mir_term deletes the MIR cut generator working area alpar@1: * freeing all the memory allocated to it. */ alpar@1: alpar@1: void ios_mir_term(void *gen) alpar@1: { struct MIR *mir = gen; alpar@1: xfree(mir->skip); alpar@1: xfree(mir->isint); alpar@1: xfree(mir->lb); alpar@1: xfree(mir->vlb); alpar@1: xfree(mir->ub); alpar@1: xfree(mir->vub); alpar@1: xfree(mir->x); alpar@1: xfree(mir->agg_row); alpar@1: ios_delete_vec(mir->agg_vec); alpar@1: xfree(mir->subst); alpar@1: ios_delete_vec(mir->mod_vec); alpar@1: ios_delete_vec(mir->cut_vec); alpar@1: xfree(mir); alpar@1: return; alpar@1: } alpar@1: alpar@1: /* eof */