1 /* $Id$ */
2 // Copyright (C) 2008, 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 #if defined(_MSC_VER)
7 // Turn off compiler warning about long names
8 #pragma warning(disable : 4786)
9 #endif
10 #include <cassert>
11 #include <cstdlib>
12 #include <cmath>
13 #include <cfloat>
14 #include <vector>
15 
16 #include "OsiSolverInterface.hpp"
17 #include "CbcModel.hpp"
18 #include "CbcMessage.hpp"
19 #include "CbcHeuristicPivotAndFix.hpp"
20 #include "OsiClpSolverInterface.hpp"
21 #include "CoinTime.hpp"
22 
23 //#define FORNOW
24 
25 // Default Constructor
CbcHeuristicPivotAndFix()26 CbcHeuristicPivotAndFix::CbcHeuristicPivotAndFix()
27   : CbcHeuristic()
28 {
29 }
30 
31 // Constructor with model - assumed before cuts
32 
CbcHeuristicPivotAndFix(CbcModel & model)33 CbcHeuristicPivotAndFix::CbcHeuristicPivotAndFix(CbcModel &model)
34   : CbcHeuristic(model)
35 {
36 }
37 
38 // Destructor
~CbcHeuristicPivotAndFix()39 CbcHeuristicPivotAndFix::~CbcHeuristicPivotAndFix()
40 {
41 }
42 
43 // Clone
44 CbcHeuristic *
clone() const45 CbcHeuristicPivotAndFix::clone() const
46 {
47   return new CbcHeuristicPivotAndFix(*this);
48 }
49 // Create C++ lines to get to current state
generateCpp(FILE * fp)50 void CbcHeuristicPivotAndFix::generateCpp(FILE *fp)
51 {
52   CbcHeuristicPivotAndFix other;
53   fprintf(fp, "0#include \"CbcHeuristicPivotAndFix.hpp\"\n");
54   fprintf(fp, "3  CbcHeuristicPivotAndFix heuristicPFX(*cbcModel);\n");
55   CbcHeuristic::generateCpp(fp, "heuristicPFX");
56   fprintf(fp, "3  cbcModel->addHeuristic(&heuristicPFX);\n");
57 }
58 
59 // Copy constructor
CbcHeuristicPivotAndFix(const CbcHeuristicPivotAndFix & rhs)60 CbcHeuristicPivotAndFix::CbcHeuristicPivotAndFix(const CbcHeuristicPivotAndFix &rhs)
61   : CbcHeuristic(rhs)
62 {
63 }
64 
65 // Assignment operator
66 CbcHeuristicPivotAndFix &
operator =(const CbcHeuristicPivotAndFix & rhs)67 CbcHeuristicPivotAndFix::operator=(const CbcHeuristicPivotAndFix &rhs)
68 {
69   if (this != &rhs) {
70     CbcHeuristic::operator=(rhs);
71   }
72   return *this;
73 }
74 
75 // Resets stuff if model changes
resetModel(CbcModel *)76 void CbcHeuristicPivotAndFix::resetModel(CbcModel * /*model*/)
77 {
78   //CbcHeuristic::resetModel(model);
79 }
80 /*
81   Comments needed
82   Returns 1 if solution, 0 if not */
solution(double &,double *)83 int CbcHeuristicPivotAndFix::solution(double & /*solutionValue*/,
84   double * /*betterSolution*/)
85 {
86 
87   numCouldRun_++; // Todo: Ask JJHF what this for.
88   std::cout << "Entering Pivot-and-Fix Heuristic" << std::endl;
89 
90 #ifdef HEURISTIC_INFORM
91   printf("Entering heuristic %s - nRuns %d numCould %d when %d\n",
92     heuristicName(), numRuns_, numCouldRun_, when_);
93 #endif
94 #ifdef FORNOW
95   std::cout << "Lucky you! You're in the Pivot-and-Fix Heuristic" << std::endl;
96   // The struct should be moved to member data
97 
98   typedef struct {
99     int numberSolutions;
100     int maximumSolutions;
101     int numberColumns;
102     double **solution;
103     int *numberUnsatisfied;
104   } clpSolution;
105 
106   double start = CoinCpuTime();
107 
108   OsiClpSolverInterface *clpSolverOriginal
109     = dynamic_cast< OsiClpSolverInterface * >(model_->solver());
110   assert(clpSolverOriginal);
111   OsiClpSolverInterface *clpSolver(clpSolverOriginal);
112 
113   ClpSimplex *simplex = clpSolver->getModelPtr();
114 
115   // Initialize the structure holding the solutions
116   clpSolution solutions;
117   // Set typeStruct field of ClpTrustedData struct to one.
118   // This tells Clp it's "Mahdi!"
119   ClpTrustedData trustedSolutions;
120   trustedSolutions.typeStruct = 1;
121   trustedSolutions.data = &solutions;
122   solutions.numberSolutions = 0;
123   solutions.maximumSolutions = 0;
124   solutions.numberColumns = simplex->numberColumns();
125   solutions.solution = NULL;
126   solutions.numberUnsatisfied = NULL;
127   simplex->setTrustedUserPointer(&trustedSolutions);
128 
129   // Solve from all slack to get some points
130   simplex->allSlackBasis();
131   simplex->primal();
132 
133   // -------------------------------------------------
134   // Get the problem information
135   // - get the number of cols and rows
136   int numCols = clpSolver->getNumCols();
137   int numRows = clpSolver->getNumRows();
138 
139   // - get the right hand side of the rows
140   const double *rhs = clpSolver->getRightHandSide();
141 
142   // - find the integer variables
143   bool *varClassInt = new bool[numCols];
144   int numInt = 0;
145   for (int i = 0; i < numCols; i++) {
146     if (clpSolver->isContinuous(i))
147       varClassInt[i] = 0;
148     else {
149       varClassInt[i] = 1;
150       numInt++;
151     }
152   }
153 
154   // -Get the rows sense
155   const char *rowSense;
156   rowSense = clpSolver->getRowSense();
157 
158   // -Get the objective coefficients
159   const double *objCoefficients = clpSolver->getObjCoefficients();
160   double *originalObjCoeff = new double[numCols];
161   for (int i = 0; i < numCols; i++)
162     originalObjCoeff[i] = objCoefficients[i];
163 
164   // -Get the matrix of the problem
165   double **matrix = new double *[numRows];
166   for (int i = 0; i < numRows; i++) {
167     matrix[i] = new double[numCols];
168     for (int j = 0; j < numCols; j++)
169       matrix[i][j] = 0;
170   }
171   const CoinPackedMatrix *matrixByRow = clpSolver->getMatrixByRow();
172   const double *matrixElements = matrixByRow->getElements();
173   const int *matrixIndices = matrixByRow->getIndices();
174   const int *matrixStarts = matrixByRow->getVectorStarts();
175   for (int j = 0; j < numRows; j++) {
176     for (int i = matrixStarts[j]; i < matrixStarts[j + 1]; i++) {
177       matrix[j][matrixIndices[i]] = matrixElements[i];
178     }
179   }
180 
181   // The newObj is the randomly perturbed constraint used to find new
182   // corner points
183   double *newObj = new double[numCols];
184 
185   // Set the random seed
186   srand(time(NULL) + 1);
187   int randNum;
188 
189   // We're going to add a new row to the LP formulation
190   // after finding each new solution.
191   // Adding a new row requires the new elements and the new indices.
192   // The elements are original objective function coefficients.
193   // The indicies are the (dense) columns indices stored in addRowIndex.
194   // The rhs is the value of the new solution stored in solutionValue.
195   int *addRowIndex = new int[numCols];
196   for (int i = 0; i < numCols; i++)
197     addRowIndex[i] = i;
198 
199   // The number of feasible solutions found by the PF heuristic.
200   // This controls the return code of the solution() method.
201   int numFeasibles = 0;
202 
203   // Shuffle the rows
204   int *index = new int[numRows];
205   for (int i = 0; i < numRows; i++)
206     index[i] = i;
207   for (int i = 0; i < numRows; i++) {
208     int temp = index[i];
209     int randNumTemp = i + (rand() % (numRows - i));
210     index[i] = index[randNumTemp];
211     index[randNumTemp] = temp;
212   }
213 
214   // In the clpSolution struct, we store a lot of column solutions.
215   // For each perturb objective, we store the solution from each
216   // iteration of the LP solve.
217   // For each perturb objective, we look at the collection of
218   // solutions to do something extremly intelligent :-)
219   // We could (and should..and will :-) wipe out the block of
220   // solutions when we're done with them. But for now, we just move on
221   // and store the next block of solutions for the next (perturbed)
222   // objective.
223   // The variable startIndex tells us where the new block begins.
224   int startIndex = 0;
225 
226   // At most "fixThreshold" number of integer variables can be unsatisfied
227   // for calling smallBranchAndBound().
228   // The PF Heuristic only fixes fixThreshold number of variables to
229   // their integer values. Not more. Not less. The reason is to give
230   // the smallBB some opportunity to find better solutions. If we fix
231   // everything it might be too many (leading the heuristic to come up
232   // with infeasibility rather than a useful result).
233   // (This is an important paramater. And it is dynamically set.)
234   double fixThreshold;
235   /*
236         if(numInt > 400)
237         fixThreshold = 17*sqrt(numInt);
238         if(numInt<=400 && numInt>100)
239         fixThreshold = 5*sqrt(numInt);
240         if(numInt<=100)
241         fixThreshold = 4*sqrt(numInt);
242     */
243   // Initialize fixThreshold based on the number of integer
244   // variables
245   if (numInt <= 100)
246     fixThreshold = .35 * numInt;
247   if (numInt > 100 && numInt < 1000)
248     fixThreshold = .85 * numInt;
249   if (numInt >= 1000)
250     fixThreshold = .1 * numInt;
251 
252   // Whenever the dynamic system for changing fixThreshold
253   // kicks in, it changes the parameter by the
254   // fixThresholdChange amount.
255   // (The 25% should be member data and tuned. Another paper!)
256   double fixThresholdChange = 0.25 * fixThreshold;
257 
258   // maxNode is the maximum number of nodes we allow smallBB to
259   // search. It's initialized to 400 and changed dynamically.
260   // The 400 should be member data, if we become virtuous.
261   int maxNode = 400;
262 
263   // We control the decision to change maxNode through the boolean
264   // variable  changeMaxNode. The boolean variable is initialized to
265   // true and gets set to false under a condition (and is never true
266   // again.)
267   // It's flipped off and stays off (in the current incarnation of PF)
268   bool changeMaxNode = 1;
269 
270   // The sumReturnCode is used for the dynamic system that sets
271   // fixThreshold and changeMaxNode.
272   //
273   // We track what's happening in sumReturnCode. There are 8 switches.
274   // The first 5 switches corresponds to a return code for smallBB.
275   //
276   // We want to know how many times we consecutively get the same
277   // return code.
278   //
279   // If "good" return codes are happening often enough, we're happy.
280   //
281   // If a "bad" returncodes happen consecutively, we want to
282   // change something.
283   //
284   // The switch 5 is the number of times PF didn't call smallBB
285   // becuase the number of integer variables that took integer values
286   // was less than fixThreshold.
287   //
288   // The swicth 6 was added for a brilliant idea...to be announced
289   // later (another paper!)
290   //
291   // The switch 7 is the one that changes the max node. Read the
292   // code. (Todo: Verbalize the brilliant idea for the masses.)
293   //
294   int sumReturnCode[8];
295   /*
296       sumReturnCode[0] ~ -1 --> problem too big for smallBB
297       sumReturnCode[1] ~ 0  --> smallBB not finshed and no soln
298       sumReturnCode[2] ~ 1  --> smallBB not finshed and there is a soln
299       sumReturnCode[3] ~ 2  --> smallBB finished and no soln
300       sumReturnCode[4] ~ 3  --> smallBB finished and there is a soln
301       sumReturnCode[5] ~ didn't call smallBranchAndBound too few to fix
302       sumReturnCode[6] ~ didn't call smallBranchAndBound too many unsatisfied
303       sumReturnCode[7] ~ the same as sumReturnCode[1] but becomes zero just if the returnCode is not 0
304     */
305 
306   for (int i = 0; i < 8; i++)
307     sumReturnCode[i] = 0;
308   int *colIndex = new int[numCols];
309   for (int i = 0; i < numCols; i++)
310     colIndex[i] = i;
311   double cutoff = COIN_DBL_MAX;
312   bool didMiniBB;
313 
314   // Main loop
315   for (int i = 0; i < numRows; i++) {
316     // track the number of mini-bb for the dynamic threshold setting
317     didMiniBB = 0;
318 
319     for (int k = startIndex; k < solutions.numberSolutions; k++)
320       //if the point has 0 unsatisfied variables; make sure it is
321       //feasible. Check integer feasiblity and constraints.
322       if (solutions.numberUnsatisfied[k] == 0) {
323         double feasibility = 1;
324         //check integer feasibility
325         for (int icol = 0; icol < numCols; icol++) {
326           double closest = floor(solutions.solution[k][icol] + 0.5);
327           if (varClassInt[icol] && (fabs(solutions.solution[k][icol] - closest) > 1e-6)) {
328             feasibility = 0;
329             break;
330           }
331         }
332         //check if the solution satisfies the constraints
333         for (int irow = 0; irow < numRows; irow++) {
334           double lhs = 0;
335           for (int j = 0; j < numCols; j++)
336             lhs += matrix[irow][j] * solutions.solution[k][j];
337           if (rowSense[irow] == 'L' && lhs > rhs[irow] + 1e-6) {
338             feasibility = 0;
339             break;
340           }
341           if (rowSense[irow] == 'G' && lhs < rhs[irow] - 1e-6) {
342             feasibility = 0;
343             break;
344           }
345           if (rowSense[irow] == 'E' && (lhs - rhs[irow] > 1e-6 || lhs - rhs[irow] < -1e-6)) {
346             feasibility = 0;
347             break;
348           }
349         }
350 
351         //if feasible, find the objective value and set the cutoff
352         // for the smallBB and add a new constraint to the LP
353         // (and update the best solution found so far for the
354         // return arguments)
355         if (feasibility) {
356           double objectiveValue = 0;
357           for (int j = 0; j < numCols; j++)
358             objectiveValue += solutions.solution[k][j] * originalObjCoeff[j];
359           cutoff = objectiveValue;
360           clpSolver->addRow(numCols, addRowIndex, originalObjCoeff, -COIN_DBL_MAX, cutoff);
361 
362           // Todo: pick up the best solution in the block (not
363           // the last).
364           solutionValue = objectiveValue;
365           for (int m = 0; m < numCols; m++)
366             betterSolution[m] = solutions.solution[k][m];
367           numFeasibles++;
368         }
369       }
370 
371     // Go through the block of solution and decide if to call smallBB
372     for (int k = startIndex; k < solutions.numberSolutions; k++) {
373       if (solutions.numberUnsatisfied[k] <= fixThreshold) {
374         // get new copy
375         OsiSolverInterface *newSolver;
376         newSolver = new OsiClpSolverInterface(*clpSolver);
377         newSolver->setObjSense(1);
378         newSolver->setObjective(originalObjCoeff);
379         int numberColumns = newSolver->getNumCols();
380         int numFixed = 0;
381 
382         // Fix the first fixThreshold number of integer vars
383         // that are satisfied
384         for (int iColumn = 0; iColumn < numberColumns; iColumn++) {
385           if (newSolver->isInteger(iColumn)) {
386             double value = solutions.solution[k][iColumn];
387             double intValue = floor(value + 0.5);
388             if (fabs(value - intValue) < 1.0e-5) {
389               newSolver->setColLower(iColumn, intValue);
390               newSolver->setColUpper(iColumn, intValue);
391               numFixed++;
392               if (numFixed > numInt - fixThreshold)
393                 break;
394             }
395           }
396         }
397         COIN_DETAIL_PRINT(printf("numFixed: %d\n", numFixed));
398         COIN_DETAIL_PRINT(printf("fixThreshold: %f\n", fixThreshold));
399         COIN_DETAIL_PRINT(printf("numInt: %d\n", numInt));
400         double *newSolution = new double[numCols];
401         double newSolutionValue;
402 
403         // Call smallBB on the modified problem
404         int returnCode = smallBranchAndBound(newSolver, maxNode, newSolution,
405           newSolutionValue, cutoff, "mini");
406 
407         // If smallBB found a solution, update the better
408         // solution and solutionValue (we gave smallBB our
409         // cutoff, so it only finds improving solutions)
410         if (returnCode == 1 || returnCode == 3) {
411           numFeasibles++;
412           solutionValue = newSolutionValue;
413           for (int m = 0; m < numCols; m++)
414             betterSolution[m] = newSolution[m];
415           COIN_DETAIL_PRINT(printf("cutoff: %f\n", newSolutionValue));
416           COIN_DETAIL_PRINT(printf("time: %.2lf\n", CoinCpuTime() - start));
417         }
418         didMiniBB = 1;
419         COIN_DETAIL_PRINT(printf("returnCode: %d\n", returnCode));
420 
421         //Update sumReturnCode array
422         for (int iRC = 0; iRC < 6; iRC++) {
423           if (iRC == returnCode + 1)
424             sumReturnCode[iRC]++;
425           else
426             sumReturnCode[iRC] = 0;
427         }
428         if (returnCode != 0)
429           sumReturnCode[7] = 0;
430         else
431           sumReturnCode[7]++;
432         if (returnCode == 1 || returnCode == 3) {
433           cutoff = newSolutionValue;
434           clpSolver->addRow(numCols, addRowIndex, originalObjCoeff, -COIN_DBL_MAX, cutoff);
435           COIN_DETAIL_PRINT(printf("******************\n\n*****************\n"));
436         }
437         break;
438       }
439     }
440 
441     if (!didMiniBB && solutions.numberSolutions - startIndex > 0) {
442       sumReturnCode[5]++;
443       for (int iRC = 0; iRC < 5; iRC++)
444         sumReturnCode[iRC] = 0;
445     }
446 
447     //Change "fixThreshold" if needed
448     // using the data we've recorded in sumReturnCode
449     if (sumReturnCode[1] >= 3)
450       fixThreshold -= fixThresholdChange;
451     if (sumReturnCode[7] >= 3 && changeMaxNode) {
452       maxNode *= 5;
453       changeMaxNode = 0;
454     }
455     if (sumReturnCode[3] >= 3 && fixThreshold < 0.95 * numInt)
456       fixThreshold += fixThresholdChange;
457     if (sumReturnCode[5] >= 4)
458       fixThreshold += fixThresholdChange;
459     if (sumReturnCode[0] > 3)
460       fixThreshold -= fixThresholdChange;
461 
462     startIndex = solutions.numberSolutions;
463 
464     //Check if the maximum iterations limit is reached
465     // rlh: Ask John how this is working with the change to trustedUserPtr.
466     if (solutions.numberSolutions > 20000)
467       break;
468 
469     // The first time in this loop PF solves orig LP.
470 
471     //Generate the random objective function
472     randNum = rand() % 10 + 1;
473     randNum = fmod(randNum, 2);
474     for (int j = 0; j < numCols; j++) {
475       if (randNum == 1)
476         if (fabs(matrix[index[i]][j]) < 1e-6)
477           newObj[j] = 0.1;
478         else
479           newObj[j] = matrix[index[i]][j] * 1.1;
480       else if (fabs(matrix[index[i]][j]) < 1e-6)
481         newObj[j] = -0.1;
482       else
483         newObj[j] = matrix[index[i]][j] * 0.9;
484     }
485     clpSolver->setObjective(newObj);
486     if (rowSense[i] == 'L')
487       clpSolver->setObjSense(-1);
488     else
489       // Todo #1: We don't need to solve the LPs to optimality.
490       // We just need corner points.
491       // There's a problem in stopping Clp that needs to be looked
492       // into. So for now, we solve optimality.
493       clpSolver->setObjSense(1);
494     //	  simplex->setMaximumIterations(100);
495     clpSolver->getModelPtr()->primal(1);
496     //	  simplex->setMaximumIterations(100000);
497 #ifdef COIN_DETAIL
498     printf("cutoff: %f\n", cutoff);
499     printf("time: %.2f\n", CoinCpuTime() - start);
500     for (int iRC = 0; iRC < 8; iRC++)
501       printf("%d ", sumReturnCode[iRC]);
502     printf("\nfixThreshold: %f\n", fixThreshold);
503     printf("numInt: %d\n", numInt);
504     printf("\n---------------------------------------------------------------- %d\n", i);
505 #endif
506 
507     //temp:
508     if (i > 3)
509       break;
510   }
511 
512   COIN_DETAIL_PRINT(printf("Best Feasible Found: %f\n", cutoff));
513   COIN_DETAIL_PRINT(printf("Total time: %.2f\n", CoinCpuTime() - start));
514 
515   if (numFeasibles == 0) {
516     return 0;
517   }
518 
519   // We found something better
520   std::cout << "See you soon! You're leaving the Pivot-and-Fix Heuristic" << std::endl;
521   std::cout << std::endl;
522 
523   return 1;
524 #endif
525 
526   return 0;
527 }
528 
529 // update model
setModel(CbcModel *)530 void CbcHeuristicPivotAndFix::setModel(CbcModel *)
531 {
532   // probably same as resetModel
533 }
534 
535 /* vi: softtabstop=2 shiftwidth=2 expandtab tabstop=2
536 */
537