/* First tries setting a variable to better value. If feasible then tries setting others. If not feasible then tries swaps Returns 1 if solution, 0 if not The main body of this routine implements an O((q^2)/2) brute force search around the current solution, for q = number of integer variables. Call this the inc/dec heuristic. For each integer variable x<i>, first decrement the value. Then, for integer variables x<i+1>, ..., x<q-1>, try increment and decrement. If one of these permutations produces a better solution, remember it. Then repeat, with x<i> incremented. If we find a better solution, update our notion of current solution and continue. The net effect is a greedy walk: As each improving pair is found, the current solution is updated and the search continues from this updated solution. Way down at the end, we call solutionFix, which will create a drastically restricted problem based on variables marked as used, then do mini-BaC on the restricted problem. This can occur even if we don't try the inc/dec heuristic. This would be more obvious if the inc/dec heuristic were broken out as a separate routine and solutionFix had a name that reflected where it was headed. The return code of 0 is grossly overloaded, because it maps to a return code of 0 from solutionFix, which is itself grossly overloaded. See comments in solutionFix and in CbcHeuristic::smallBranchAndBound. */ int CbcHeuristicLocal::solution(double & solutionValue, double * betterSolution) { /* Execute only if a new solution has been discovered since the last time we were called. */ numCouldRun_++; // See if frequency kills off idea int swap = swap_%100; int skip = swap_/100; int nodeCount = model_->getNodeCount(); if (nodeCount<lastRunDeep_+skip && nodeCount != lastRunDeep_+1) return 0; if (numberSolutions_ == model_->getSolutionCount() && (numberSolutions_ == howOftenShallow_ || nodeCount < lastRunDeep_+2*skip)) return 0; howOftenShallow_ = numberSolutions_; numberSolutions_ = model_->getSolutionCount(); if (nodeCount<lastRunDeep_+skip ) return 0; lastRunDeep_ = nodeCount; howOftenShallow_ = numberSolutions_; if ((swap%10) == 2) { // try merge return solutionFix( solutionValue, betterSolution, NULL); } /* Exclude long (column), thin (row) systems. Given the n^2 nature of the search, more than 100,000 columns could get expensive. But I don't yet see the rationale for the second part of the condition (cols > 10*rows). And cost is proportional to number of integer variables --- shouldn't we use that? Why wait until we have more than one solution? */ if ((model_->getNumCols() > 100000 && model_->getNumCols() > 10*model_->getNumRows()) || numberSolutions_ <= 1) return 0; // probably not worth it // worth trying OsiSolverInterface * solver = model_->solver(); const double * rowLower = solver->getRowLower(); const double * rowUpper = solver->getRowUpper(); const double * solution = model_->bestSolution(); /* Shouldn't this test be redundant if we've already checked that numberSolutions_ > 1? Stronger: shouldn't this be an assertion? */ if (!solution) return 0; // No solution found yet const double * objective = solver->getObjCoefficients(); double primalTolerance; solver->getDblParam(OsiPrimalTolerance, primalTolerance); int numberRows = matrix_.getNumRows(); int numberIntegers = model_->numberIntegers(); const int * integerVariable = model_->integerVariable(); int i; double direction = solver->getObjSense(); double newSolutionValue = model_->getObjValue() * direction; int returnCode = 0; numRuns_++; // Column copy const double * element = matrix_.getElements(); const int * row = matrix_.getIndices(); const CoinBigIndex * columnStart = matrix_.getVectorStarts(); const int * columnLength = matrix_.getVectorLengths(); // Get solution array for heuristic solution int numberColumns = solver->getNumCols(); double * newSolution = new double [numberColumns]; memcpy(newSolution, solution, numberColumns*sizeof(double)); #ifdef LOCAL_FIX_CONTINUOUS // mark continuous used const double * columnLower = solver->getColLower(); for (int iColumn = 0; iColumn < numberColumns; iColumn++) { if (!solver->isInteger(iColumn)) { if (solution[iColumn] > columnLower[iColumn] + 1.0e-8) used_[iColumn] = numberSolutions_; } } #endif // way is 1 if down possible, 2 if up possible, 3 if both possible char * way = new char[numberIntegers]; // corrected costs double * cost = new double[numberIntegers]; // for array to mark infeasible rows after iColumn branch char * mark = new char[numberRows]; memset(mark, 0, numberRows); // space to save values so we don't introduce rounding errors double * save = new double[numberRows]; /* Force variables within their original bounds, then to the nearest integer. Overall, we seem to be prepared to cope with noninteger bounds. Is this necessary? Seems like we'd be better off to force the bounds to integrality as part of preprocessing. More generally, why do we need to do this? This solution should have been cleaned and checked when it was accepted as a solution! Once the value is set, decide whether we can move up or down. The only place that used_ is used is in solutionFix; if a variable is not flagged as used, it will be fixed (at lower bound). Why the asymmetric treatment? This makes some sense for binary variables (for which there are only two options). But for general integer variables, why not make a similar test against the original upper bound? */ // clean solution for (i = 0; i < numberIntegers; i++) { int iColumn = integerVariable[i]; const OsiObject * object = model_->object(i); // get original bounds double originalLower; double originalUpper; getIntegerInformation( object, originalLower, originalUpper); double value = newSolution[iColumn]; if (value < originalLower) { value = originalLower; newSolution[iColumn] = value; } else if (value > originalUpper) { value = originalUpper; newSolution[iColumn] = value; } double nearest = floor(value + 0.5); //assert(fabs(value-nearest)<10.0*primalTolerance); value = nearest; newSolution[iColumn] = nearest; // if away from lower bound mark that fact if (nearest > originalLower) { used_[iColumn] = numberSolutions_; } cost[i] = direction * objective[iColumn]; /* Given previous computation we're checking that value is at least 1 away from the original bounds. */ int iway = 0; if (value > originalLower + 0.5) iway = 1; if (value < originalUpper - 0.5) iway |= 2; way[i] = static_cast<char>(iway); } /* Calculate lhs of each constraint for groomed solution. */ // get row activities double * rowActivity = new double[numberRows]; memset(rowActivity, 0, numberRows*sizeof(double)); for (i = 0; i < numberColumns; i++) { int j; double value = newSolution[i]; if (value) { for (j = columnStart[i]; j < columnStart[i] + columnLength[i]; j++) { int iRow = row[j]; rowActivity[iRow] += value * element[j]; } } } /* Check that constraints are satisfied. For small infeasibility, force the activity within bound. Again, why is this necessary if the current solution was accepted as a valid solution? Why are we scanning past the first unacceptable constraint? */ // check was feasible - if not adjust (cleaning may move) // if very infeasible then give up bool tryHeuristic = true; for (i = 0; i < numberRows; i++) { if (rowActivity[i] < rowLower[i]) { if (rowActivity[i] < rowLower[i] - 10.0*primalTolerance) tryHeuristic = false; rowActivity[i] = rowLower[i]; } else if (rowActivity[i] > rowUpper[i]) { if (rowActivity[i] < rowUpper[i] + 10.0*primalTolerance) tryHeuristic = false; rowActivity[i] = rowUpper[i]; } } /* This bit of code is not quite totally redundant: it'll bail at 10,000 instead of 100,000. Potentially we can do a lot of work to get here, only to abandon it. */ // Switch off if may take too long if (model_->getNumCols() > 10000 && model_->getNumCols() > 10*model_->getNumRows()) tryHeuristic = false; /* Try the inc/dec heuristic? */ if (tryHeuristic) { // total change in objective double totalChange = 0.0; // local best change in objective double bestChange = 0.0; // maybe just do 1000 int maxIntegers = numberIntegers; if (((swap/10) &1) != 0) { maxIntegers = CoinMin(1000,numberIntegers); } /* Outer loop to walk integer variables. Call the current variable x<i>. At the end of this loop, bestChange will contain the best (negative) change in the objective for any single pair. The trouble is, we're limited to monotonically increasing improvement. Suppose we discover an improvement of 10 for some pair. If, later in the search, we discover an improvement of 9 for some other pair, we will not use it. That seems wasteful. */ for (i = 0; i < numberIntegers; i++) { int iColumn = integerVariable[i]; bestChange = 0.0; int endInner = CoinMin(numberIntegers,i+maxIntegers); double objectiveCoefficient = cost[i]; int k; int j; int goodK = -1; int wayK = -1, wayI = -1; /* Try decrementing x<i>. */ if ((way[i]&1) != 0) { int numberInfeasible = 0; /* Adjust row activities where x<i> has a nonzero coefficient. Save the old values for restoration. Mark any rows that become infeasible as a result of the decrement. */ // save row activities and adjust for (j = columnStart[iColumn]; j < columnStart[iColumn] + columnLength[iColumn]; j++) { int iRow = row[j]; save[iRow] = rowActivity[iRow]; rowActivity[iRow] -= element[j]; if (rowActivity[iRow] < rowLower[iRow] - primalTolerance || rowActivity[iRow] > rowUpper[iRow] + primalTolerance) { // mark row mark[iRow] = 1; numberInfeasible++; } } /* Run through the remaining integer variables. Try increment and decrement on each one. If the potential objective change is better than anything we've seen so far, do a full evaluation of x<k> in that direction. If we can repair all infeasibilities introduced by pushing x<i> down, we have a winner. Remember the best variable, and the direction for x<i> and x<k>. */ // try down for (k = i + 1; k < endInner; k++) { if ((way[k]&1) != 0) { // try down if (-objectiveCoefficient - cost[k] < bestChange) { // see if feasible down bool good = true; int numberMarked = 0; int kColumn = integerVariable[k]; for (j = columnStart[kColumn]; j < columnStart[kColumn] + columnLength[kColumn]; j++) { int iRow = row[j]; double newValue = rowActivity[iRow] - element[j]; if (newValue < rowLower[iRow] - primalTolerance || newValue > rowUpper[iRow] + primalTolerance) { good = false; break; } else if (mark[iRow]) { // made feasible numberMarked++; } } if (good && numberMarked == numberInfeasible) { // better solution goodK = k; wayK = -1; wayI = -1; bestChange = -objectiveCoefficient - cost[k]; } } } if ((way[k]&2) != 0) { // try up if (-objectiveCoefficient + cost[k] < bestChange) { // see if feasible up bool good = true; int numberMarked = 0; int kColumn = integerVariable[k]; for (j = columnStart[kColumn]; j < columnStart[kColumn] + columnLength[kColumn]; j++) { int iRow = row[j]; double newValue = rowActivity[iRow] + element[j]; if (newValue < rowLower[iRow] - primalTolerance || newValue > rowUpper[iRow] + primalTolerance) { good = false; break; } else if (mark[iRow]) { // made feasible numberMarked++; } } if (good && numberMarked == numberInfeasible) { // better solution goodK = k; wayK = 1; wayI = -1; bestChange = -objectiveCoefficient + cost[k]; } } } } /* Remove effect of decrementing x<i> by restoring original lhs values. */ // restore row activities for (j = columnStart[iColumn]; j < columnStart[iColumn] + columnLength[iColumn]; j++) { int iRow = row[j]; rowActivity[iRow] = save[iRow]; mark[iRow] = 0; } } /* Try to increment x<i>. Actions as for decrement. */ if ((way[i]&2) != 0) { int numberInfeasible = 0; // save row activities and adjust for (j = columnStart[iColumn]; j < columnStart[iColumn] + columnLength[iColumn]; j++) { int iRow = row[j]; save[iRow] = rowActivity[iRow]; rowActivity[iRow] += element[j]; if (rowActivity[iRow] < rowLower[iRow] - primalTolerance || rowActivity[iRow] > rowUpper[iRow] + primalTolerance) { // mark row mark[iRow] = 1; numberInfeasible++; } } // try up for (k = i + 1; k < endInner; k++) { if ((way[k]&1) != 0) { // try down if (objectiveCoefficient - cost[k] < bestChange) { // see if feasible down bool good = true; int numberMarked = 0; int kColumn = integerVariable[k]; for (j = columnStart[kColumn]; j < columnStart[kColumn] + columnLength[kColumn]; j++) { int iRow = row[j]; double newValue = rowActivity[iRow] - element[j]; if (newValue < rowLower[iRow] - primalTolerance || newValue > rowUpper[iRow] + primalTolerance) { good = false; break; } else if (mark[iRow]) { // made feasible numberMarked++; } } if (good && numberMarked == numberInfeasible) { // better solution goodK = k; wayK = -1; wayI = 1; bestChange = objectiveCoefficient - cost[k]; } } } if ((way[k]&2) != 0) { // try up if (objectiveCoefficient + cost[k] < bestChange) { // see if feasible up bool good = true; int numberMarked = 0; int kColumn = integerVariable[k]; for (j = columnStart[kColumn]; j < columnStart[kColumn] + columnLength[kColumn]; j++) { int iRow = row[j]; double newValue = rowActivity[iRow] + element[j]; if (newValue < rowLower[iRow] - primalTolerance || newValue > rowUpper[iRow] + primalTolerance) { good = false; break; } else if (mark[iRow]) { // made feasible numberMarked++; } } if (good && numberMarked == numberInfeasible) { // better solution goodK = k; wayK = 1; wayI = 1; bestChange = objectiveCoefficient + cost[k]; } } } } // restore row activities for (j = columnStart[iColumn]; j < columnStart[iColumn] + columnLength[iColumn]; j++) { int iRow = row[j]; rowActivity[iRow] = save[iRow]; mark[iRow] = 0; } } /* We've found a pair x<i> and x<k> which produce a better solution. Update our notion of current solution to match. Why does this not update newSolutionValue? */ if (goodK >= 0) { // we found something - update solution for (j = columnStart[iColumn]; j < columnStart[iColumn] + columnLength[iColumn]; j++) { int iRow = row[j]; rowActivity[iRow] += wayI * element[j]; } newSolution[iColumn] += wayI; int kColumn = integerVariable[goodK]; for (j = columnStart[kColumn]; j < columnStart[kColumn] + columnLength[kColumn]; j++) { int iRow = row[j]; rowActivity[iRow] += wayK * element[j]; } newSolution[kColumn] += wayK; /* Adjust motion range for x<k>. We may have banged up against a bound with that last move. */ // See if k can go further ? const OsiObject * object = model_->object(goodK); // get original bounds double originalLower; double originalUpper; getIntegerInformation( object, originalLower, originalUpper); double value = newSolution[kColumn]; int iway = 0; if (value > originalLower + 0.5) iway = 1; if (value < originalUpper - 0.5) iway |= 2; way[goodK] = static_cast<char>(iway); totalChange += bestChange; } } /* End of loop to try increment/decrement of integer variables. newSolutionValue does not necessarily match the current newSolution, and bestChange simply reflects the best single change. Still, that's sufficient to indicate that there's been at least one change. Check that we really do have a valid solution. */ if (totalChange + newSolutionValue < solutionValue) { // paranoid check memset(rowActivity, 0, numberRows*sizeof(double)); for (i = 0; i < numberColumns; i++) { int j; double value = newSolution[i]; if (value) { for (j = columnStart[i]; j < columnStart[i] + columnLength[i]; j++) { int iRow = row[j]; rowActivity[iRow] += value * element[j]; } } } int numberBad = 0; double sumBad = 0.0; // check was approximately feasible for (i = 0; i < numberRows; i++) { if (rowActivity[i] < rowLower[i]) { sumBad += rowLower[i] - rowActivity[i]; if (rowActivity[i] < rowLower[i] - 10.0*primalTolerance) numberBad++; } else if (rowActivity[i] > rowUpper[i]) { sumBad += rowUpper[i] - rowActivity[i]; if (rowActivity[i] > rowUpper[i] + 10.0*primalTolerance) numberBad++; } } if (!numberBad) { for (i = 0; i < numberIntegers; i++) { int iColumn = integerVariable[i]; const OsiObject * object = model_->object(i); // get original bounds double originalLower; double originalUpper; getIntegerInformation( object, originalLower, originalUpper); double value = newSolution[iColumn]; // if away from lower bound mark that fact if (value > originalLower) { used_[iColumn] = numberSolutions_; } } /* Copy the solution to the array returned to the client. Grab a basis from the solver (which, if it exists, is almost certainly infeasible, but it should be ok for a dual start). The value returned as solutionValue is conservative because of handling of newSolutionValue and bestChange, as described above. */ // new solution memcpy(betterSolution, newSolution, numberColumns*sizeof(double)); CoinWarmStartBasis * basis = dynamic_cast<CoinWarmStartBasis *>(solver->getWarmStart()) ; if (basis) { model_->setBestSolutionBasis(* basis); delete basis; } returnCode = 1; solutionValue = newSolutionValue + bestChange; } else { // bad solution - should not happen so debug if see message COIN_DETAIL_PRINT(printf("Local search got bad solution with %d infeasibilities summing to %g\n", numberBad, sumBad)); } } } /* We're done. Clean up. */ delete [] newSolution; delete [] rowActivity; delete [] way; delete [] cost; delete [] save; delete [] mark; /* Do we want to try swapping values between solutions? swap_ is set elsewhere; it's not adjusted during heuristic execution. Again, redundant test. We shouldn't be here if numberSolutions_ = 1. */ if (numberSolutions_ > 1 && (swap%10) == 1) { // try merge int returnCode2 = solutionFix( solutionValue, betterSolution, NULL); if (returnCode2) returnCode = 1; } return returnCode; }
/* First tries setting a variable to better value. If feasible then tries setting others. If not feasible then tries swaps Returns 1 if solution, 0 if not */ int CbcHeuristicVND::solution(double & solutionValue, double * betterSolution) { numCouldRun_++; int returnCode = 0; const double * bestSolution = model_->bestSolution(); if (!bestSolution) return 0; // No solution found yet #ifdef HEURISTIC_INFORM printf("Entering heuristic %s - nRuns %d numCould %d when %d\n", heuristicName(),numRuns_,numCouldRun_,when_); #endif if (numberSolutions_ < model_->getSolutionCount()) { // new solution - add info numberSolutions_ = model_->getSolutionCount(); int numberIntegers = model_->numberIntegers(); const int * integerVariable = model_->integerVariable(); int i; for (i = 0; i < numberIntegers; i++) { int iColumn = integerVariable[i]; const OsiObject * object = model_->object(i); // get original bounds double originalLower; double originalUpper; getIntegerInformation( object, originalLower, originalUpper); double value = bestSolution[iColumn]; if (value < originalLower) { value = originalLower; } else if (value > originalUpper) { value = originalUpper; } } } int numberNodes = model_->getNodeCount(); if (howOften_ == 100) { if (numberNodes < lastNode_ + 12) return 0; // Do at 50 and 100 if ((numberNodes > 40 && numberNodes <= 50) || (numberNodes > 90 && numberNodes < 100)) numberNodes = howOften_; } if ((numberNodes % howOften_) == 0 && (model_->getCurrentPassNumber() <= 1 || model_->getCurrentPassNumber() == 999999)) { lastNode_ = model_->getNodeCount(); OsiSolverInterface * solver = model_->solver(); int numberIntegers = model_->numberIntegers(); const int * integerVariable = model_->integerVariable(); const double * currentSolution = solver->getColSolution(); OsiSolverInterface * newSolver = cloneBut(3); // was model_->continuousSolver()->clone(); //const double * colLower = newSolver->getColLower(); //const double * colUpper = newSolver->getColUpper(); double primalTolerance; solver->getDblParam(OsiPrimalTolerance, primalTolerance); // Sort on distance double * distance = new double [numberIntegers]; int * which = new int [numberIntegers]; int i; int nFix = 0; double tolerance = 10.0 * primalTolerance; for (i = 0; i < numberIntegers; i++) { int iColumn = integerVariable[i]; const OsiObject * object = model_->object(i); // get original bounds double originalLower; double originalUpper; getIntegerInformation( object, originalLower, originalUpper); double valueInt = bestSolution[iColumn]; if (valueInt < originalLower) { valueInt = originalLower; } else if (valueInt > originalUpper) { valueInt = originalUpper; } baseSolution_[iColumn] = currentSolution[iColumn]; distance[i] = fabs(currentSolution[iColumn] - valueInt); which[i] = i; if (fabs(currentSolution[iColumn] - valueInt) < tolerance) nFix++; } CoinSort_2(distance, distance + numberIntegers, which); nDifferent_ = numberIntegers - nFix; stepSize_ = nDifferent_ / 10; k_ = stepSize_; //nFix = numberIntegers-stepSize_; for (i = 0; i < nFix; i++) { int j = which[i]; int iColumn = integerVariable[j]; const OsiObject * object = model_->object(i); // get original bounds double originalLower; double originalUpper; getIntegerInformation( object, originalLower, originalUpper); double valueInt = bestSolution[iColumn]; if (valueInt < originalLower) { valueInt = originalLower; } else if (valueInt > originalUpper) { valueInt = originalUpper; } double nearest = floor(valueInt + 0.5); newSolver->setColLower(iColumn, nearest); newSolver->setColUpper(iColumn, nearest); } delete [] distance; delete [] which; if (nFix > numberIntegers / 5) { //printf("%d integers have samish value\n",nFix); returnCode = smallBranchAndBound(newSolver, numberNodes_, betterSolution, solutionValue, model_->getCutoff(), "CbcHeuristicVND"); if (returnCode < 0) returnCode = 0; // returned on size else numRuns_++; if ((returnCode&1) != 0) numberSuccesses_++; //printf("return code %d",returnCode); if ((returnCode&2) != 0) { // could add cut returnCode &= ~2; //printf("could add cut with %d elements (if all 0-1)\n",nFix); } else { //printf("\n"); } numberTries_++; if ((numberTries_ % 10) == 0 && numberSuccesses_*3 < numberTries_) howOften_ += static_cast<int> (howOften_ * decayFactor_); } delete newSolver; } return returnCode; }
// This version fixes stuff and does IP int CbcHeuristicLocal::solutionFix(double & objectiveValue, double * newSolution, const int * /*keep*/) { /* If when is set to off (0), or set to root (1) and we're not at the root, return. If this heuristic discovered the current solution, don't continue. */ numCouldRun_++; // See if to do if (!when() || (when() == 1 && model_->phase() != 1)) return 0; // switched off // Don't do if it was this heuristic which found solution! if (this == model_->lastHeuristic()) return 0; /* Load up a new solver with the solution. Why continuousSolver(), as opposed to solver()? */ OsiSolverInterface * newSolver = model_->continuousSolver()->clone(); const double * colLower = newSolver->getColLower(); //const double * colUpper = newSolver->getColUpper(); int numberIntegers = model_->numberIntegers(); const int * integerVariable = model_->integerVariable(); /* The net effect here is that anything that hasn't moved from its lower bound will be fixed at lower bound. See comments in solution() w.r.t. asymmetric treatment of upper and lower bounds. */ int i; int nFix = 0; for (i = 0; i < numberIntegers; i++) { int iColumn = integerVariable[i]; const OsiObject * object = model_->object(i); // get original bounds double originalLower; double originalUpper; getIntegerInformation( object, originalLower, originalUpper); newSolver->setColLower(iColumn, CoinMax(colLower[iColumn], originalLower)); if (!used_[iColumn]) { newSolver->setColUpper(iColumn, colLower[iColumn]); nFix++; } } /* Try a `small' branch-and-bound search. The notion here is that we've fixed a lot of variables and reduced the amount of `free' problem to a point where a small BaB search will suffice to fully explore the remaining problem. This routine will execute integer presolve, then call branchAndBound to do the actual search. */ int returnCode = 0; #ifdef CLP_INVESTIGATE2 printf("Fixing %d out of %d (%d continuous)\n", nFix, numberIntegers, newSolver->getNumCols() - numberIntegers); #endif if (nFix*10 <= numberIntegers) { // see if we can fix more int * which = new int [2*(numberIntegers-nFix)]; int * sort = which + (numberIntegers - nFix); int n = 0; for (i = 0; i < numberIntegers; i++) { int iColumn = integerVariable[i]; if (used_[iColumn]) { which[n] = iColumn; sort[n++] = used_[iColumn]; } } CoinSort_2(sort, sort + n, which); // only half fixed in total n = CoinMin(n, numberIntegers / 2 - nFix); int allow = CoinMax(numberSolutions_ - 2, sort[0]); int nFix2 = 0; for (i = 0; i < n; i++) { int iColumn = integerVariable[i]; if (used_[iColumn] <= allow) { newSolver->setColUpper(iColumn, colLower[iColumn]); nFix2++; } else { break; } } delete [] which; nFix += nFix2; #ifdef CLP_INVESTIGATE2 printf("Number fixed increased from %d to %d\n", nFix - nFix2, nFix); #endif } if (nFix*10 > numberIntegers) { returnCode = smallBranchAndBound(newSolver, numberNodes_, newSolution, objectiveValue, objectiveValue, "CbcHeuristicLocal"); /* -2 is return due to user event, and -1 is overloaded with what look to be two contradictory meanings. */ if (returnCode < 0) { returnCode = 0; // returned on size int numberColumns = newSolver->getNumCols(); int numberContinuous = numberColumns - numberIntegers; if (numberContinuous > 2*numberIntegers && nFix*10 < numberColumns) { #define LOCAL_FIX_CONTINUOUS #ifdef LOCAL_FIX_CONTINUOUS //const double * colUpper = newSolver->getColUpper(); const double * colLower = newSolver->getColLower(); int nAtLb = 0; //double sumDj=0.0; const double * dj = newSolver->getReducedCost(); double direction = newSolver->getObjSense(); for (int iColumn = 0; iColumn < numberColumns; iColumn++) { if (!newSolver->isInteger(iColumn)) { if (!used_[iColumn]) { //double djValue = dj[iColumn]*direction; nAtLb++; //sumDj += djValue; } } } if (nAtLb) { // fix some continuous double * sort = new double[nAtLb]; int * which = new int [nAtLb]; //double threshold = CoinMax((0.01*sumDj)/static_cast<double>(nAtLb),1.0e-6); int nFix2 = 0; for (int iColumn = 0; iColumn < numberColumns; iColumn++) { if (!newSolver->isInteger(iColumn)) { if (!used_[iColumn]) { double djValue = dj[iColumn] * direction; if (djValue > 1.0e-6) { sort[nFix2] = -djValue; which[nFix2++] = iColumn; } } } } CoinSort_2(sort, sort + nFix2, which); int divisor = 2; nFix2 = CoinMin(nFix2, (numberColumns - nFix) / divisor); for (int i = 0; i < nFix2; i++) { int iColumn = which[i]; newSolver->setColUpper(iColumn, colLower[iColumn]); } delete [] sort; delete [] which; #ifdef CLP_INVESTIGATE2 printf("%d integers have zero value, and %d continuous fixed at lb\n", nFix, nFix2); #endif returnCode = smallBranchAndBound(newSolver, numberNodes_, newSolution, objectiveValue, objectiveValue, "CbcHeuristicLocal"); if (returnCode < 0) returnCode = 0; // returned on size } #endif } } } /* If the result is complete exploration with a solution (3) or proven infeasibility (2), we could generate a cut (the AI folks would call it a nogood) to prevent us from going down this route in the future. */ if ((returnCode&2) != 0) { // could add cut returnCode &= ~2; } delete newSolver; return returnCode; }