1 // $Id$
2 // Copyright (C) 2004, International Business Machines
3 // Corporation and others. All Rights Reserved.
4 // This code is licensed under the terms of the Eclipse Public License (EPL).
5
6 // Edwin 11/13/2009-- carved out of CbcBranchCut
7
8 #if defined(_MSC_VER)
9 // Turn off compiler warning about long names
10 #pragma warning(disable : 4786)
11 #endif
12 #include <cassert>
13 #include <cstdlib>
14 #include <cmath>
15 #include <cfloat>
16 //#define CBC_DEBUG
17
18 #include "OsiSolverInterface.hpp"
19 #include "CbcModel.hpp"
20 #include "CbcMessage.hpp"
21 #include "CbcBranchCut.hpp"
22 #include "CoinSort.hpp"
23 #include "CoinError.hpp"
24 #include "CbcBranchToFixLots.hpp"
25
26 /** Default Constructor
27
28 Equivalent to an unspecified binary variable.
29 */
CbcBranchToFixLots()30 CbcBranchToFixLots::CbcBranchToFixLots()
31 : CbcBranchCut()
32 , djTolerance_(COIN_DBL_MAX)
33 , fractionFixed_(1.0)
34 , mark_(NULL)
35 , depth_(-1)
36 , numberClean_(0)
37 , alwaysCreate_(false)
38 {
39 }
40
41 /* Useful constructor - passed reduced cost tolerance and fraction we would like fixed.
42 Also depth level to do at.
43 Also passed number of 1 rows which when clean triggers fix
44 Always does if all 1 rows cleaned up and number>0 or if fraction columns reached
45 Also whether to create branch if can't reach fraction.
46 */
CbcBranchToFixLots(CbcModel * model,double djTolerance,double fractionFixed,int depth,int numberClean,const char * mark,bool alwaysCreate)47 CbcBranchToFixLots::CbcBranchToFixLots(CbcModel *model, double djTolerance,
48 double fractionFixed, int depth,
49 int numberClean,
50 const char *mark, bool alwaysCreate)
51 : CbcBranchCut(model)
52 {
53 djTolerance_ = djTolerance;
54 fractionFixed_ = fractionFixed;
55 if (mark) {
56 int numberColumns = model->getNumCols();
57 mark_ = new char[numberColumns];
58 memcpy(mark_, mark, numberColumns);
59 } else {
60 mark_ = NULL;
61 }
62 depth_ = depth;
63 assert(model);
64 OsiSolverInterface *solver = model_->solver();
65 matrixByRow_ = *solver->getMatrixByRow();
66 numberClean_ = numberClean;
67 alwaysCreate_ = alwaysCreate;
68 }
69 // Copy constructor
CbcBranchToFixLots(const CbcBranchToFixLots & rhs)70 CbcBranchToFixLots::CbcBranchToFixLots(const CbcBranchToFixLots &rhs)
71 : CbcBranchCut(rhs)
72 {
73 djTolerance_ = rhs.djTolerance_;
74 fractionFixed_ = rhs.fractionFixed_;
75 int numberColumns = model_->getNumCols();
76 mark_ = CoinCopyOfArray(rhs.mark_, numberColumns);
77 matrixByRow_ = rhs.matrixByRow_;
78 depth_ = rhs.depth_;
79 numberClean_ = rhs.numberClean_;
80 alwaysCreate_ = rhs.alwaysCreate_;
81 }
82
83 // Clone
84 CbcObject *
clone() const85 CbcBranchToFixLots::clone() const
86 {
87 return new CbcBranchToFixLots(*this);
88 }
89
90 // Assignment operator
91 CbcBranchToFixLots &
operator =(const CbcBranchToFixLots & rhs)92 CbcBranchToFixLots::operator=(const CbcBranchToFixLots &rhs)
93 {
94 if (this != &rhs) {
95 CbcBranchCut::operator=(rhs);
96 djTolerance_ = rhs.djTolerance_;
97 fractionFixed_ = rhs.fractionFixed_;
98 int numberColumns = model_->getNumCols();
99 delete[] mark_;
100 mark_ = CoinCopyOfArray(rhs.mark_, numberColumns);
101 matrixByRow_ = rhs.matrixByRow_;
102 depth_ = rhs.depth_;
103 numberClean_ = rhs.numberClean_;
104 alwaysCreate_ = rhs.alwaysCreate_;
105 }
106 return *this;
107 }
108
109 // Destructor
~CbcBranchToFixLots()110 CbcBranchToFixLots::~CbcBranchToFixLots()
111 {
112 delete[] mark_;
113 }
114 CbcBranchingObject *
createCbcBranch(OsiSolverInterface * solver,const OsiBranchingInformation *,int)115 CbcBranchToFixLots::createCbcBranch(OsiSolverInterface *solver, const OsiBranchingInformation * /*info*/, int /*way*/)
116 {
117 // by default way must be -1
118 //assert (way==-1);
119 //OsiSolverInterface * solver = model_->solver();
120 const double *solution = model_->testSolution();
121 const double *lower = solver->getColLower();
122 const double *upper = solver->getColUpper();
123 const double *dj = solver->getReducedCost();
124 int i;
125 int numberIntegers = model_->numberIntegers();
126 const int *integerVariable = model_->integerVariable();
127 double integerTolerance = model_->getDblParam(CbcModel::CbcIntegerTolerance);
128 // make smaller ?
129 double tolerance = CoinMin(1.0e-8, integerTolerance);
130 // How many fixed are we aiming at
131 int wantedFixed = static_cast< int >(static_cast< double >(numberIntegers) * fractionFixed_);
132 int nSort = 0;
133 int numberFixed = 0;
134 int numberColumns = solver->getNumCols();
135 int *sort = new int[numberColumns];
136 double *dsort = new double[numberColumns];
137 if (djTolerance_ != -1.234567) {
138 int type = shallWe();
139 assert(type);
140 // Take clean first
141 if (type == 1) {
142 for (i = 0; i < numberIntegers; i++) {
143 int iColumn = integerVariable[i];
144 if (upper[iColumn] > lower[iColumn]) {
145 if (!mark_ || !mark_[iColumn]) {
146 if (solution[iColumn] < lower[iColumn] + tolerance) {
147 if (dj[iColumn] > djTolerance_) {
148 dsort[nSort] = -dj[iColumn];
149 sort[nSort++] = iColumn;
150 }
151 } else if (solution[iColumn] > upper[iColumn] - tolerance) {
152 if (dj[iColumn] < -djTolerance_) {
153 dsort[nSort] = dj[iColumn];
154 sort[nSort++] = iColumn;
155 }
156 }
157 }
158 } else {
159 numberFixed++;
160 }
161 }
162 // sort
163 CoinSort_2(dsort, dsort + nSort, sort);
164 nSort = CoinMin(nSort, wantedFixed - numberFixed);
165 } else if (type < 10) {
166 int i;
167 //const double * rowLower = solver->getRowLower();
168 const double *rowUpper = solver->getRowUpper();
169 // Row copy
170 const double *elementByRow = matrixByRow_.getElements();
171 const int *column = matrixByRow_.getIndices();
172 const CoinBigIndex *rowStart = matrixByRow_.getVectorStarts();
173 const int *rowLength = matrixByRow_.getVectorLengths();
174 const double *columnLower = solver->getColLower();
175 const double *columnUpper = solver->getColUpper();
176 const double *solution = solver->getColSolution();
177 int numberColumns = solver->getNumCols();
178 int numberRows = solver->getNumRows();
179 for (i = 0; i < numberColumns; i++) {
180 sort[i] = i;
181 if (columnLower[i] != columnUpper[i]) {
182 dsort[i] = 1.0e100;
183 } else {
184 dsort[i] = 1.0e50;
185 numberFixed++;
186 }
187 }
188 for (i = 0; i < numberRows; i++) {
189 double rhsValue = rowUpper[i];
190 bool oneRow = true;
191 // check elements
192 int numberUnsatisfied = 0;
193 for (CoinBigIndex j = rowStart[i]; j < rowStart[i] + rowLength[i]; j++) {
194 int iColumn = column[j];
195 double value = elementByRow[j];
196 double solValue = solution[iColumn];
197 if (columnLower[iColumn] != columnUpper[iColumn]) {
198 if (solValue < 1.0 - integerTolerance && solValue > integerTolerance)
199 numberUnsatisfied++;
200 if (value != 1.0) {
201 oneRow = false;
202 break;
203 }
204 } else {
205 rhsValue -= value * floor(solValue + 0.5);
206 }
207 }
208 if (oneRow && rhsValue <= 1.0 + tolerance) {
209 if (!numberUnsatisfied) {
210 for (CoinBigIndex j = rowStart[i]; j < rowStart[i] + rowLength[i]; j++) {
211 int iColumn = column[j];
212 if (dsort[iColumn] > 1.0e50) {
213 dsort[iColumn] = 0;
214 nSort++;
215 }
216 }
217 }
218 }
219 }
220 // sort
221 CoinSort_2(dsort, dsort + numberColumns, sort);
222 } else {
223 // new way
224 for (i = 0; i < numberIntegers; i++) {
225 int iColumn = integerVariable[i];
226 if (upper[iColumn] > lower[iColumn]) {
227 if (!mark_ || !mark_[iColumn]) {
228 double distanceDown = solution[iColumn] - lower[iColumn];
229 double distanceUp = upper[iColumn] - solution[iColumn];
230 double distance = CoinMin(distanceDown, distanceUp);
231 if (distance > 0.001 && distance < 0.5) {
232 dsort[nSort] = distance;
233 sort[nSort++] = iColumn;
234 }
235 }
236 }
237 }
238 // sort
239 CoinSort_2(dsort, dsort + nSort, sort);
240 int n = 0;
241 double sum = 0.0;
242 for (int k = 0; k < nSort; k++) {
243 sum += dsort[k];
244 if (sum <= djTolerance_)
245 n = k;
246 else
247 break;
248 }
249 nSort = CoinMin(n, numberClean_ / 1000000);
250 }
251 } else {
252 #define FIX_IF_LESS -0.1
253 // 3 in same row and sum <FIX_IF_LESS?
254 int numberRows = matrixByRow_.getNumRows();
255 const double *solution = model_->testSolution();
256 const int *column = matrixByRow_.getIndices();
257 const CoinBigIndex *rowStart = matrixByRow_.getVectorStarts();
258 const int *rowLength = matrixByRow_.getVectorLengths();
259 double bestSum = 1.0;
260 int nBest = -1;
261 int kRow = -1;
262 OsiSolverInterface *solver = model_->solver();
263 for (int i = 0; i < numberRows; i++) {
264 int numberUnsatisfied = 0;
265 double sum = 0.0;
266 for (CoinBigIndex j = rowStart[i]; j < rowStart[i] + rowLength[i]; j++) {
267 int iColumn = column[j];
268 if (solver->isInteger(iColumn)) {
269 double solValue = solution[iColumn];
270 if (solValue > 1.0e-5 && solValue < FIX_IF_LESS) {
271 numberUnsatisfied++;
272 sum += solValue;
273 }
274 }
275 }
276 if (numberUnsatisfied >= 3 && sum < FIX_IF_LESS) {
277 // possible
278 if (numberUnsatisfied > nBest || (numberUnsatisfied == nBest && sum < bestSum)) {
279 nBest = numberUnsatisfied;
280 bestSum = sum;
281 kRow = i;
282 }
283 }
284 }
285 assert(nBest > 0);
286 for (CoinBigIndex j = rowStart[kRow]; j < rowStart[kRow] + rowLength[kRow]; j++) {
287 int iColumn = column[j];
288 if (solver->isInteger(iColumn)) {
289 double solValue = solution[iColumn];
290 if (solValue > 1.0e-5 && solValue < FIX_IF_LESS) {
291 sort[nSort++] = iColumn;
292 }
293 }
294 }
295 }
296 OsiRowCut down;
297 down.setLb(-COIN_DBL_MAX);
298 double rhs = 0.0;
299 for (i = 0; i < nSort; i++) {
300 int iColumn = sort[i];
301 double distanceDown = solution[iColumn] - lower[iColumn];
302 double distanceUp = upper[iColumn] - solution[iColumn];
303 if (distanceDown < distanceUp) {
304 rhs += lower[iColumn];
305 dsort[i] = 1.0;
306 } else {
307 rhs -= upper[iColumn];
308 dsort[i] = -1.0;
309 }
310 }
311 down.setUb(rhs);
312 down.setRow(nSort, sort, dsort);
313 down.setEffectiveness(COIN_DBL_MAX); // so will persist
314 delete[] sort;
315 delete[] dsort;
316 // up is same - just with rhs changed
317 OsiRowCut up = down;
318 up.setLb(rhs + 1.0);
319 up.setUb(COIN_DBL_MAX);
320 // Say can fix one way
321 CbcCutBranchingObject *newObject = new CbcCutBranchingObject(model_, down, up, true);
322 if (model_->messageHandler()->logLevel() > 1)
323 printf("creating cut in CbcBranchCut\n");
324 return newObject;
325 }
326 /* Does a lot of the work,
327 Returns 0 if no good, 1 if dj, 2 if clean, 3 if both
328 10 if branching on ones away from bound
329 */
shallWe() const330 int CbcBranchToFixLots::shallWe() const
331 {
332 int returnCode = 0;
333 OsiSolverInterface *solver = model_->solver();
334 int numberRows = matrixByRow_.getNumRows();
335 //if (numberRows!=solver->getNumRows())
336 //return 0;
337 const double *solution = model_->testSolution();
338 const double *lower = solver->getColLower();
339 const double *upper = solver->getColUpper();
340 const double *dj = solver->getReducedCost();
341 int i;
342 int numberIntegers = model_->numberIntegers();
343 const int *integerVariable = model_->integerVariable();
344 if (numberClean_ > 1000000) {
345 int wanted = numberClean_ % 1000000;
346 int *sort = new int[numberIntegers];
347 double *dsort = new double[numberIntegers];
348 int nSort = 0;
349 for (i = 0; i < numberIntegers; i++) {
350 int iColumn = integerVariable[i];
351 if (upper[iColumn] > lower[iColumn]) {
352 if (!mark_ || !mark_[iColumn]) {
353 double distanceDown = solution[iColumn] - lower[iColumn];
354 double distanceUp = upper[iColumn] - solution[iColumn];
355 double distance = CoinMin(distanceDown, distanceUp);
356 if (distance > 0.001 && distance < 0.5) {
357 dsort[nSort] = distance;
358 sort[nSort++] = iColumn;
359 }
360 }
361 }
362 }
363 // sort
364 CoinSort_2(dsort, dsort + nSort, sort);
365 int n = 0;
366 double sum = 0.0;
367 for (int k = 0; k < nSort; k++) {
368 sum += dsort[k];
369 if (sum <= djTolerance_)
370 n = k;
371 else
372 break;
373 }
374 delete[] sort;
375 delete[] dsort;
376 return (n >= wanted) ? 10 : 0;
377 }
378 double integerTolerance = model_->getDblParam(CbcModel::CbcIntegerTolerance);
379 // make smaller ?
380 double tolerance = CoinMin(1.0e-8, integerTolerance);
381 // How many fixed are we aiming at
382 int wantedFixed = static_cast< int >(static_cast< double >(numberIntegers) * fractionFixed_);
383 if (djTolerance_ < 1.0e10) {
384 int nSort = 0;
385 int numberFixed = 0;
386 for (i = 0; i < numberIntegers; i++) {
387 int iColumn = integerVariable[i];
388 if (upper[iColumn] > lower[iColumn]) {
389 if (!mark_ || !mark_[iColumn]) {
390 if (solution[iColumn] < lower[iColumn] + tolerance) {
391 if (dj[iColumn] > djTolerance_) {
392 nSort++;
393 }
394 } else if (solution[iColumn] > upper[iColumn] - tolerance) {
395 if (dj[iColumn] < -djTolerance_) {
396 nSort++;
397 }
398 }
399 }
400 } else {
401 numberFixed++;
402 }
403 }
404 if (numberFixed + nSort < wantedFixed && !alwaysCreate_) {
405 returnCode = 0;
406 } else if (numberFixed < wantedFixed) {
407 returnCode = 1;
408 } else {
409 returnCode = 0;
410 }
411 }
412 if (numberClean_) {
413 // see how many rows clean
414 int i;
415 //const double * rowLower = solver->getRowLower();
416 const double *rowUpper = solver->getRowUpper();
417 // Row copy
418 const double *elementByRow = matrixByRow_.getElements();
419 const int *column = matrixByRow_.getIndices();
420 const CoinBigIndex *rowStart = matrixByRow_.getVectorStarts();
421 const int *rowLength = matrixByRow_.getVectorLengths();
422 const double *columnLower = solver->getColLower();
423 const double *columnUpper = solver->getColUpper();
424 const double *solution = solver->getColSolution();
425 int numberClean = 0;
426 bool someToDoYet = false;
427 int numberColumns = solver->getNumCols();
428 char *mark = new char[numberColumns];
429 int numberFixed = 0;
430 for (i = 0; i < numberColumns; i++) {
431 if (columnLower[i] != columnUpper[i]) {
432 mark[i] = 0;
433 } else {
434 mark[i] = 1;
435 numberFixed++;
436 }
437 }
438 int numberNewFixed = 0;
439 for (i = 0; i < numberRows; i++) {
440 double rhsValue = rowUpper[i];
441 bool oneRow = true;
442 // check elements
443 int numberUnsatisfied = 0;
444 for (CoinBigIndex j = rowStart[i]; j < rowStart[i] + rowLength[i]; j++) {
445 int iColumn = column[j];
446 double value = elementByRow[j];
447 double solValue = solution[iColumn];
448 if (columnLower[iColumn] != columnUpper[iColumn]) {
449 if (solValue < 1.0 - integerTolerance && solValue > integerTolerance)
450 numberUnsatisfied++;
451 if (value != 1.0) {
452 oneRow = false;
453 break;
454 }
455 } else {
456 rhsValue -= value * floor(solValue + 0.5);
457 }
458 }
459 if (oneRow && rhsValue <= 1.0 + tolerance) {
460 if (numberUnsatisfied) {
461 someToDoYet = true;
462 } else {
463 numberClean++;
464 for (CoinBigIndex j = rowStart[i]; j < rowStart[i] + rowLength[i]; j++) {
465 int iColumn = column[j];
466 if (columnLower[iColumn] != columnUpper[iColumn] && !mark[iColumn]) {
467 mark[iColumn] = 1;
468 numberNewFixed++;
469 }
470 }
471 }
472 }
473 }
474 delete[] mark;
475 //printf("%d clean, %d old fixed, %d new fixed\n",
476 // numberClean,numberFixed,numberNewFixed);
477 if (someToDoYet && numberClean < numberClean_
478 && numberNewFixed + numberFixed < wantedFixed) {
479 } else if (numberFixed < wantedFixed) {
480 returnCode |= 2;
481 } else {
482 }
483 }
484 return returnCode;
485 }
486 double
infeasibility(const OsiBranchingInformation *,int & preferredWay) const487 CbcBranchToFixLots::infeasibility(const OsiBranchingInformation * /*info*/,
488 int &preferredWay) const
489 {
490 preferredWay = -1;
491 CbcNode *node = model_->currentNode();
492 int depth;
493 if (node)
494 depth = CoinMax(node->depth(), 0);
495 else
496 return 0.0;
497 if (depth_ < 0) {
498 return 0.0;
499 } else if (depth_ > 0) {
500 if ((depth % depth_) != 0)
501 return 0.0;
502 }
503 if (djTolerance_ != -1.234567) {
504 if (!shallWe())
505 return 0.0;
506 else
507 return 1.0e20;
508 } else {
509 // See if 3 in same row and sum <FIX_IF_LESS?
510 int numberRows = matrixByRow_.getNumRows();
511 const double *solution = model_->testSolution();
512 const int *column = matrixByRow_.getIndices();
513 const CoinBigIndex *rowStart = matrixByRow_.getVectorStarts();
514 const int *rowLength = matrixByRow_.getVectorLengths();
515 double bestSum = 1.0;
516 int nBest = -1;
517 OsiSolverInterface *solver = model_->solver();
518 for (int i = 0; i < numberRows; i++) {
519 int numberUnsatisfied = 0;
520 double sum = 0.0;
521 for (CoinBigIndex j = rowStart[i]; j < rowStart[i] + rowLength[i]; j++) {
522 int iColumn = column[j];
523 if (solver->isInteger(iColumn)) {
524 double solValue = solution[iColumn];
525 if (solValue > 1.0e-5 && solValue < FIX_IF_LESS) {
526 numberUnsatisfied++;
527 sum += solValue;
528 }
529 }
530 }
531 if (numberUnsatisfied >= 3 && sum < FIX_IF_LESS) {
532 // possible
533 if (numberUnsatisfied > nBest || (numberUnsatisfied == nBest && sum < bestSum)) {
534 nBest = numberUnsatisfied;
535 bestSum = sum;
536 }
537 }
538 }
539 if (nBest > 0)
540 return 1.0e20;
541 else
542 return 0.0;
543 }
544 }
545 // Redoes data when sequence numbers change
redoSequenceEtc(CbcModel * model,int numberColumns,const int * originalColumns)546 void CbcBranchToFixLots::redoSequenceEtc(CbcModel *model, int numberColumns, const int *originalColumns)
547 {
548 model_ = model;
549 if (mark_) {
550 OsiSolverInterface *solver = model_->solver();
551 int numberColumnsNow = solver->getNumCols();
552 char *temp = new char[numberColumnsNow];
553 memset(temp, 0, numberColumnsNow);
554 for (int i = 0; i < numberColumns; i++) {
555 int j = originalColumns[i];
556 temp[i] = mark_[j];
557 }
558 delete[] mark_;
559 mark_ = temp;
560 }
561 OsiSolverInterface *solver = model_->solver();
562 matrixByRow_ = *solver->getMatrixByRow();
563 }
564
565 /* vi: softtabstop=2 shiftwidth=2 expandtab tabstop=2
566 */
567