// Copyright (C) 2002, International Business Machines
// Corporation and others. All Rights Reserved.
#include <stdio.h>
#include <math.h>
#include "CoinPresolveMatrix.hpp"
#include "CoinPresolveFixed.hpp"
#include "CoinHelperFunctions.hpp"
#if PRESOLVE_DEBUG || PRESOLVE_CONSISTENCY
#include "CoinPresolvePsdebug.hpp"
#endif
/* Begin routines associated with remove_fixed_action */
const char *remove_fixed_action::name() const
{
return ("remove_fixed_action");
}
/*
* Original comment:
*
* invariant: both reps are loosely packed.
* coefficients of both reps remain consistent.
*
* Note that this concerns variables whose column bounds determine that
* they are slack; this does NOT concern singleton row constraints that
* determine that the relevant variable is slack.
*
* Invariant: col and row rep are consistent
*/
/*
This routine empties the columns for the list of fixed variables passed in
(fcols, nfcols). As each coefficient a<ij> is set to 0, rlo<i> and rup<i>
are adjusted accordingly. Note, however, that c<j> is not considered to be
removed from the objective until column j is physically removed from the
matrix (drop_empty_cols_action), so the correction to the objective is
adjusted there.
If a column solution is available, row activity (acts_) is adjusted.
remove_fixed_action implicitly assumes that the value of the variable has
already been forced within bounds. If this isn't true, the correction to
acts_ will be wrong. See make_fixed_action if you need to force the value
within bounds first.
*/
const remove_fixed_action*
remove_fixed_action::presolve (CoinPresolveMatrix *prob,
int *fcols,
int nfcols,
const CoinPresolveAction *next)
{
double *colels = prob->colels_;
int *hrow = prob->hrow_;
CoinBigIndex *mcstrt = prob->mcstrt_;
int *hincol = prob->hincol_;
double *rowels = prob->rowels_;
int *hcol = prob->hcol_;
CoinBigIndex *mrstrt = prob->mrstrt_;
int *hinrow = prob->hinrow_;
double *clo = prob->clo_;
double *rlo = prob->rlo_;
double *rup = prob->rup_;
double *sol = prob->sol_;
double *acts = prob->acts_;
presolvehlink *clink = prob->clink_;
presolvehlink *rlink = prob->rlink_;
action *actions = new action[nfcols+1];
/*
Scan columns to be removed and total up the number of coefficients.
*/
int estsize=0;
int ckc;
for (ckc = 0 ; ckc < nfcols ; ckc++) {
int j = fcols[ckc];
estsize += hincol[j];
}
// Allocate arrays to hold coefficients and associated row indices
double * els_action = new double[estsize];
int * rows_action = new int[estsize];
int actsize=0;
// faster to do all deletes in row copy at once
int nrows = prob->nrows_;
CoinBigIndex * rstrt = new int[nrows+1];
CoinZeroN(rstrt,nrows);
/*
Open a loop to excise each column a<j>. The first thing to do is load the
action entry with the index j, the value of x<j>, and the number of
entries in a<j>. After we walk the column and tweak the row-major
representation, we'll simply claim this column is empty by setting
hincol[j] = 0.
*/
for (ckc = 0 ; ckc < nfcols ; ckc++) {
int j = fcols[ckc];
double solj = clo[j];
CoinBigIndex kcs = mcstrt[j];
CoinBigIndex kce = kcs + hincol[j];
CoinBigIndex k;
{ action &f = actions[ckc];
f.col = j;
f.sol = solj;
f.start = actsize;
}
/*
Now walk a<j>. For each row i with a coefficient a<ij> != 0:
* save the coefficient and row index,
* substitute the value of x<j>, adjusting the row bounds and lhs value
accordingly, then
* delete a<ij> from the row-major representation.
* Finally: mark the row as changed and add it to the list of rows to be
processed next. Then, for each remaining column in the row, do the same.
(It makes sense to put the columns on the `to be processed' list, but
I'm wondering about the wisdom of marking them as changed.
-- lh, 040824 -- )
*/
for (k = kcs ; k < kce ; k++) {
int row = hrow[k];
double coeff = colels[k];
els_action[actsize]=coeff;
rstrt[row]++; // increase counts
rows_action[actsize++]=row;
// Avoid reducing finite infinity.
if (-PRESOLVE_INF < rlo[row])
rlo[row] -= solj*coeff;
if (rup[row] < PRESOLVE_INF)
rup[row] -= solj*coeff;
if (sol) {
acts[row] -= solj*coeff;
}
#define TRY2
#ifndef TRY2
presolve_delete_from_row(row,j,mrstrt,hinrow,hcol,rowels);
if (hinrow[row] == 0)
{ PRESOLVE_REMOVE_LINK(rlink,row) ; }
// mark unless already marked
if (!prob->rowChanged(row)) {
prob->addRow(row);
CoinBigIndex krs = mrstrt[row];
CoinBigIndex kre = krs + hinrow[row];
for (CoinBigIndex k=krs; k<kre; k++) {
int jcol = hcol[k];
prob->addCol(jcol);
}
}
#endif
}
/*
Remove the column's link from the linked list of columns, and declare
it empty in the column-major representation. Link removal must execute
even if the column is already of length 0 when it arrives.
*/
PRESOLVE_REMOVE_LINK(clink, j);
hincol[j] = 0;
}
/*
Set the actual end of the coefficient and row index arrays.
*/
actions[nfcols].start=actsize;
# if PRESOLVE_SUMMARY
printf("NFIXED: %d", nfcols);
if (estsize-actsize > 0)
{ printf(", overalloc %d",estsize-actsize) ; }
printf("\n") ;
# endif
// Now get columns by row
int * column = new int[actsize];
int nel=0;
int iRow;
for (iRow=0;iRow<nrows;iRow++) {
int n=rstrt[iRow];
rstrt[iRow]=nel;
nel += n;
}
rstrt[nrows]=nel;
for (ckc = 0 ; ckc < nfcols ; ckc++) {
int kcs = actions[ckc].start;
int j=actions[ckc].col;
int kce;
if (ckc<nfcols-1)
kce = actions[ckc+1].start;
else
kce = actsize;
for (int k=kcs;k<kce;k++) {
int iRow = rows_action[k];
CoinBigIndex put = rstrt[iRow];
rstrt[iRow]++;
column[put]=j;
}
}
// Now do rows
int ncols = prob->ncols_;
char * mark = new char[ncols];
memset(mark,0,ncols);
// rstrts are now one out i.e. rstrt[0] is end of row 0
nel=0;
#ifdef TRY2
for (iRow=0;iRow<nrows;iRow++) {
int k;
for (k=nel;k<rstrt[iRow];k++) {
mark[column[k]]=1;
}
presolve_delete_many_from_major(iRow,mark,mrstrt,hinrow,hcol,rowels);
#ifndef NDEBUG
for (k=nel;k<rstrt[iRow];k++) {
assert(mark[column[k]]==0);
}
#endif
if (hinrow[iRow] == 0)
{
PRESOLVE_REMOVE_LINK(rlink,iRow) ;
}
// mark unless already marked
if (!prob->rowChanged(iRow)) {
prob->addRow(iRow);
CoinBigIndex krs = mrstrt[iRow];
CoinBigIndex kre = krs + hinrow[iRow];
for (CoinBigIndex k=krs; k<kre; k++) {
int jcol = hcol[k];
prob->addCol(jcol);
}
}
nel=rstrt[iRow];
}
#endif
delete [] mark;
delete [] column;
delete [] rstrt;
/*
Create the postsolve object, link it at the head of the list of postsolve
objects, and return a pointer.
*/
return (new remove_fixed_action(nfcols,actions,
els_action,rows_action,next));
}
remove_fixed_action::remove_fixed_action(int nactions,
action *actions,
double * els_action,
int * rows_action,
const CoinPresolveAction *next) :
CoinPresolveAction(next),
colrows_(rows_action),
colels_(els_action),
nactions_(nactions),
actions_(actions)
{
}
remove_fixed_action::~remove_fixed_action()
{
deleteAction(actions_,action*);
delete [] colels_;
delete [] colrows_;
}
/*
* Say we determined that cup - clo <= ztolzb, so we fixed sol at clo.
* This involved subtracting clo*coeff from ub/lb for each row the
* variable occurred in.
* Now when we put the variable back in, by construction the variable
* is within tolerance, the non-slacks are unchanged, and the
* distances of the affected slacks from their bounds should remain
* unchanged (ignoring roundoff errors).
* It may be that by adding the term back in, the affected constraints
* now aren't as accurate due to round-off errors; this could happen
* if only one summand and the slack in the original formulation were large
* (and naturally had opposite signs), and the new term in the constraint
* is about the size of the old slack, so the new slack becomes about 0.
* It may be that there is catastrophic cancellation in the summation,
* so it might not compute to 0.
*/
void remove_fixed_action::postsolve(CoinPostsolveMatrix *prob) const
{
action * actions = actions_;
const int nactions = nactions_;
double *colels = prob->colels_;
int *hrow = prob->hrow_;
CoinBigIndex *mcstrt = prob->mcstrt_;
int *hincol = prob->hincol_;
int *link = prob->link_;
CoinBigIndex &free_list = prob->free_list_;
double *clo = prob->clo_;
double *cup = prob->cup_;
double *rlo = prob->rlo_;
double *rup = prob->rup_;
double *sol = prob->sol_;
double *dcost = prob->cost_;
double *rcosts = prob->rcosts_;
double *acts = prob->acts_;
double *rowduals = prob->rowduals_;
unsigned char *colstat = prob->colstat_;
const double maxmin = prob->maxmin_;
# if PRESOLVE_DEBUG || PRESOLVE_CONSISTENCY
char *cdone = prob->cdone_;
# endif
double * els_action = colels_;
int * rows_action = colrows_;
int end = actions[nactions].start;
/*
At one point, it turned out that forcing_constraint_action was putting
duplicates in the column list it passed to remove_fixed_action. This is now
fixed, but ... it looks to me like we could be in trouble here if we
reinstate a column multiple times. Hence the assert.
*/
for (const action *f = &actions[nactions-1]; actions<=f; f--) {
int icol = f->col;
const double thesol = f->sol;
# if PRESOLVE_DEBUG || PRESOLVE_CONSISTENCY
assert(cdone[icol] != FIXED_VARIABLE) ;
cdone[icol] = FIXED_VARIABLE ;
# endif
sol[icol] = thesol;
clo[icol] = thesol;
cup[icol] = thesol;
int cs = NO_LINK ;
int start = f->start;
double dj = maxmin * dcost[icol];
for (int i=start; i<end; ++i) {
int row = rows_action[i];
double coeff =els_action[i];
// pop free_list
CoinBigIndex k = free_list;
assert(k >= 0 && k < prob->bulk0_) ;
free_list = link[free_list];
// restore
hrow[k] = row;
colels[k] = coeff;
link[k] = cs;
cs = k;
if (-PRESOLVE_INF < rlo[row])
rlo[row] += coeff * thesol;
if (rup[row] < PRESOLVE_INF)
rup[row] += coeff * thesol;
acts[row] += coeff * thesol;
dj -= rowduals[row] * coeff;
}
# if PRESOLVE_CONSISTENCY
presolve_check_free_list(prob) ;
# endif
mcstrt[icol] = cs;
rcosts[icol] = dj;
hincol[icol] = end-start;
end=start;
/* Original comment:
* the bounds in the reduced problem were tightened.
* that means that this variable may not have been basic
* because it didn't have to be,
* but now it may have to.
* no - the bounds aren't changed by this operation
*/
/*
We've reintroduced the variable, but it's still fixed (equal bounds).
Pick the nonbasic status that agrees with the reduced cost. Later, if
postsolve unfixes the variable, we'll need to confirm that this status is
still viable. We live in a minimisation world here.
*/
if (colstat)
{ if (dj < 0)
prob->setColumnStatus(icol,CoinPrePostsolveMatrix::atUpperBound);
else
prob->setColumnStatus(icol,CoinPrePostsolveMatrix::atLowerBound); }
}
return ;
}
/*
Scan the problem for variables that are already fixed, and remove them.
There's an implicit assumption that the value of the variable is already
within bounds. If you want to protect against this possibility, you want to
use make_fixed.
*/
const CoinPresolveAction *remove_fixed (CoinPresolveMatrix *prob,
const CoinPresolveAction *next)
{
int ncols = prob->ncols_;
int *fcols = new int[ncols];
int nfcols = 0;
int *hincol = prob->hincol_;
double *clo = prob->clo_;
double *cup = prob->cup_;
for (int i = 0 ; i < ncols ; i++)
if (hincol[i] > 0 && clo[i] == cup[i]&&!prob->colProhibited2(i))
fcols[nfcols++] = i;
if (nfcols > 0)
{ next = remove_fixed_action::presolve(prob, fcols, nfcols, next) ; }
delete[]fcols;
return (next);
}
/* End routines associated with remove_fixed_action */
/* Begin routines associated with make_fixed_action */
const char *make_fixed_action::name() const
{
return ("make_fixed_action");
}
/*
This routine does the actual job of fixing one or more variables. The set
of indices to be fixed is specified by nfcols and fcols. fix_to_lower
specifies the bound where the variable(s) should be fixed. The other bound
is preserved as part of the action and the bounds are set equal. Note that
you don't get to specify the bound on a per-variable basis.
If a primal solution is available, make_fixed_action will adjust the the
row activity to compensate for forcing the variable within bounds. If the
bounds are already equal, and the variable is within bounds, you should
consider remove_fixed_action.
*/
const CoinPresolveAction*
make_fixed_action::presolve (CoinPresolveMatrix *prob,
int *fcols, int nfcols,
bool fix_to_lower,
const CoinPresolveAction *next)
{ double *clo = prob->clo_;
double *cup = prob->cup_;
double *csol = prob->sol_;
double *colels = prob->colels_;
int *hrow = prob->hrow_;
CoinBigIndex *mcstrt = prob->mcstrt_;
int *hincol = prob->hincol_;
double *acts = prob->acts_;
/*
Shouldn't happen, but ...
*/
if (nfcols <= 0) return (next) ;
action *actions = new action[nfcols];
/*
Scan the set of indices specifying variables to be fixed. For each variable,
stash the unused bound in the action and set the bounds equal. If the client
has passed in a primal solution, update it if the value of the variable
changes.
*/
for (int ckc = 0 ; ckc < nfcols ; ckc++)
{ int j = fcols[ckc] ;
double movement = 0 ;
action &f = actions[ckc] ;
f.col = j ;
if (fix_to_lower) {
f.bound = cup[j];
cup[j] = clo[j];
if (csol) {
movement = clo[j]-csol[j] ;
csol[j] = clo[j] ;
}
} else {
f.bound = clo[j];
clo[j] = cup[j];
if (csol) {
movement = cup[j]-csol[j];
csol[j] = cup[j];
}
}
if (movement) {
CoinBigIndex k;
for (k = mcstrt[j] ; k < mcstrt[j]+hincol[j] ; k++) {
int row = hrow[k];
acts[row] += movement*colels[k];
}
}
}
/*
Original comment:
This is unusual in that the make_fixed_action transform
contains within it a remove_fixed_action transform
bad idea?
Explanatory comment:
Now that we've adjusted the bounds, time to create the postsolve action
that will restore the original bounds. But wait! We're not done. By calling
remove_fixed_action::presolve, we will remove these variables from the
model, caching the postsolve transform that will restore them inside the
postsolve transform for fixing the bounds.
*/
if (nfcols > 0)
{ next = new make_fixed_action(nfcols, actions, fix_to_lower,
remove_fixed_action::presolve(prob,fcols, nfcols,0),
next) ; }
return (next) ;
}
/*
Recall that in presolve, make_fixed_action forced a bound to fix a variable,
then called remove_fixed_action to empty the column. removed_fixed_action
left a postsolve object hanging off faction_, and our first act here is to
call r_f_a::postsolve to repopulate the columns. The m_f_a postsolve activity
consists of relaxing one of the bounds and making sure that the status is
still viable (we can potentially eliminate the bound here).
*/
void make_fixed_action::postsolve(CoinPostsolveMatrix *prob) const
{
const action *const actions = actions_;
const int nactions = nactions_;
const bool fix_to_lower = fix_to_lower_;
double *clo = prob->clo_;
double *cup = prob->cup_;
double *sol = prob->sol_ ;
unsigned char *colstat = prob->colstat_;
/*
Repopulate the columns.
*/
assert(nactions == faction_->nactions_) ;
faction_->postsolve(prob);
/*
Walk the actions: restore each bound and check that the status is still
appropriate. Given that we're unfixing a fixed variable, it's safe to assume
that the unaffected bound is finite.
*/
for (int cnt = nactions-1 ; cnt >= 0 ; cnt--)
{ const action *f = &actions[cnt];
int icol = f->col;
double xj = sol[icol] ;
assert(faction_->actions_[cnt].col == icol) ;
if (fix_to_lower)
{ double ub = f->bound ;
cup[icol] = ub ;
if (colstat)
{ if (ub >= PRESOLVE_INF || xj != ub)
{ prob->setColumnStatus(icol,
CoinPrePostsolveMatrix::atLowerBound) ; } } }
else
{ double lb = f->bound ;
clo[icol] = lb ;
if (colstat)
{ if (lb <= -PRESOLVE_INF || xj != lb)
{ prob->setColumnStatus(icol,
CoinPrePostsolveMatrix::atUpperBound) ; } } } }
return ; }
/*!
Scan the columns and collect indices of columns that have upper and lower
bounds within the zero tolerance of one another. Hand this list to
make_fixed_action::presolve() to do the heavy lifting.
make_fixed_action will compensate for variables which are infeasible, forcing
them to feasibility and correcting the row activity, before invoking
remove_fixed_action to remove the variable from the problem. If you're
confident of feasibility, consider remove_fixed.
*/
const CoinPresolveAction *make_fixed (CoinPresolveMatrix *prob,
const CoinPresolveAction *next)
{
int ncols = prob->ncols_;
int *fcols = new int[ncols];
int nfcols = 0;
int *hincol = prob->hincol_;
double *clo = prob->clo_;
double *cup = prob->cup_;
for (int i = 0 ; i < ncols ; i++)
{ if (hincol[i] > 0 &&
fabs(cup[i] - clo[i]) < ZTOLDP && !prob->colProhibited2(i))
{ fcols[nfcols++] = i ; } }
/*
Call m_f_a::presolve to do the heavy lifting. This will create a new
CoinPresolveAction, which will become the head of the list of
CoinPresolveAction's currently pointed to by next.
*/
next = make_fixed_action::presolve(prob,fcols,nfcols,true,next) ;
delete[]fcols ;
return (next) ; }
// Transfers costs
void
transferCosts(CoinPresolveMatrix * prob)
{
double *colels = prob->colels_;
int *hrow = prob->hrow_;
CoinBigIndex *mcstrt = prob->mcstrt_;
int *hincol = prob->hincol_;
double *rowels = prob->rowels_;
int *hcol = prob->hcol_;
CoinBigIndex *mrstrt = prob->mrstrt_;
int *hinrow = prob->hinrow_;
double *rlo = prob->rlo_;
double *rup = prob->rup_;
double *clo = prob->clo_;
double *cup = prob->cup_;
int ncols = prob->ncols_;
double *dcost = prob->cost_;
unsigned char * integerType = prob->integerType_;
double bias = prob->dobias_;
int icol;
int numberIntegers=0;
for (icol=0;icol<ncols;icol++) {
if (integerType[icol])
numberIntegers++;
}
int nchanged=0;
for (icol=0;icol<ncols;icol++) {
if (dcost[icol]&&hincol[icol]==1&&cup[icol]>clo[icol]) {
int irow=hrow[mcstrt[icol]];
if (rlo[irow]==rup[irow]) {
// transfer costs so can be made slack
double ratio = dcost[icol]/colels[mcstrt[icol]];
bias += rlo[irow]*ratio;
for (CoinBigIndex j=mrstrt[irow];j<mrstrt[irow]+hinrow[irow];j++) {
int jcol = hcol[j];
double value = rowels[j];
dcost[jcol] -= ratio*value;
}
dcost[icol]=0.0;
nchanged++;
}
}
}
if (nchanged)
printf("%d singleton columns have transferred costs\n",nchanged);
if (numberIntegers) {
int changed=-1;
while (changed) {
changed=0;
for (icol=0;icol<ncols;icol++) {
if (dcost[icol]&&cup[icol]>clo[icol]) {
for (CoinBigIndex k=mcstrt[icol];k<mcstrt[icol]+hincol[icol];k++) {
int irow=hrow[k];
if (rlo[irow]==rup[irow]) {
// See if can give more integer variables costs
CoinBigIndex j;
int nNow = integerType[icol] ? 1 : 0;
int nThen=0;
for (j=mrstrt[irow];j<mrstrt[irow]+hinrow[irow];j++) {
int jcol = hcol[j];
if (!dcost[jcol]&&integerType[jcol])
nThen++;
}
if (nThen>nNow) {
// transfer costs so can be made slack
double ratio = dcost[icol]/colels[mcstrt[icol]];
bias += rlo[irow]*ratio;
for (j=mrstrt[irow];j<mrstrt[irow]+hinrow[irow];j++) {
int jcol = hcol[j];
double value = rowels[j];
dcost[jcol] -= ratio*value;
}
dcost[icol]=0.0;
changed++;
break;
}
}
}
}
}
if (changed) {
nchanged+=changed;
printf("%d changed this pass\n",changed);
}
}
}
if (bias!=prob->dobias_)
printf("new bias %g\n",bias);
prob->dobias_ = bias;
}
syntax highlighted by Code2HTML, v. 0.9.1