limp-cbc-0.3.2.0: cbits/coin/CbcSolverExpandKnapsack.cpp
/* $Id: CbcSolverExpandKnapsack.cpp 1902 2013-04-10 16:58:16Z stefan $ */
// Copyright (C) 2007, International Business Machines
// Corporation and others. All Rights Reserved.
// This code is licensed under the terms of the Eclipse Public License (EPL).
/*! \file CbcSolverExpandKnapsack.cpp
Returns OsiSolverInterface (User should delete)
On entry numberKnapsack is maximum number of Total entries
Expanding possibilities of x*y, where x*y are both integers, constructing
a knapsack constraint. Results in a tighter model.
*/
#include "CbcConfig.h"
#include "CoinPragma.hpp"
#include "OsiSolverInterface.hpp"
#include "CglStored.hpp"
#ifndef COIN_HAS_LINK
#define COIN_HAS_LINK
#endif
#ifdef COIN_HAS_LINK
#include "CbcLinked.hpp"
#endif
#ifdef COIN_HAS_LINK
OsiSolverInterface *
expandKnapsack(CoinModel & model, int * whichColumn, int * knapsackStart,
int * knapsackRow, int &numberKnapsack,
CglStored & stored, int logLevel,
int fixedPriority, int SOSPriority, CoinModel & tightenedModel)
{
int maxTotal = numberKnapsack;
// load from coin model
OsiSolverLink *si = new OsiSolverLink();
OsiSolverInterface * finalModel = NULL;
si->setDefaultMeshSize(0.001);
// need some relative granularity
si->setDefaultBound(100.0);
si->setDefaultMeshSize(0.01);
si->setDefaultBound(100000.0);
si->setIntegerPriority(1000);
si->setBiLinearPriority(10000);
si->load(model, true, logLevel);
// get priorities
const int * priorities = model.priorities();
int numberColumns = model.numberColumns();
if (priorities) {
OsiObject ** objects = si->objects();
int numberObjects = si->numberObjects();
for (int iObj = 0; iObj < numberObjects; iObj++) {
int iColumn = objects[iObj]->columnNumber();
if (iColumn >= 0 && iColumn < numberColumns) {
#ifndef NDEBUG
OsiSimpleInteger * obj =
dynamic_cast <OsiSimpleInteger *>(objects[iObj]) ;
#endif
assert (obj);
int iPriority = priorities[iColumn];
if (iPriority > 0)
objects[iObj]->setPriority(iPriority);
}
}
if (fixedPriority > 0) {
si->setFixedPriority(fixedPriority);
}
if (SOSPriority < 0)
SOSPriority = 100000;
}
CoinModel coinModel = *si->coinModel();
assert(coinModel.numberRows() > 0);
tightenedModel = coinModel;
int numberRows = coinModel.numberRows();
// Mark variables
int * whichKnapsack = new int [numberColumns];
int iRow, iColumn;
for (iColumn = 0; iColumn < numberColumns; iColumn++)
whichKnapsack[iColumn] = -1;
int kRow;
bool badModel = false;
// analyze
if (logLevel > 1) {
for (iRow = 0; iRow < numberRows; iRow++) {
/* Just obvious one at first
positive non unit coefficients
all integer
positive rowUpper
for now - linear (but further down in code may use nonlinear)
column bounds should be tight
*/
//double lower = coinModel.getRowLower(iRow);
double upper = coinModel.getRowUpper(iRow);
if (upper < 1.0e10) {
CoinModelLink triple = coinModel.firstInRow(iRow);
bool possible = true;
int n = 0;
int n1 = 0;
while (triple.column() >= 0) {
int iColumn = triple.column();
const char * el = coinModel.getElementAsString(iRow, iColumn);
if (!strcmp("Numeric", el)) {
if (coinModel.columnLower(iColumn) == coinModel.columnUpper(iColumn)) {
triple = coinModel.next(triple);
continue; // fixed
}
double value = coinModel.getElement(iRow, iColumn);
if (value < 0.0) {
possible = false;
} else {
n++;
if (value == 1.0)
n1++;
if (coinModel.columnLower(iColumn) < 0.0)
possible = false;
if (!coinModel.isInteger(iColumn))
possible = false;
if (whichKnapsack[iColumn] >= 0)
possible = false;
}
} else {
possible = false; // non linear
}
triple = coinModel.next(triple);
}
if (n - n1 > 1 && possible) {
double lower = coinModel.getRowLower(iRow);
double upper = coinModel.getRowUpper(iRow);
CoinModelLink triple = coinModel.firstInRow(iRow);
while (triple.column() >= 0) {
int iColumn = triple.column();
lower -= coinModel.columnLower(iColumn) * triple.value();
upper -= coinModel.columnLower(iColumn) * triple.value();
triple = coinModel.next(triple);
}
printf("%d is possible %g <=", iRow, lower);
// print
triple = coinModel.firstInRow(iRow);
while (triple.column() >= 0) {
int iColumn = triple.column();
if (coinModel.columnLower(iColumn) != coinModel.columnUpper(iColumn))
printf(" (%d,el %g up %g)", iColumn, triple.value(),
coinModel.columnUpper(iColumn) - coinModel.columnLower(iColumn));
triple = coinModel.next(triple);
}
printf(" <= %g\n", upper);
}
}
}
}
numberKnapsack = 0;
for (kRow = 0; kRow < numberRows; kRow++) {
iRow = kRow;
/* Just obvious one at first
positive non unit coefficients
all integer
positive rowUpper
for now - linear (but further down in code may use nonlinear)
column bounds should be tight
*/
//double lower = coinModel.getRowLower(iRow);
double upper = coinModel.getRowUpper(iRow);
if (upper < 1.0e10) {
CoinModelLink triple = coinModel.firstInRow(iRow);
bool possible = true;
int n = 0;
int n1 = 0;
while (triple.column() >= 0) {
int iColumn = triple.column();
const char * el = coinModel.getElementAsString(iRow, iColumn);
if (!strcmp("Numeric", el)) {
if (coinModel.columnLower(iColumn) == coinModel.columnUpper(iColumn)) {
triple = coinModel.next(triple);
continue; // fixed
}
double value = coinModel.getElement(iRow, iColumn);
if (value < 0.0) {
possible = false;
} else {
n++;
if (value == 1.0)
n1++;
if (coinModel.columnLower(iColumn) < 0.0)
possible = false;
if (!coinModel.isInteger(iColumn))
possible = false;
if (whichKnapsack[iColumn] >= 0)
possible = false;
}
} else {
possible = false; // non linear
}
triple = coinModel.next(triple);
}
if (n - n1 > 1 && possible) {
// try
CoinModelLink triple = coinModel.firstInRow(iRow);
while (triple.column() >= 0) {
int iColumn = triple.column();
if (coinModel.columnLower(iColumn) != coinModel.columnUpper(iColumn))
whichKnapsack[iColumn] = numberKnapsack;
triple = coinModel.next(triple);
}
knapsackRow[numberKnapsack++] = iRow;
}
}
}
if (logLevel > 0)
printf("%d out of %d candidate rows are possible\n", numberKnapsack, numberRows);
// Check whether we can get rid of nonlinearities
/* mark rows
-2 in knapsack and other variables
-1 not involved
n only in knapsack n
*/
int * markRow = new int [numberRows];
for (iRow = 0; iRow < numberRows; iRow++)
markRow[iRow] = -1;
int canDo = 1; // OK and linear
for (iColumn = 0; iColumn < numberColumns; iColumn++) {
CoinModelLink triple = coinModel.firstInColumn(iColumn);
int iKnapsack = whichKnapsack[iColumn];
bool linear = true;
// See if quadratic objective
const char * expr = coinModel.getColumnObjectiveAsString(iColumn);
if (strcmp(expr, "Numeric")) {
linear = false;
}
while (triple.row() >= 0) {
int iRow = triple.row();
if (iKnapsack >= 0) {
if (markRow[iRow] == -1) {
markRow[iRow] = iKnapsack;
} else if (markRow[iRow] != iKnapsack) {
markRow[iRow] = -2;
}
}
const char * expr = coinModel.getElementAsString(iRow, iColumn);
if (strcmp(expr, "Numeric")) {
linear = false;
}
triple = coinModel.next(triple);
}
if (!linear) {
if (whichKnapsack[iColumn] < 0) {
canDo = 0;
break;
} else {
canDo = 2;
}
}
}
int * markKnapsack = NULL;
double * coefficient = NULL;
double * linear = NULL;
int * whichRow = NULL;
int * lookupRow = NULL;
badModel = (canDo == 0);
if (numberKnapsack && canDo) {
/* double check - OK if
no nonlinear
nonlinear only on columns in knapsack
nonlinear only on columns in knapsack * ONE other - same for all in knapsack
AND that is only row connected to knapsack
(theoretically could split knapsack if two other and small numbers)
also ONE could be ONE expression - not just a variable
*/
int iKnapsack;
markKnapsack = new int [numberKnapsack];
coefficient = new double [numberKnapsack];
linear = new double [numberColumns];
for (iKnapsack = 0; iKnapsack < numberKnapsack; iKnapsack++)
markKnapsack[iKnapsack] = -1;
if (canDo == 2) {
for (iRow = -1; iRow < numberRows; iRow++) {
int numberOdd;
CoinPackedMatrix * row = coinModel.quadraticRow(iRow, linear, numberOdd);
if (row) {
// see if valid
const double * element = row->getElements();
const int * column = row->getIndices();
const CoinBigIndex * columnStart = row->getVectorStarts();
const int * columnLength = row->getVectorLengths();
int numberLook = row->getNumCols();
for (int i = 0; i < numberLook; i++) {
int iKnapsack = whichKnapsack[i];
if (iKnapsack < 0) {
// might be able to swap - but for now can't have knapsack in
for (int j = columnStart[i]; j < columnStart[i] + columnLength[i]; j++) {
int iColumn = column[j];
if (whichKnapsack[iColumn] >= 0) {
canDo = 0; // no good
badModel = true;
break;
}
}
} else {
// OK if in same knapsack - or maybe just one
int marked = markKnapsack[iKnapsack];
for (int j = columnStart[i]; j < columnStart[i] + columnLength[i]; j++) {
int iColumn = column[j];
if (whichKnapsack[iColumn] != iKnapsack && whichKnapsack[iColumn] >= 0) {
canDo = 0; // no good
badModel = true;
break;
} else if (marked == -1) {
markKnapsack[iKnapsack] = iColumn;
marked = iColumn;
coefficient[iKnapsack] = element[j];
coinModel.associateElement(coinModel.columnName(iColumn), 1.0);
} else if (marked != iColumn) {
badModel = true;
canDo = 0; // no good
break;
} else {
// could manage with different coefficients - but for now ...
assert(coefficient[iKnapsack] == element[j]);
}
}
}
}
delete row;
}
}
}
if (canDo) {
// for any rows which are cuts
whichRow = new int [numberRows];
lookupRow = new int [numberRows];
bool someNonlinear = false;
double maxCoefficient = 1.0;
for (iKnapsack = 0; iKnapsack < numberKnapsack; iKnapsack++) {
if (markKnapsack[iKnapsack] >= 0) {
someNonlinear = true;
int iColumn = markKnapsack[iKnapsack];
maxCoefficient = CoinMax(maxCoefficient, fabs(coefficient[iKnapsack] * coinModel.columnUpper(iColumn)));
}
}
if (someNonlinear) {
// associate all columns to stop possible error messages
for (iColumn = 0; iColumn < numberColumns; iColumn++) {
coinModel.associateElement(coinModel.columnName(iColumn), 1.0);
}
}
ClpSimplex tempModel;
tempModel.loadProblem(coinModel);
// Create final model - first without knapsacks
int nCol = 0;
int nRow = 0;
for (iRow = 0; iRow < numberRows; iRow++) {
if (markRow[iRow] < 0) {
lookupRow[iRow] = nRow;
whichRow[nRow++] = iRow;
} else {
lookupRow[iRow] = -1;
}
}
for (iColumn = 0; iColumn < numberColumns; iColumn++) {
if (whichKnapsack[iColumn] < 0)
whichColumn[nCol++] = iColumn;
}
ClpSimplex finalModelX(&tempModel, nRow, whichRow, nCol, whichColumn, false, false, false);
OsiClpSolverInterface finalModelY(&finalModelX, true);
finalModel = finalModelY.clone();
finalModelY.releaseClp();
// Put back priorities
const int * priorities = model.priorities();
if (priorities) {
finalModel->findIntegers(false);
OsiObject ** objects = finalModel->objects();
int numberObjects = finalModel->numberObjects();
for (int iObj = 0; iObj < numberObjects; iObj++) {
int iColumn = objects[iObj]->columnNumber();
if (iColumn >= 0 && iColumn < nCol) {
#ifndef NDEBUG
OsiSimpleInteger * obj =
dynamic_cast <OsiSimpleInteger *>(objects[iObj]) ;
#endif
assert (obj);
int iPriority = priorities[whichColumn[iColumn]];
if (iPriority > 0)
objects[iObj]->setPriority(iPriority);
}
}
}
for (iRow = 0; iRow < numberRows; iRow++) {
whichRow[iRow] = iRow;
}
int numberOther = finalModel->getNumCols();
int nLargest = 0;
int nelLargest = 0;
int nTotal = 0;
for (iKnapsack = 0; iKnapsack < numberKnapsack; iKnapsack++) {
iRow = knapsackRow[iKnapsack];
int nCreate = maxTotal;
int nelCreate = coinModel.expandKnapsack(iRow, nCreate, NULL, NULL, NULL, NULL);
if (nelCreate < 0)
badModel = true;
nTotal += nCreate;
nLargest = CoinMax(nLargest, nCreate);
nelLargest = CoinMax(nelLargest, nelCreate);
}
if (nTotal > maxTotal)
badModel = true;
if (!badModel) {
// Now arrays for building
nelLargest = CoinMax(nelLargest, nLargest) + 1;
double * buildObj = new double [nLargest];
double * buildElement = new double [nelLargest];
int * buildStart = new int[nLargest+1];
int * buildRow = new int[nelLargest];
// alow for integers in knapsacks
OsiObject ** object = new OsiObject * [numberKnapsack+nTotal];
int nSOS = 0;
int nObj = numberKnapsack;
for (iKnapsack = 0; iKnapsack < numberKnapsack; iKnapsack++) {
knapsackStart[iKnapsack] = finalModel->getNumCols();
iRow = knapsackRow[iKnapsack];
int nCreate = 10000;
coinModel.expandKnapsack(iRow, nCreate, buildObj, buildStart, buildRow, buildElement);
// Redo row numbers
for (iColumn = 0; iColumn < nCreate; iColumn++) {
for (int j = buildStart[iColumn]; j < buildStart[iColumn+1]; j++) {
int jRow = buildRow[j];
jRow = lookupRow[jRow];
assert (jRow >= 0 && jRow < nRow);
buildRow[j] = jRow;
}
}
finalModel->addCols(nCreate, buildStart, buildRow, buildElement, NULL, NULL, buildObj);
int numberFinal = finalModel->getNumCols();
for (iColumn = numberOther; iColumn < numberFinal; iColumn++) {
if (markKnapsack[iKnapsack] < 0) {
finalModel->setColUpper(iColumn, maxCoefficient);
finalModel->setInteger(iColumn);
} else {
finalModel->setColUpper(iColumn, maxCoefficient + 1.0);
finalModel->setInteger(iColumn);
}
OsiSimpleInteger * sosObject = new OsiSimpleInteger(finalModel, iColumn);
sosObject->setPriority(1000000);
object[nObj++] = sosObject;
buildRow[iColumn-numberOther] = iColumn;
buildElement[iColumn-numberOther] = 1.0;
}
if (markKnapsack[iKnapsack] < 0) {
// convexity row
finalModel->addRow(numberFinal - numberOther, buildRow, buildElement, 1.0, 1.0);
} else {
int iColumn = markKnapsack[iKnapsack];
int n = numberFinal - numberOther;
buildRow[n] = iColumn;
buildElement[n++] = -fabs(coefficient[iKnapsack]);
// convexity row (sort of)
finalModel->addRow(n, buildRow, buildElement, 0.0, 0.0);
OsiSOS * sosObject = new OsiSOS(finalModel, n - 1, buildRow, NULL, 1);
sosObject->setPriority(iKnapsack + SOSPriority);
// Say not integral even if is (switch off heuristics)
sosObject->setIntegerValued(false);
object[nSOS++] = sosObject;
}
numberOther = numberFinal;
}
finalModel->addObjects(nObj, object);
for (iKnapsack = 0; iKnapsack < nObj; iKnapsack++)
delete object[iKnapsack];
delete [] object;
// Can we move any rows to cuts
const int * cutMarker = coinModel.cutMarker();
if (cutMarker && 0) {
printf("AMPL CUTS OFF until global cuts fixed\n");
cutMarker = NULL;
}
if (cutMarker) {
// Row copy
const CoinPackedMatrix * matrixByRow = finalModel->getMatrixByRow();
const double * elementByRow = matrixByRow->getElements();
const int * column = matrixByRow->getIndices();
const CoinBigIndex * rowStart = matrixByRow->getVectorStarts();
const int * rowLength = matrixByRow->getVectorLengths();
const double * rowLower = finalModel->getRowLower();
const double * rowUpper = finalModel->getRowUpper();
int nDelete = 0;
for (iRow = 0; iRow < numberRows; iRow++) {
if (cutMarker[iRow] && lookupRow[iRow] >= 0) {
int jRow = lookupRow[iRow];
whichRow[nDelete++] = jRow;
int start = rowStart[jRow];
stored.addCut(rowLower[jRow], rowUpper[jRow],
rowLength[jRow], column + start, elementByRow + start);
}
}
finalModel->deleteRows(nDelete, whichRow);
}
knapsackStart[numberKnapsack] = finalModel->getNumCols();
delete [] buildObj;
delete [] buildElement;
delete [] buildStart;
delete [] buildRow;
finalModel->writeMps("full");
}
}
}
delete [] whichKnapsack;
delete [] markRow;
delete [] markKnapsack;
delete [] coefficient;
delete [] linear;
delete [] whichRow;
delete [] lookupRow;
delete si;
si = NULL;
if (!badModel && finalModel) {
finalModel->setDblParam(OsiObjOffset, coinModel.objectiveOffset());
return finalModel;
} else {
delete finalModel;
printf("can't make knapsacks - did you set fixedPriority (extra1)\n");
return NULL;
}
}
#endif //COIN_HAS_LINK
// Fills in original solution (coinModel length)
void
afterKnapsack(const CoinModel & coinModel2, const int * whichColumn, const int * knapsackStart,
const int * knapsackRow, int numberKnapsack,
const double * knapsackSolution, double * solution, int logLevel)
{
CoinModel coinModel = coinModel2;
int numberColumns = coinModel.numberColumns();
int iColumn;
// associate all columns to stop possible error messages
for (iColumn=0;iColumn<numberColumns;iColumn++) {
coinModel.associateElement(coinModel.columnName(iColumn),1.0);
}
CoinZeroN(solution,numberColumns);
int nCol=knapsackStart[0];
for (iColumn=0;iColumn<nCol;iColumn++) {
int jColumn = whichColumn[iColumn];
solution[jColumn]=knapsackSolution[iColumn];
}
int * buildRow = new int [numberColumns]; // wild overkill
double * buildElement = new double [numberColumns];
int iKnapsack;
for (iKnapsack=0;iKnapsack<numberKnapsack;iKnapsack++) {
int k=-1;
for (iColumn=knapsackStart[iKnapsack];iColumn<knapsackStart[iKnapsack+1];iColumn++) {
if (knapsackSolution[iColumn]>1.0e-5) {
if (k>=0) {
printf("Two nonzero values for knapsack %d at (%d,%g) and (%d,%g)\n",iKnapsack,
k,knapsackSolution[k],iColumn,knapsackSolution[iColumn]);
abort();
}
k=iColumn;
assert (fabs(floor(knapsackSolution[iColumn]+0.5)-knapsackSolution[iColumn])<1.0e-5);
}
}
if (k>=0) {
int iRow = knapsackRow[iKnapsack];
int nCreate = 10000;
int nel=coinModel.expandKnapsack(iRow,nCreate,NULL,NULL,buildRow,buildElement,k-knapsackStart[iKnapsack]);
assert (nel);
if (logLevel>0)
printf("expanded column %d in knapsack %d has %d nonzero entries:\n",
k-knapsackStart[iKnapsack],iKnapsack,nel);
for (int i=0;i<nel;i++) {
int jColumn = buildRow[i];
double value = buildElement[i];
if (logLevel>0)
printf("%d - original %d has value %g\n",i,jColumn,value);
solution[jColumn]=value;
}
}
}
delete [] buildRow;
delete [] buildElement;
#if 0
for (iColumn=0;iColumn<numberColumns;iColumn++) {
if (solution[iColumn]>1.0e-5&&coinModel.isInteger(iColumn))
printf("%d %g\n",iColumn,solution[iColumn]);
}
#endif
}