limp-cbc-0.3.2.0: cbits/coin/CbcTree.cpp
/* $Id: CbcTree.cpp 1813 2012-11-22 19:00:22Z forrest $ */
// Copyright (C) 2004, International Business Machines
// Corporation and others. All Rights Reserved.
// This code is licensed under the terms of the Eclipse Public License (EPL).
#include "CbcModel.hpp"
#include "CbcNode.hpp"
#include "CbcTree.hpp"
#include "CbcCountRowCut.hpp"
#include "CbcCompareActual.hpp"
#include "CbcBranchActual.hpp"
#if CBC_DEBUG_HEAP > 0
namespace {
/*
The next few methods test that the heap property (parent equal or better
than either child) is maintained in the heap. Originally created to sort out
why `cbc -unitTest' triggered an `Invalid heap' error in a MSVS debug build.
*/
/*
Predicate test. The parent should be better or equal to the child. Since the
predicate comparison_(x,y) returns true if y (child) is strictly better,
we want failure on the initial test. Clearly, success for comparison(x,y)
and comparison(y,x) is also a failure.
Returns true if the predicate passes, false if it fails.
*/
bool check_pred (CbcCompareBase &pred, CbcNode *parent, CbcNode *child)
{
if (parent == 0 || child == 0) return (false) ;
if (!pred(parent,child))
return (true) ;
else if (pred(child,parent))
std::cout
<< " Heap predicate failure! (x<y) && (y<x)!" << std::endl ;
return (false) ;
}
} // end file-local namespace
/*
Check for the heap property: the parent is better than or equal to
either child.
The heap is a binary tree, stored in the vector layer by layer. By advancing
parent at half the rate of child (aka curNode), we check both children
of a given parent. (Draw yourself a picture; it'll help.) An empty heap
is trivially valid. A heap with no predicate is trivially invalid.
TODO: The heap -> vector mapping assumed here is valid for the MSVS heap
implementation. No guarantee it's valid elsewhere.
*/
void CbcTree::validateHeap ()
{
if (comparison_.test_ == 0) {
std::cout
<< " Invalid heap (no predicate)!" << std::endl ;
return ;
}
std::vector<CbcNode *>::const_iterator curNode,lastNode ;
curNode = nodes_.begin() ;
lastNode = nodes_.end() ;
int curNdx = 0 ;
int parNdx = 0 ;
if (curNode == lastNode) return ;
if (*curNode == 0) {
std::cout
<< " Invalid heap[" << curNdx << "] (null entry)!" << std::endl ;
}
std::vector<CbcNode *>::const_iterator parent ;
std::vector<CbcNode*>::const_iterator &child = curNode ;
for (parent = curNode ; ++curNode != lastNode ; ++parent, ++parNdx) {
curNdx++ ;
if (*parent == 0) {
std::cout
<< " Invalid heap[" << parNdx << "] (parent null entry)!" << std::endl ;
curNode++ ;
curNdx++ ;
continue ;
}
if (*curNode == 0) {
std::cout
<< " Invalid heap[" << curNdx << "] (left child null entry)!"
<< std::endl ;
} else {
if (!check_pred(*comparison_.test_,*parent,*child)) {
std::cout
<< " Invalid heap (left child better)!" << std::endl ;
CbcNode *node = *parent ;
std::cout
<< " Parent [" << parNdx << "] (" << std::hex << node << std::dec
<< ") unsat " << node->numberUnsatisfied() << ", depth "
<< node->depth() << ", obj " << node->objectiveValue() << "."
<< std::endl ;
node = *child ;
std::cout
<< " Child [" << curNdx << "] (" << std::hex << node << std::dec
<< ") unsat " << node->numberUnsatisfied() << ", depth "
<< node->depth() << ", obj " << node->objectiveValue() << "."
<< std::endl ;
}
}
curNode++ ;
curNdx++ ;
if (curNode == lastNode) break ;
if (*curNode == 0) {
std::cout
<< " Invalid heap[" << curNdx << "] (right child null entry)!"
<< std::endl ;
} else {
if (!check_pred(*comparison_.test_,*parent,*child)) {
std::cout
<< " Invalid heap (right child better)!" << std::endl ;
CbcNode *node = *parent ;
std::cout
<< " Parent [" << parNdx << "] (" << std::hex << node << std::dec
<< ") unsat " << node->numberUnsatisfied() << ", depth "
<< node->depth() << ", obj " << node->objectiveValue() << "."
<< std::endl ;
node = *child ;
std::cout
<< " Child [" << curNdx << "] (" << std::hex << node << std::dec
<< ") unsat " << node->numberUnsatisfied() << ", depth "
<< node->depth() << ", obj " << node->objectiveValue() << "."
<< std::endl ;
}
}
}
return ;
}
#endif // CBC_DEBUG_HEAP
CbcTree::CbcTree()
{
maximumNodeNumber_ = 0;
numberBranching_ = 0;
maximumBranching_ = 0;
branched_ = NULL;
newBound_ = NULL;
}
CbcTree::~CbcTree()
{
delete [] branched_;
delete [] newBound_;
}
// Copy constructor
CbcTree::CbcTree ( const CbcTree & rhs)
{
nodes_ = rhs.nodes_;
maximumNodeNumber_ = rhs.maximumNodeNumber_;
numberBranching_ = rhs.numberBranching_;
maximumBranching_ = rhs.maximumBranching_;
if (maximumBranching_ > 0) {
branched_ = CoinCopyOfArray(rhs.branched_, maximumBranching_);
newBound_ = CoinCopyOfArray(rhs.newBound_, maximumBranching_);
} else {
branched_ = NULL;
newBound_ = NULL;
}
}
// Assignment operator
CbcTree &
CbcTree::operator=(const CbcTree & rhs)
{
if (this != &rhs) {
nodes_ = rhs.nodes_;
maximumNodeNumber_ = rhs.maximumNodeNumber_;
delete [] branched_;
delete [] newBound_;
numberBranching_ = rhs.numberBranching_;
maximumBranching_ = rhs.maximumBranching_;
if (maximumBranching_ > 0) {
branched_ = CoinCopyOfArray(rhs.branched_, maximumBranching_);
newBound_ = CoinCopyOfArray(rhs.newBound_, maximumBranching_);
} else {
branched_ = NULL;
newBound_ = NULL;
}
}
return *this;
}
/*
Rebuild the heap.
*/
void CbcTree::rebuild ()
{
std::make_heap(nodes_.begin(), nodes_.end(), comparison_);
# if CBC_DEBUG_HEAP > 1
std::cout << " HEAP: rebuild complete." << std::endl ;
# endif
# if CBC_DEBUG_HEAP > 0
validateHeap() ;
# endif
}
// Adds branching information to complete state
void
CbcTree::addBranchingInformation(const CbcModel * model, const CbcNodeInfo * nodeInfo,
const double * currentLower,
const double * currentUpper)
{
const OsiBranchingObject * objA = nodeInfo->owner()->branchingObject();
const CbcIntegerBranchingObject * objBranch = dynamic_cast<const CbcIntegerBranchingObject *> (objA);
if (objBranch) {
const CbcObject * objB = objBranch->object();
const CbcSimpleInteger * obj = dynamic_cast<const CbcSimpleInteger *> (objB);
assert (obj);
int iColumn = obj->columnNumber();
const double * down = objBranch->downBounds();
const double * up = objBranch->upBounds();
assert (currentLower[iColumn] == down[0]);
assert (currentUpper[iColumn] == up[1]);
if (dynamic_cast<const CbcPartialNodeInfo *> (nodeInfo)) {
const CbcPartialNodeInfo * info = dynamic_cast<const CbcPartialNodeInfo *> (nodeInfo);
const double * newBounds = info->newBounds();
const int * variables = info->variables();
int numberChanged = info->numberChangedBounds();
for (int i = 0; i < numberChanged; i++) {
int jColumn = variables[i];
int kColumn = jColumn & (~0x80000000);
if (iColumn == kColumn) {
jColumn |= 0x40000000;
#ifndef NDEBUG
double value = newBounds[i];
if ((jColumn&0x80000000) == 0) {
assert (value == up[0]);
} else {
assert (value == down[1]);
}
#endif
}
if (numberBranching_ == maximumBranching_)
increaseSpace();
newBound_[numberBranching_] = static_cast<int> (newBounds[i]);
branched_[numberBranching_++] = jColumn;
}
} else {
const CbcFullNodeInfo * info = dynamic_cast<const CbcFullNodeInfo *> (nodeInfo);
int numberIntegers = model->numberIntegers();
const int * which = model->integerVariable();
const double * newLower = info->lower();
const double * newUpper = info->upper();
if (numberBranching_ == maximumBranching_)
increaseSpace();
assert (newLower[iColumn] == up[0] ||
newUpper[iColumn] == down[1]);
int jColumn = iColumn | 0x40000000;
if (newLower[iColumn] == up[0]) {
newBound_[numberBranching_] = static_cast<int> (up[0]);
} else {
newBound_[numberBranching_] = static_cast<int> (down[1]);
jColumn |= 0x80000000;
}
branched_[numberBranching_++] = jColumn;
for (int i = 0; i < numberIntegers; i++) {
int jColumn = which[i];
assert (currentLower[jColumn] == newLower[jColumn] ||
currentUpper[jColumn] == newUpper[jColumn]);
if (jColumn != iColumn) {
bool changed = false;
double value;
if (newLower[jColumn] > currentLower[jColumn]) {
value = newLower[jColumn];
changed = true;
} else if (newUpper[jColumn] < currentUpper[jColumn]) {
value = newUpper[jColumn];
jColumn |= 0x80000000;
changed = true;
}
if (changed) {
if (numberBranching_ == maximumBranching_)
increaseSpace();
newBound_[numberBranching_] = static_cast<int> (value);
branched_[numberBranching_++] = jColumn;
}
}
}
}
} else {
// switch off
delete [] branched_;
delete [] newBound_;
maximumBranching_ = -1;
branched_ = NULL;
newBound_ = NULL;
}
}
// Increase space for data
void
CbcTree::increaseSpace()
{
assert (numberBranching_ == maximumBranching_);
maximumBranching_ = (3 * maximumBranching_ + 10) >> 1;
unsigned int * temp1 = CoinCopyOfArrayPartial(branched_, maximumBranching_, numberBranching_);
delete [] branched_;
branched_ = temp1;
int * temp2 = CoinCopyOfArrayPartial(newBound_, maximumBranching_, numberBranching_);
delete [] newBound_;
newBound_ = temp2;
}
// Clone
CbcTree *
CbcTree::clone() const
{
return new CbcTree(*this);
}
#ifndef CBC_DUBIOUS_HEAP
/*
Set comparison predicate and re-sort the heap.
Note that common usage is to tweak the incumbent predicate and then
call this method to rebuild the heap. Hence we cannot check for heap
validity at entry. rebuild() will check on the way out, if
CBC_DEBUG_HEAP is set.
TODO: remove the call to cleanDive and put it somewhere appropriate.
*/
void
CbcTree::setComparison(CbcCompareBase &compare)
{
# if CBC_DEBUG_HEAP > 1
std::cout << " HEAP: resetting comparison predicate." << std::endl ;
# endif
comparison_.test_ = &compare;
/*
From a software engineering point of view, setComparison has no business
knowing anything about the comparison function. Need to look for a better
solution. Perhaps a callback comparable to newSolution, executing when
the comparison method is set (i.e., in setComparison).
-- lh, 100921 --
*/
CbcCompareDefault *compareD =
dynamic_cast<CbcCompareDefault *>(&compare);
if (compareD) {
// clean up diving
compareD->cleanDive();
}
rebuild() ;
}
// Return the top node of the heap
CbcNode *
CbcTree::top() const
{
return nodes_.front();
}
// Add a node to the heap
void
CbcTree::push(CbcNode * x)
{
x->setNodeNumber(maximumNodeNumber_);
maximumNodeNumber_++;
# if CBC_DEBUG_HEAP > 2
CbcNodeInfo *info = x->nodeInfo() ;
assert(info) ;
std::cout
<< " HEAP: Pushing node " << x->nodeNumber()
<< "(" << std::hex << x << std::dec << ") obj " << x->objectiveValue()
<< ", ref " << info->decrement(0)
<< ", todo " << info->numberBranchesLeft()
<< ", refd by " << info->numberPointingToThis() << "." << std::endl ;
assert(x->objectiveValue() != COIN_DBL_MAX);
# endif
# if CBC_DEBUG_HEAP > 0
validateHeap() ;
# endif
x->setOnTree(true);
nodes_.push_back(x);
std::push_heap(nodes_.begin(), nodes_.end(), comparison_);
# if CBC_DEBUG_HEAP > 0
validateHeap() ;
# endif
}
// Remove the top node from the heap
void
CbcTree::pop()
{
# if CBC_DEBUG_HEAP > 2
CbcNode *node = nodes_.front() ;
CbcNodeInfo *info = node->nodeInfo() ;
assert(info) ;
std::cout
<< " HEAP: Popping node " << node->nodeNumber()
<< "(" << std::hex << node << std::dec
<< ") obj " << node->objectiveValue()
<< ", ref " << info->decrement(0)
<< ", todo " << info->numberBranchesLeft()
<< ", refd by " << info->numberPointingToThis() << "." << std::endl ;
# endif
# if CBC_DEBUG_HEAP > 0
validateHeap() ;
# endif
nodes_.front()->setOnTree(false);
std::pop_heap(nodes_.begin(), nodes_.end(), comparison_);
nodes_.pop_back();
# if CBC_DEBUG_HEAP > 0
validateHeap() ;
# endif
}
// Test if empty *** note may be overridden
bool
CbcTree::empty()
{
return nodes_.empty();
}
/*
Return the best node from the heap.
Note that best is offered a chance (checkIsCutoff) to reevaluate
itself and make arbitrary changes. A caller should be prepared
to check that the returned node is acceptable.
There's quite a bit of suspect code here, much of it disabled in
some way. The net effect at present is to return the top node on
the heap after offering the node an opportunity to reevaluate itself.
Documentation for checkIsCutoff() puts no restrictions on allowable
changes. -- lh, 100921 --
*/
CbcNode *
CbcTree::bestNode(double cutoff)
{
# if CBC_DEBUG_HEAP > 0
validateHeap() ;
# endif
/*
This code is problematic. As of 100921, there's really no loop.
If front() == null, an assert will trigger. checkIsCutoff seems to be
work in progress; comments assert that it can make pretty much arbitrary
changes to best. If best can change its objective, there's a good
possibility the heap is invalid.
*/
CbcNode * best = NULL;
while (!best && nodes_.size()) {
best = nodes_.front();
if (best)
assert(best->objectiveValue() != COIN_DBL_MAX && best->nodeInfo());
if (best && best->objectiveValue() != COIN_DBL_MAX && best->nodeInfo())
assert (best->nodeInfo()->numberBranchesLeft());
if (best && best->objectiveValue() >= cutoff) {
// double check in case node can change its mind!
best->checkIsCutoff(cutoff);
}
if (!best || best->objectiveValue() >= cutoff) {
#ifdef JJF_ZERO
// take off
std::pop_heap(nodes_.begin(), nodes_.end(), comparison_);
nodes_.pop_back();
delete best;
best = NULL;
#else
// let code get rid of it
assert (best);
#endif
}
}
/*
See if the comparison object wants us to do a full scan with the
alternative criteria. The net effect is to confirm best by the
alternative criteria, or identify a competitor and erase it.
This code is problematic. Nulling an arbitrary node will in general
break the heap property. Disabled some time ago, as noted in several
places.
*/
if (false && best && comparison_.test_->fullScan()) {
CbcNode * saveBest = best;
size_t n = nodes_.size();
size_t iBest = -1;
for (size_t i = 0; i < n; i++) {
// temp
assert (nodes_[i]);
assert (nodes_[i]->nodeInfo());
if (nodes_[i] && nodes_[i]->objectiveValue() != COIN_DBL_MAX && nodes_[i]->nodeInfo())
assert (nodes_[i]->nodeInfo()->numberBranchesLeft());
if (nodes_[i] && nodes_[i]->objectiveValue() < cutoff
&& comparison_.alternateTest(best, nodes_[i])) {
best = nodes_[i];
iBest = i;
}
}
if (best == saveBest) {
// can pop
// take off
std::pop_heap(nodes_.begin(), nodes_.end(), comparison_);
nodes_.pop_back();
} else {
// make impossible
nodes_[iBest] = NULL;
}
} else if (best) {
# if CBC_DEBUG_HEAP > 2
CbcNode *node = nodes_.front() ;
CbcNodeInfo *info = node->nodeInfo() ;
assert(info) ;
std::cout
<< " bestNode: Popping node " << node->nodeNumber()
<< "(" << std::hex << node << std::dec
<< ") obj " << node->objectiveValue()
<< ", ref " << info->decrement(0)
<< ", todo " << info->numberBranchesLeft()
<< ", refd by " << info->numberPointingToThis() << "." << std::endl ;
# endif
// take off
std::pop_heap(nodes_.begin(), nodes_.end(), comparison_);
nodes_.pop_back();
}
#if CBC_DEBUG_HEAP > 0
validateHeap() ;
#endif
if (best)
best->setOnTree(false);
return best;
}
/*! \brief Prune the tree using an objective function cutoff
This routine removes all nodes with objective worse than the
specified cutoff value.
*/
void
CbcTree::cleanTree(CbcModel * model, double cutoff, double & bestPossibleObjective)
{
# if CBC_DEBUG_HEAP > 1
std::cout << " cleanTree: beginning clean." << std::endl ;
# endif
# if CBC_DEBUG_HEAP > 0
validateHeap() ;
# endif
int j;
int nNodes = size();
CbcNode ** nodeArray = new CbcNode * [nNodes];
int * depth = new int [nNodes];
int k = 0;
int kDelete = nNodes;
bestPossibleObjective = 1.0e100 ;
/*
Destructively scan the heap. Nodes to be retained go into the front of
nodeArray, nodes to be deleted into the back. Store the depth in a
correlated array for nodes to be deleted.
*/
for (j = 0; j < nNodes; j++) {
CbcNode * node = top();
pop();
double value = node ? node->objectiveValue() : COIN_DBL_MAX;
if (node && value >= cutoff) {
// double check in case node can change its mind!
value = node->checkIsCutoff(cutoff);
}
if (value >= cutoff || !node->active()) {
if (node) {
nodeArray[--kDelete] = node;
depth[kDelete] = node->depth();
}
} else {
bestPossibleObjective = CoinMin(bestPossibleObjective, value);
nodeArray[k++] = node;
}
}
/*
Rebuild the heap using the retained nodes.
*/
for (j = 0; j < k; j++) {
push(nodeArray[j]);
}
# if CBC_DEBUG_HEAP > 1
std::cout << " cleanTree: finished rebuild." << std::endl ;
# endif
# if CBC_DEBUG_HEAP > 0
validateHeap() ;
# endif
/*
Sort the list of nodes to be deleted, nondecreasing.
*/
CoinSort_2(depth + kDelete, depth + nNodes, nodeArray + kDelete);
/*
Work back from deepest to shallowest. In spite of the name, addCuts1 is
just a preparatory step. When it returns, the following will be true:
* all cuts are removed from the solver's copy of the constraint system;
* lastws will be a basis appropriate for the specified node;
* variable bounds will be adjusted to be appropriate for the specified
node;
* addedCuts_ (returned via addedCuts()) will contain a list of cuts that
should be added to the constraint system at this node (but they have
not actually been added).
Then we scan the cut list for the node. Decrement the reference count
for the cut, and if it's gone to 0, really delete it.
I don't yet see why the checks for status != basic and addedCuts_[i] != 0
are necessary. When reconstructing a node, these checks are used to skip
over loose cuts, excluding them from the reconstituted basis. But here
we're just interested in correcting the reference count. Tight/loose
should make no difference.
Arguably a separate routine should be used in place of addCuts1. It's
doing more work than needed, modifying the model to match a subproblem
at a node that will be discarded. Then again, we seem to need the basis.
*/
for (j = nNodes - 1; j >= kDelete; j--) {
CbcNode * node = nodeArray[j];
CoinWarmStartBasis *lastws = model->getEmptyBasis() ;
model->addCuts1(node, lastws);
// Decrement cut counts
assert (node);
//assert (node->nodeInfo());
int numberLeft = (node->nodeInfo()) ? node->nodeInfo()->numberBranchesLeft() : 0;
int i;
for (i = 0; i < model->currentNumberCuts(); i++) {
// take off node
CoinWarmStartBasis::Status status =
lastws->getArtifStatus(i + model->numberRowsAtContinuous());
if (status != CoinWarmStartBasis::basic &&
model->addedCuts()[i]) {
if (!model->addedCuts()[i]->decrement(numberLeft))
delete model->addedCuts()[i];
}
}
// node should not have anything pointing to it
if (node->nodeInfo())
node->nodeInfo()->throwAway();
delete node ;
delete lastws ;
}
delete [] nodeArray;
delete [] depth;
}
// Return the best node of the heap using alternate criterion
CbcNode *
CbcTree::bestAlternate()
{
size_t n = nodes_.size();
CbcNode * best = NULL;
if (n) {
best = nodes_[0];
for (size_t i = 1; i < n; i++) {
if (comparison_.alternateTest(best, nodes_[i])) {
best = nodes_[i];
}
}
}
return best;
}
#ifdef JJF_ZERO // not used, reference removed in CbcModel.cpp
CbcTreeArray::CbcTreeArray()
: CbcTree(),
lastNode_(NULL),
lastNodePopped_(NULL),
switches_(0)
{
}
CbcTreeArray::~CbcTreeArray()
{
}
// Copy constructor
CbcTreeArray::CbcTreeArray ( const CbcTreeArray & rhs)
: CbcTree(rhs),
lastNode_(rhs.lastNode_),
lastNodePopped_(rhs.lastNodePopped_),
switches_(rhs.switches_)
{
}
// Assignment operator
CbcTreeArray &
CbcTreeArray::operator=(const CbcTreeArray & rhs)
{
if (this != &rhs) {
CbcTree::operator=(rhs);
lastNode_ = rhs.lastNode_;
lastNodePopped_ = rhs.lastNodePopped_;
switches_ = rhs.switches_;
}
return *this;
}
// Clone
CbcTree *
CbcTreeArray::clone() const
{
return new CbcTreeArray(*this);
}
// Set comparison function and resort heap
void
CbcTreeArray::setComparison(CbcCompareBase &compare)
{
comparison_.test_ = &compare;
rebuild() ;
}
// Add a node to the heap
void
CbcTreeArray::push(CbcNode * x)
{
/*printf("push obj %g, refcount %d, left %d, pointing to %d\n",
x->objectiveValue(),x->nodeInfo()->decrement(0),
x->nodeInfo()->numberBranchesLeft(),x->nodeInfo()->numberPointingToThis());*/
assert(x->objectiveValue() != COIN_DBL_MAX && x->nodeInfo());
x->setOnTree(true);
if (lastNode_) {
if (lastNode_->nodeInfo()->parent() == x->nodeInfo()) {
// x is parent of lastNode_ so put x on heap
//#define CBCTREE_PRINT
#ifdef CBCTREE_PRINT
printf("pushX x %x (%x at depth %d n %d) is parent of lastNode_ %x (%x at depth %d n %d)\n",
x, x->nodeInfo(), x->depth(), x->nodeNumber(),
lastNode_, lastNode_->nodeInfo(), lastNode_->depth(), lastNode_->nodeNumber());
#endif
nodes_.push_back(x);
} else {
x->setNodeNumber(maximumNodeNumber_);
maximumNodeNumber_++;
#ifdef CBCTREE_PRINT
printf("pushLast x %x (%x at depth %d n %d) is parent of lastNode_ %x (%x at depth %d n %d)\n",
x, x->nodeInfo(), x->depth(), x->nodeNumber(),
lastNode_, lastNode_->nodeInfo(), lastNode_->depth(), lastNode_->nodeNumber());
#endif
nodes_.push_back(lastNode_);
lastNode_ = x;
}
std::push_heap(nodes_.begin(), nodes_.end(), comparison_);
} else {
x->setNodeNumber(maximumNodeNumber_);
maximumNodeNumber_++;
if (x != lastNodePopped_) {
lastNode_ = x;
#ifdef CBCTREE_PRINT
printf("pushNULL x %x (%x at depth %d n %d)\n",
x, x->nodeInfo(), x->depth(), x->nodeNumber());
#endif
} else {
// means other way was infeasible
#ifdef CBCTREE_PRINT
printf("push_other_infeasible x %x (%x at depth %d n %d)\n",
x, x->nodeInfo(), x->depth(), x->nodeNumber());
#endif
nodes_.push_back(x);
std::push_heap(nodes_.begin(), nodes_.end(), comparison_);
}
}
}
// Test if empty *** note may be overridden
bool
CbcTreeArray::empty()
{
return nodes_.empty() && (lastNode_ == NULL);
}
// Gets best node and takes off heap
CbcNode *
CbcTreeArray::bestNode(double cutoff)
{
CbcNode * best = NULL;
// See if we want last node or best on heap
if (lastNode_) {
#ifdef CBCTREE_PRINT
printf("Best lastNode_ %x (%x at depth %d) - nodeNumber %d obj %g\n",
lastNode_, lastNode_->nodeInfo(), lastNode_->depth(),
lastNode_->nodeNumber(), lastNode_->objectiveValue());
#endif
assert (lastNode_->onTree());
int nodeNumber = lastNode_->nodeNumber();
bool useLastNode = false;
if (nodeNumber + 1 == maximumNodeNumber_) {
// diving - look further
CbcCompareDefault * compareDefault
= dynamic_cast<CbcCompareDefault *> (comparison_.test_);
assert (compareDefault);
double bestPossible = compareDefault->getBestPossible();
double cutoff = compareDefault->getCutoff();
double objValue = lastNode_->objectiveValue();
if (cutoff < 1.0e20) {
if (objValue - bestPossible < 0.999*(cutoff - bestPossible))
useLastNode = true;
} else {
useLastNode = true;
}
}
if (useLastNode) {
lastNode_->setOnTree(false);
best = lastNode_;
lastNode_ = NULL;
assert(best->objectiveValue() != COIN_DBL_MAX && best->nodeInfo());
if (best->objectiveValue() != COIN_DBL_MAX && best->nodeInfo())
assert (best->nodeInfo()->numberBranchesLeft());
if (best->objectiveValue() >= cutoff) {
// double check in case node can change its mind!
best->checkIsCutoff(cutoff);
}
lastNodePopped_ = best;
return best;
} else {
// put on tree
nodes_.push_back(lastNode_);
lastNode_->setNodeNumber(maximumNodeNumber_);
maximumNodeNumber_++;
lastNode_ = NULL;
std::push_heap(nodes_.begin(), nodes_.end(), comparison_);
}
}
while (!best && nodes_.size()) {
best = nodes_.front();
if (best)
assert(best->objectiveValue() != COIN_DBL_MAX && best->nodeInfo());
if (best && best->objectiveValue() != COIN_DBL_MAX && best->nodeInfo())
assert (best->nodeInfo()->numberBranchesLeft());
if (best && best->objectiveValue() >= cutoff) {
// double check in case node can change its mind!
best->checkIsCutoff(cutoff);
}
if (!best || best->objectiveValue() >= cutoff) {
// let code get rid of it
assert (best);
}
}
lastNodePopped_ = best;
#ifdef CBCTREE_PRINT
if (best)
printf("Heap returning node %x (%x at depth %d) - nodeNumber %d - obj %g\n",
best, best->nodeInfo(), best->depth(),
best->nodeNumber(), best->objectiveValue());
else
printf("Heap returning Null\n");
#endif
if (best) {
// take off
std::pop_heap(nodes_.begin(), nodes_.end(), comparison_);
nodes_.pop_back();
}
#ifdef DEBUG_CBC_HEAP
if (best) {
int n = nodes_.size();
bool good = true;
for (int i = 0; i < n; i++) {
// temp
assert (nodes_[i]);
if (!comparison_.compareNodes(nodes_[i], best)) {
good = false;
CbcNode * x = nodes_[i];
printf("i=%d x is better nun %d depth %d obj %g, best nun %d depth %d obj %g\n", i,
x->numberUnsatisfied(), x->depth(), x->objectiveValue(),
best->numberUnsatisfied(), best->depth(), best->objectiveValue());
}
}
if (!good) {
// compare best to all
int i;
for (i = 0; i < n; i++) {
CbcNode * x = nodes_[i];
printf("i=%d x is nun %d depth %d obj %g", i,
x->numberUnsatisfied(), x->depth(), x->objectiveValue());
if (!comparison_.compareNodes(x, best)) {
printf(" - best is worse!\n");
} else {
printf("\n");
}
}
// Now compare amongst rest
for (i = 0; i < n; i++) {
CbcNode * x = nodes_[i];
printf("For i=%d ", i);
for (int j = i + 1; j < n; j++) {
CbcNode * y = nodes_[j];
if (!comparison_.compareNodes(x, y)) {
printf(" b %d", j);
} else {
printf(" w %d", j);
}
}
printf("\n");
}
assert(good);
}
}
#endif
if (best)
best->setOnTree(false);
return best;
}
double
CbcTreeArray::getBestPossibleObjective()
{
double bestPossibleObjective = 1e100;
for (int i = 0 ; i < static_cast<int> (nodes_.size()) ; i++) {
if (nodes_[i] && nodes_[i]->objectiveValue() < bestPossibleObjective) {
bestPossibleObjective = nodes_[i]->objectiveValue();
}
}
if (lastNode_) {
bestPossibleObjective = CoinMin(bestPossibleObjective, lastNode_->objectiveValue());
}
CbcCompareDefault * compareDefault
= dynamic_cast<CbcCompareDefault *> (comparison_.test_);
assert (compareDefault);
compareDefault->setBestPossible(bestPossibleObjective);
return bestPossibleObjective;
}
/*! \brief Prune the tree using an objective function cutoff
This routine removes all nodes with objective worst than the
specified cutoff value.
*/
void
CbcTreeArray::cleanTree(CbcModel * model, double cutoff, double & bestPossibleObjective)
{
int j;
int nNodes = size();
int lastNode = nNodes + 1;
CbcNode ** nodeArray = new CbcNode * [lastNode];
int * depth = new int [lastNode];
int k = 0;
int kDelete = lastNode;
bestPossibleObjective = 1.0e100 ;
/*
Destructively scan the heap. Nodes to be retained go into the front of
nodeArray, nodes to be deleted into the back. Store the depth in a
correlated array for nodes to be deleted.
*/
for (j = 0; j < nNodes; j++) {
CbcNode * node = nodes_.front();
nodes_.front()->setOnTree(false);
std::pop_heap(nodes_.begin(), nodes_.end(), comparison_);
nodes_.pop_back();
double value = node ? node->objectiveValue() : COIN_DBL_MAX;
if (node && value >= cutoff) {
// double check in case node can change its mind!
value = node->checkIsCutoff(cutoff);
}
if (value >= cutoff || !node->active()) {
if (node) {
nodeArray[--kDelete] = node;
depth[kDelete] = node->depth();
}
} else {
bestPossibleObjective = CoinMin(bestPossibleObjective, value);
nodeArray[k++] = node;
}
}
if (lastNode_) {
double value = lastNode_->objectiveValue();
bestPossibleObjective = CoinMin(bestPossibleObjective, value);
if (value >= cutoff || !lastNode_->active()) {
nodeArray[--kDelete] = lastNode_;
depth[kDelete] = lastNode_->depth();
lastNode_ = NULL;
}
}
CbcCompareDefault * compareDefault
= dynamic_cast<CbcCompareDefault *> (comparison_.test_);
assert (compareDefault);
compareDefault->setBestPossible(bestPossibleObjective);
compareDefault->setCutoff(cutoff);
/*
Rebuild the heap using the retained nodes.
*/
for (j = 0; j < k; j++) {
CbcNode * node = nodeArray[j];
node->setOnTree(true);
nodes_.push_back(node);
std::push_heap(nodes_.begin(), nodes_.end(), comparison_);
}
/*
Sort the list of nodes to be deleted, nondecreasing.
*/
CoinSort_2(depth + kDelete, depth + lastNode, nodeArray + kDelete);
/*
Work back from deepest to shallowest. In spite of the name, addCuts1 is
just a preparatory step. When it returns, the following will be true:
* all cuts are removed from the solver's copy of the constraint system;
* lastws will be a basis appropriate for the specified node;
* variable bounds will be adjusted to be appropriate for the specified
node;
* addedCuts_ (returned via addedCuts()) will contain a list of cuts that
should be added to the constraint system at this node (but they have
not actually been added).
Then we scan the cut list for the node. Decrement the reference count
for the cut, and if it's gone to 0, really delete it.
I don't yet see why the checks for status != basic and addedCuts_[i] != 0
are necessary. When reconstructing a node, these checks are used to skip
over loose cuts, excluding them from the reconstituted basis. But here
we're just interested in correcting the reference count. Tight/loose
should make no difference.
Arguably a separate routine should be used in place of addCuts1. It's
doing more work than needed, modifying the model to match a subproblem
at a node that will be discarded. Then again, we seem to need the basis.
*/
for (j = lastNode - 1; j >= kDelete; j--) {
CbcNode * node = nodeArray[j];
CoinWarmStartBasis *lastws = model->getEmptyBasis() ;
model->addCuts1(node, lastws);
// Decrement cut counts
assert (node);
//assert (node->nodeInfo());
int numberLeft = (node->nodeInfo()) ? node->nodeInfo()->numberBranchesLeft() : 0;
int i;
for (i = 0; i < model->currentNumberCuts(); i++) {
// take off node
CoinWarmStartBasis::Status status =
lastws->getArtifStatus(i + model->numberRowsAtContinuous());
if (status != CoinWarmStartBasis::basic &&
model->addedCuts()[i]) {
if (!model->addedCuts()[i]->decrement(numberLeft))
delete model->addedCuts()[i];
}
}
// node should not have anything pointing to it
if (node->nodeInfo())
node->nodeInfo()->throwAway();
delete node ;
delete lastws ;
}
delete [] nodeArray;
delete [] depth;
}
#endif
#else // defined(CBC_DUBIOUS_HEAP)
/*
Unclear whether this code is useful any longer. Likely stale. See
note in CbcCompareDefault.hpp re. CBC_DUBIOUS_HEAP.
-- lh, 100921 --
*/
// Set comparison function and resort heap
void
CbcTree::setComparison(CbcCompareBase &compare)
{
comparison_.test_ = &compare;
std::vector <CbcNode *> newNodes = nodes_;
nodes_.resize(0);
while (newNodes.size() > 0) {
push( newNodes.back());
newNodes.pop_back();
}
}
// Return the top node of the heap
CbcNode *
CbcTree::top() const
{
return nodes_.front();
}
// Add a node to the heap
void
CbcTree::push(CbcNode * x)
{
x->setNodeNumber(maximumNodeNumber_);
maximumNodeNumber_++;
/*printf("push obj %g, refcount %d, left %d, pointing to %d\n",
x->objectiveValue(),x->nodeInfo()->decrement(0),
x->nodeInfo()->numberBranchesLeft(),x->nodeInfo()->numberPointingToThis());*/
assert(x->objectiveValue() != COIN_DBL_MAX && x->nodeInfo());
#ifdef JJF_ZERO
nodes_.push_back(x);
push_heap(nodes_.begin(), nodes_.end(), comparison_);
#else
realpush(x);
#endif
}
// Remove the top node from the heap
void
CbcTree::pop()
{
#ifdef JJF_ZERO
std::pop_heap(nodes_.begin(), nodes_.end(), comparison_);
nodes_.pop_back();
#else
if (nodes_.size()) {
//CbcNode* s = nodes_.front();
realpop();
//delete s;
}
assert (nodes_.size() >= 0);
#endif
}
// Test if empty *** note may be overridden
bool
CbcTree::empty()
{
return nodes_.empty();
}
// Gets best node and takes off heap
CbcNode *
CbcTree::bestNode(double cutoff)
{
CbcNode * best = NULL;
while (!best && nodes_.size()) {
best = nodes_.front();
if (best)
assert(best->objectiveValue() != COIN_DBL_MAX && best->nodeInfo());
if (best && best->objectiveValue() != COIN_DBL_MAX && best->nodeInfo())
assert (best->nodeInfo()->numberBranchesLeft());
if (best && best->objectiveValue() >= cutoff) {
// double check in case node can change its mind!
best->checkIsCutoff(cutoff);
}
if (!best || best->objectiveValue() >= cutoff) {
#ifdef JJF_ZERO
// take off
std::pop_heap(nodes_.begin(), nodes_.end(), comparison_);
nodes_.pop_back();
delete best;
best = NULL;
#else
// let code get rid of it
assert (best);
#endif
}
}
// switched off for now
if (best && comparison_.test_->fullScan() && false) {
CbcNode * saveBest = best;
int n = nodes_.size();
int iBest = -1;
for (int i = 0; i < n; i++) {
// temp
assert (nodes_[i]);
assert (nodes_[i]->nodeInfo());
if (nodes_[i] && nodes_[i]->objectiveValue() != COIN_DBL_MAX && nodes_[i]->nodeInfo())
assert (nodes_[i]->nodeInfo()->numberBranchesLeft());
if (nodes_[i] && nodes_[i]->objectiveValue() < cutoff
&& comparison_.alternateTest(best, nodes_[i])) {
best = nodes_[i];
iBest = i;
}
}
if (best == saveBest) {
// can pop
// take off
std::pop_heap(nodes_.begin(), nodes_.end(), comparison_);
nodes_.pop_back();
} else {
// make impossible
nodes_[iBest] = NULL;
}
} else if (best) {
// take off
#ifdef JJF_ZERO
std::pop_heap(nodes_.begin(), nodes_.end(), comparison_);
nodes_.pop_back();
#else
realpop();
#endif
}
#ifdef DEBUG_CBC_HEAP
if (best) {
int n = nodes_.size();
bool good = true;
for (int i = 0; i < n; i++) {
// temp
assert (nodes_[i]);
if (!comparison_.compareNodes(nodes_[i], best)) {
good = false;
CbcNode * x = nodes_[i];
printf("i=%d x is better nun %d depth %d obj %g, best nun %d depth %d obj %g\n", i,
x->numberUnsatisfied(), x->depth(), x->objectiveValue(),
best->numberUnsatisfied(), best->depth(), best->objectiveValue());
}
}
if (!good) {
// compare best to all
int i;
for (i = 0; i < n; i++) {
CbcNode * x = nodes_[i];
printf("i=%d x is nun %d depth %d obj %g", i,
x->numberUnsatisfied(), x->depth(), x->objectiveValue());
if (!comparison_.compareNodes(x, best)) {
printf(" - best is worse!\n");
} else {
printf("\n");
}
}
// Now compare amongst rest
for (i = 0; i < n; i++) {
CbcNode * x = nodes_[i];
printf("For i=%d ", i);
for (int j = i + 1; j < n; j++) {
CbcNode * y = nodes_[j];
if (!comparison_.compareNodes(x, y)) {
printf(" b %d", j);
} else {
printf(" w %d", j);
}
}
printf("\n");
}
assert(good);
}
}
#endif
if (best)
best->setOnTree(false);
return best;
}
/*! \brief Prune the tree using an objective function cutoff
This routine removes all nodes with objective worst than the
specified cutoff value.
*/
void
CbcTree::cleanTree(CbcModel * model, double cutoff, double & bestPossibleObjective)
{
int j;
int nNodes = nodes_.size();
CbcNode ** nodeArray = new CbcNode * [nNodes];
int * depth = new int [nNodes];
int k = 0;
int kDelete = nNodes;
bestPossibleObjective = 1.0e100 ;
/*
Destructively scan the heap. Nodes to be retained go into the front of
nodeArray, nodes to be deleted into the back. Store the depth in a
correlated array for nodes to be deleted.
*/
for (j = 0; j < nNodes; j++) {
CbcNode * node = top();
pop();
double value = node ? node->objectiveValue() : COIN_DBL_MAX;
if (node && value >= cutoff) {
// double check in case node can change its mind!
value = node->checkIsCutoff(cutoff);
}
bestPossibleObjective = CoinMin(bestPossibleObjective, value);
if (value >= cutoff) {
if (node) {
nodeArray[--kDelete] = node;
depth[kDelete] = node->depth();
}
} else {
nodeArray[k++] = node;
}
}
/*
Rebuild the heap using the retained nodes.
*/
for (j = 0; j < k; j++) {
push(nodeArray[j]);
}
/*
Sort the list of nodes to be deleted, nondecreasing.
*/
CoinSort_2(depth + kDelete, depth + nNodes, nodeArray + kDelete);
/*
Work back from deepest to shallowest. In spite of the name, addCuts1 is
just a preparatory step. When it returns, the following will be true:
* all cuts are removed from the solver's copy of the constraint system;
* lastws will be a basis appropriate for the specified node;
* variable bounds will be adjusted to be appropriate for the specified
node;
* addedCuts_ (returned via addedCuts()) will contain a list of cuts that
should be added to the constraint system at this node (but they have
not actually been added).
Then we scan the cut list for the node. Decrement the reference count
for the cut, and if it's gone to 0, really delete it.
I don't yet see why the checks for status != basic and addedCuts_[i] != 0
are necessary. When reconstructing a node, these checks are used to skip
over loose cuts, excluding them from the reconstituted basis. But here
we're just interested in correcting the reference count. Tight/loose
should make no difference.
Arguably a separate routine should be used in place of addCuts1. It's
doing more work than needed, modifying the model to match a subproblem
at a node that will be discarded. Then again, we seem to need the basis.
*/
for (j = nNodes - 1; j >= kDelete; j--) {
CbcNode * node = nodeArray[j];
CoinWarmStartBasis *lastws = model->getEmptyBasis() ;
model->addCuts1(node, lastws);
// Decrement cut counts
assert (node);
//assert (node->nodeInfo());
int numberLeft = (node->nodeInfo()) ? node->nodeInfo()->numberBranchesLeft() : 0;
int i;
for (i = 0; i < model->currentNumberCuts(); i++) {
// take off node
CoinWarmStartBasis::Status status =
lastws->getArtifStatus(i + model->numberRowsAtContinuous());
if (status != CoinWarmStartBasis::basic &&
model->addedCuts()[i]) {
if (!model->addedCuts()[i]->decrement(numberLeft))
delete model->addedCuts()[i];
}
}
// node should not have anything pointing to it
if (node->nodeInfo())
node->nodeInfo()->throwAway();
delete node ;
delete lastws ;
}
delete [] nodeArray;
delete [] depth;
}
// Return the best node of the heap using alternate criterion
CbcNode *
CbcTree::bestAlternate()
{
int n = nodes_.size();
CbcNode * best = NULL;
if (n) {
best = nodes_[0];
for (int i = 1; i < n; i++) {
if (comparison_.alternateTest(best, nodes_[i])) {
best = nodes_[i];
}
}
}
return best;
}
void
CbcTree::realpop()
{
if (nodes_.size() > 0) {
nodes_[0] = nodes_.back();
nodes_.pop_back();
fixTop();
}
assert (nodes_.size() >= 0);
}
/* After changing data in the top node, fix the heap */
void
CbcTree::fixTop()
{
const int size = nodes_.size();
if (size > 1) {
CbcNode** candidates = &nodes_[0];
CbcNode* s = candidates[0];
--candidates;
int pos = 1;
int ch;
for (ch = 2; ch < size; pos = ch, ch *= 2) {
if (!comparison_.compareNodes(candidates[ch+1], candidates[ch]))
++ch;
if (!comparison_.compareNodes(s, candidates[ch]))
break;
candidates[pos] = candidates[ch];
}
if (ch == size) {
if (!comparison_.compareNodes(candidates[ch], s)) {
candidates[pos] = candidates[ch];
pos = ch;
}
}
candidates[pos] = s;
}
}
void
CbcTree::realpush(CbcNode * node)
{
node->setOnTree(true);
nodes_.push_back(node);
CbcNode** candidates = &nodes_[0];
--candidates;
int pos = nodes_.size();
int ch;
for (ch = pos / 2; ch != 0; pos = ch, ch /= 2) {
if (!comparison_.compareNodes(candidates[ch], node))
break;
candidates[pos] = candidates[ch];
}
candidates[pos] = node;
}
#endif
double
CbcTree::getBestPossibleObjective()
{
double r_val = 1e100;
for (int i = 0 ; i < static_cast<int> (nodes_.size()) ; i++) {
if (nodes_[i] && nodes_[i]->objectiveValue() < r_val) {
r_val = nodes_[i]->objectiveValue();
}
}
return r_val;
}