Skip to content
Snippets Groups Projects
bbsolver.cpp 49.91 KiB
/********************************************************************************************
 Name:       DiscretizationSolver
 Discrete optimization for graph discretization
 Author:     J.Omer
 Sources:    C++
 License:    GNU General Public License v.2
 History:
 *********************************************************************************************/

#include "bbsolver.hpp"

#include <algorithm>


ILOSTLBEGIN
//

ILOLAZYCONSTRAINTCALLBACK2(CyclesLazyConstraintsBB,
                           Instance &, inst,
                           DiscretizationSolver&, solver) {
#ifdef VERBOSE
  std::cout << "revorder: check lazy dicycle constraints" << std::endl;
#endif
  // Search for cycles in the current solution digraph
  //
  // generate the adjacency lists of the vertices corresponding to the current
  // cplex solution
  float tolerance = 1e-06;
  std::map<Vertex*, std::vector<Vertex*> > adjlist;
  std::map<Vertex*, int > nbrefs;
  for (Vertex* v : inst.vertices_) {
    adjlist[v] = std::vector<Vertex*>();
    nbrefs[v] = 0;
  }
  for (Vertex* u : inst.vertices_) {
    if (solver.modeltype_ == WITNESS) {
      if (getValue(solver.isvertexinclique(u) >= 1 - tolerance)) continue;
    }
    for (Vertex* v : u->neighbors_) {
      if (getValue(solver.isbefore(u, v)) >= 1 - tolerance) {
        adjlist[u].push_back(v);
        nbrefs[v]++;
      }
    }
  }
  //
  // search for the root of the digraph
  Vertex* root = nullptr;
  int minnbrefs = inst.U();
  if (solver.modeltype_ == WITNESS) {
    for (Vertex* v : inst.vertices_) {
      if (getValue(solver.isvertexinclique(v) >= 1 - tolerance)) {
        root = v;
        break;
      }
    }
  } else {
    for (Vertex* v : inst.vertices_) {
      if (nbrefs[v] < minnbrefs) {
        minnbrefs = nbrefs[v];
        root = v;
        break;
      }
    }
  }
  //
  // call the enumeration of cycles, returns false if there are none
  std::vector<std::vector<Vertex*> > cycles;
  bool iscyclic = solver.enumeratecycles(adjlist, root, cycles);
  // Look for dicycle inequalities : we restrict the search to the dicycles
  // that contain at least one edge in the distance graph
  // besides that, the search is performed using brute force
  int nbaddedcuts = 0;
  IloEnv env = getEnv();
  if (iscyclic) {
    for (std::vector<Vertex*> path : cycles) {
      IloExpr sumedges(env);
      int cyclelength = path.size();
      Vertex* u = path.back();
      for (int i = 0; i < cyclelength; i++) {
        Vertex* v = path[i];
        sumedges += solver.isbefore(u, v);
        u = v;
      }
      if ((solver.modeltype_ == WITNESS) && (cyclelength <= inst.L() + 1)) {
        add(sumedges - solver.isvertexinclique(path.front())
                <= cyclelength - 1).end();
        nbaddedcuts++;
      } else {
        add(sumedges <= cyclelength - 1).end();
        nbaddedcuts++;
      }
    }
#ifdef VERBOSE
    std::cout << "revorder: number of violated dicycle constraints : " << nbaddedcuts << std::endl;
#endif
  } else {
#ifdef VERBOSE
    std::cout << "revorder: the graph is acyclic " << std::endl;
#endif
  }
  return;
}
/*************************************************************************************************************************************************************/
//
// sort the nodes by ascending number of ordered vertices
bool bbnodesnborderedcompare(BBNode* n1, BBNode* n2) {
  return (n1->nbordered() < n2->nbordered());
}
bool bbnodesbestcompare(BBNode*n1, BBNode* n2) {
  return (static_cast<float>(n1->cost_) / n1->nbordered()
      > static_cast<float>(n2->cost_) / n2->nbordered());
}

// Public methods
//
// constructor and destructor
//
// constructor used only for the root
BBNode::BBNode(Instance& inst):
    depth_(0),
    inst_(&inst),
    nbordered_(0),
    nbfullref_(0),
    istreated_(true) {}
//
// constructor used only for the initial cliques
BBNode::BBNode(Instance& inst,
               BBNode* root,
               Clique& c,
               bool branchonsmallerindex):
    depth_(1),
    father_(root),
    inst_(&inst),
    nbordered_(0),
    nbfullref_(0),
    istreated_(false) {
  this->orderedvertices_.clear();
  for (Vertex* v : inst.vertices_) {
    this->rank_[v] = -1;
    this->nbrefs_[v] = 0;
    this->isfullref_[v] = false;
    this->isordered_[v] = false;
    this->ispotentialchild_[v] = false;
    this->mustbefullref_[v] = false;
    this->maxrefs_[v] = v->degree_;
  }
  int currentrank = 1;
  for (Vertex* u : c.vertices_) {
    this->rank_[u] = currentrank++;
    this->orderedvertices_.push_back(u);
    this->switchtoordered(u);
    this->nbordered_++;
    //
    // update the number of references of the neighbors of the vertex
    for (Vertex* v : u->neighbors_) {
      if (!this->isordered_[v]) {
        this->nbrefs_[v]++;
      }
    }
  }

  // record the list of partially and fully referenced (unordered) vertices
  for (Vertex* v : inst.vertices_) {
    if (!this->isordered_[v]) {
      // record the list of unordered fully-referenced vertices
      if (this->nbrefs_[v] >= inst.U()) {
        this->isfullref_[v] = true;
        this->fullrefs_.push_back(v);
      } else if (this->nbrefs_[v] >= inst.L()) {
        // add partially referenced vertices to the list of potential choices
        // for branching
        if (branchonsmallerindex) {
          bool largerindex = true;
          Vertex* u = c.vertices_.back();
          if (!u->isneighbor(v)) {
            if (u->id_ >= v->id_) largerindex = false;
          }
          //
          // to break symmetries, make sure that the chosen potential child
          // is always that with biggest index
          if (largerindex) {
            this->ispotentialchild_[v] = true;
            this->potentialchildren_.push_back(v);
            //
            // partially referenced vertices with less than U neighbors can
            // be ordered right away since they will never be fully referenced
            if (this->maxrefs_[v] <= inst.U() - 1) {
              this->readytoorder_.push_back(v);
#ifdef VERBOSE
              std::cout << "revorder: bb: order vertex " << v->id_ << " without branching, maximum nb of references = " << this->maxrefs_[v] << std::endl;
#endif
            }
          } else {
            this->mustbefullref_[v] = true;
            // if a vertex must be fully-referenced, but it has exactly U
            // neighbors, it will for sure come after all its neighbors in
            // the order
            if (this->maxrefs_[v] <= inst.U()) {
              for (Vertex* u : v->neighbors_) {
                if (this->mustbefullref_[u]) {
                  if (this->maxrefs_[u] >= inst.U()+1) {
                    this->maxrefs_[u]--;
                  }
                } else {
                  this->maxrefs_[u]--;
                }
              }
            }
          }
        } else {
          this->ispotentialchild_[v] = true;
          this->potentialchildren_.push_back(v);

          // partially referenced vertices with less than U neighbors can be
          // ordered right away since they will never be fully referenced
          if (this->maxrefs_[v] <= inst.U() - 1) {
            this->readytoorder_.push_back(v);
#ifdef VERBOSE
            std::cout << "revorder: bb: order vertex " << v->id_ << " without branching, maximum nb of references = " << this->maxrefs_[v] << std::endl;
#endif
          }
        }
      }
    }
  }
  this->dualbound_ = -1;
  root->addtochildren(this);
}

// Make a copy of the input BB node: used for every other node
BBNode::BBNode(BBNode& node): id_(node.id_),
                              depth_(node.depth_+1),
                              father_(&node),
                              inst_(node.inst_),
                              nbordered_(node.nbordered_),
                              nbfullref_(node.nbfullref_),
                              istreated_(false) {
  cost_ = node.cost_;
  dualbound_ = node.dualbound_;
  primalbound_ = node.primalbound_;
  for (Vertex* v : inst_->vertices_) {
    rank_[v] = node.rank_.at(v);
    isordered_[v] = node.isordered_.at(v);
    nbrefs_[v] = node.nbrefs_.at(v);
    isfullref_[v] = node.isfullref_.at(v);
    ispotentialchild_[v] = node.ispotentialchild_.at(v);
    mustbefullref_[v] = node.mustbefullref_.at(v);
    maxrefs_[v] = node.maxrefs_.at(v);
  }
  fullrefs_.clear();
  orderedvertices_.clear();
  potentialchildren_.clear();
  children_.clear();
  for (Vertex* v : node.readytoorder_) readytoorder_.push_back(v);
  for (Vertex* v : node.fullrefs_) fullrefs_.push_back(v);
  for (Vertex* v : node.orderedvertices_) orderedvertices_.push_back(v);
  for (Vertex* v : node.potentialchildren_) potentialchildren_.push_back(v);
}
//
// Destroy the node
BBNode::~BBNode() {


}
//
// remove a vertex from the list of potential choice for branching
void BBNode::removefrompotentialchildren(Vertex* v) {
  //
  // no need to proceed if the vertex is not marked as potential child
  if (!this->ispotentialchild_[v]) return;

  this->ispotentialchild_[v] = false;
  std::vector<Vertex*>::iterator itv = this->potentialchildren_.begin();
  while (itv < this->potentialchildren_.end()) {
    if (*itv == v) {
      this->potentialchildren_.erase(itv);
      break;
    } else {
      itv++;

      // detect error if the vertex was not found in the list of potential
      // children
      if (itv == this->potentialchildren_.end()) {
        std::cout << "revorder: bb: did not find a potential child in the list"
                  << '\n';
        throw;
      }
    }
  }
  itv = this->readytoorder_.begin();
  while (itv < this->readytoorder_.end()) {
    if (*itv == v) {
      this->readytoorder_.erase(itv);
      break;
    }
    itv++;
  }
}
//
// assign the next available rank to the input vertex
void BBNode::assignnextrank(Vertex* u) {
  //
  // assign the next available rank
  for (Vertex* v : this->orderedvertices_) {
    if (u == v) {
      std::cout << "Les ennuis commencent ici." << std::endl;
    }
  }
  this->rank_[u] = this->nbordered_ + 1;
  this->orderedvertices_.push_back(u);
  this->switchtoordered(u);
  this->nbordered_++;
  this->removefrompotentialchildren(u);

  if (this->nbrefs_[u] <= this->inst_->L() - 1) {
    std::cout << "revorder: bb: trying to assign a rank to a vertex with less"
                 " than L references" << '\n';
    throw;
  } else if (this->nbrefs_[u] >= this->inst_->U()) {
    this->nbfullref_++;
  }

  // update the number of references of the neighbors of v
  for (Vertex* v : u->neighbors_) {
    if (this->rank_[v] < 0) {
      this->nbrefs_[v]++;

      // add partially-referenced vertices to the list of potential choices
      // for branching
      if (this->nbrefs_[v] == this->inst_->L()) {
        this->potentialchildren_.push_back(v);
        this->ispotentialchild_[v] = true;

        // partially referenced vertices with less than U neighbors can be
        // ordered right away since they will never be fully referenced
        if (this->maxrefs_[v] <= this->inst_->U() - 1) {
          this->readytoorder_.push_back(v);
#ifdef VERBOSE
          std::cout << "revorder: bb: order vertex " << v->id_ << " without branching, maximum nb of references = " << this->maxrefs_[v] << std::endl;
#endif
        }
      }
      // a vertex is fully-referenced if it has more than U references, and it
      // is not a potential choice for branching anymore
      if (this->nbrefs_[v] == this->inst_->U()) {
        this->isfullref_[v] = true;
        this->fullrefs_.push_back(v);
        if (this->ispotentialchild_[v]) this->removefrompotentialchildren(v);
      }
    }
  }
}

// get the current objective value at a node, depending on the ordered vertices
void BBNode::setnodecost(Problem pb) {
  int nbpartials = 0;
  for (Vertex* u : this->inst_->vertices_) {
    if (this->isordered_[u]) {
      if (this->nbrefs_[u] <= this->inst_->U() - 1) nbpartials += 1;
    }
  }
  this->cost_ =  nbpartials;
}

// a bb node n1 dominates another one, n2, if the corresponding partial
// solution has a smaller or equal cost but its list of ordered vertices
// contains that of n2
bool BBSolver::dominates(BBNode& n1, BBNode& n2) {
  if ( n2.cost_ < n1.cost_ )  {
    return false;
  }

  for (Vertex* v : n2.orderedvertices_) {
    if (!(n1.isordered(v))) {
      return false;
    }
  }
  return true;
}
//
// propagate the set of ordered vertices by iteratively ordering
// fully-referenced vertices
void BBNode::prepropagation() {
#ifdef VERBOSE
  cout << "prepropagation" << endl;
#endif
  // iteratively propagate while there are fully-referenced vertices
  while (!this->fullrefs_.empty()) {
    Vertex* u = this->fullrefs_.back();
    this->fullrefs_.pop_back();
    this->isfullref_[u] = false;
    this->assignnextrank(u);
  }
  this->setnodecost(MINPARTIAL);
}
//
// propagate the set of ordered vertices by iteratively ordering vertices
// as long as there is either at least one unordered vertex with more than U
// references or exactly one unordered vertex with exactly U references
void BBNode::postpropagation() {
#ifdef VERBOSE
  cout << "in postpropagation" << endl;
#endif
  //
  // no need of propagation if all the vertices are ordered
  if (this->nbordered_ == this->inst_->nbvertices_) return;
  //
  // iteratively propagate while there are fully-referenced vertices or
  // exactly one partially-referenced vertex
  while (true) {
    bool ispropagate = false;
    Vertex* u;
    if (!this->fullrefs_.empty()) {
      // add a vertex with more than U references
      u = this->fullrefs_.back();
      this->fullrefs_.pop_back();
      this->isfullref_[u] = false;
      ispropagate = true;
    } else if (!this->readytoorder_.empty()) {
      // if there is a partially referenced vertex with less than U
      // neighbors, it can be ordered right away: it will never be fully
      // referenced
      u = this->readytoorder_.back();
      this->readytoorder_.pop_back();
      ispropagate = true;
    } else if (this->potentialchildren_.size() == 1) {
      // if there is exactly one partially referenced vertex: add it to the
      // order
      u = this->potentialchildren_.back();
      this->potentialchildren_.pop_back();
      this->ispotentialchild_[u] = false;
      ispropagate = true;
    }

    if (!ispropagate) break;
    //
    // add the identified vertex at the end of the order
    this->assignnextrank(u);
  }
  //
  // update the cost of the node
  this->setnodecost(MINPARTIAL);
}

/*******************************************************************************
 Class BBSolver
 - an instance of BBSolver is a solver of the discretization problem using
 branch-and-bound
 *******************************************************************************/
// Public methods
//
// constructor and destructor
BBSolver::BBSolver(Instance* inst,
                   Problem pb,
                   std::string  optionfile,
                   float timelimit):
    DiscretizationSolver(inst,
                         BRANCHANDBOUND,
                         pb,
                         NONE,
                         optionfile,
                         timelimit) {
  this->isfeasible_ = false;
  this->isoptimal_ = false;
  this->bbnodes_ = 1;
  this->treatednodes_ = 0;
  this->nbactivenodes_ = 0;
  this->primalbound_ = inst->nbvertices_;

  if (!optionfile.empty())
  {
    std::ifstream infile(optionfile.c_str());
    if (!infile.is_open()) {
      throwError("revorder: error: the option file could not be opened");
    }
    // read the file line by line
    // one line contains the data relative to one option
    std::string line;
    while (std::getline(infile, line))
    {
      std::istringstream iss(line);
      std::string optionname;
      char buf[100];
      int optionvalue;
      std::string optionstring;

      //read the line and detect format errors
      if (!(iss >> buf)){
        std::cerr << "revorder: error: there is a mistake with the format of "
                     "the option file" <<std::endl;
        throw;
      } // error
      optionname  = std::string(buf);
      if (optionname.find("relaxmodel") != std::string::npos) {
        if (!(iss >> buf)){
          std::cerr << "revorder: error: there is a mistake with the format "
                       "of the option file" <<std::endl;
          throw;
        }
        optionstring = std::string(buf);
        if (!strcmp(optionstring.c_str(),"ccg"))
        {
          this->modeltype_ = CCG;
        }
        else if (!strcmp(optionstring.c_str(),"cycles"))
        {
          this->modeltype_ = CYCLES;
        }
        else if (!strcmp(optionstring.c_str(),"ranks"))
        {
          this->modeltype_ = RANKS;
        }
        else if (!strcmp(optionstring.c_str(),"vertexrank"))
        {
          this->modeltype_ = VERTEXRANK;
        }
        else if (!strcmp(optionstring.c_str(),"witness"))
        {
          this->modeltype_ = WITNESS;
        }
        continue;
      }
      if (!(iss >> optionvalue)){
        std::cerr << "revorder: error: there is a mistake with the format of "
                     "the option file" <<std::endl;
        throw;
      } // error
      //
      if (optionname.find("branchonsmallerindex") !=std::string::npos) {
        branchonsmallerindex_ = (bool) optionvalue;
        continue;
      }
      else if (optionname.find("prunesameneighbors") !=std::string::npos) {
        prunesameneighbors_ = (bool) optionvalue;
        continue;
      }
      else if (optionname.find("checkdominance") !=std::string::npos) {
        if (optionname.find("checkdominancealltree") !=std::string::npos) {
          checkdominancealltree_ = (bool) optionvalue;
          continue;
        }
        checkdominance_ = (bool) optionvalue;
        continue;
      }
      else if (optionname.find("maxdeltadominance") !=std::string::npos) {
        maxdeltadominance_ = optionvalue;
        continue;
      }
      else if (optionname.find("improvedualbound") !=std::string::npos) {
        improvedualbound_ = (bool) optionvalue;
        continue;
      }
      else if (optionname.find("userelaxbound") !=std::string::npos) {
        userelaxbound_ = (bool) optionvalue;
        continue;
      }
      else if (optionname.find("explorebest") !=std::string::npos) {
        explorebest_ = (bool) optionvalue;
        continue;
      }
      else if (optionname.find("exploredepth") !=std::string::npos) {
        if (optionname.find("exploredepthbeforebest") !=std::string::npos) {
          exploredepthbeforebest_ = (bool) optionvalue;
          continue;
        }
        exploredepth_ = (bool) optionvalue;
        continue;
      }
    }
  }
  else
  {
    //
    // set some parameters for the solution of the relaxation in dual bounds
    // computations
    this->modeltype_ = CCG;
    this->branchonsmallerindex_ = true;
    this->prunesameneighbors_ = true;
    this->checkdominance_ = true;
    this->checkdominancealltree_ = true;
    this->maxdeltadominance_ = 1;
    this->improvedualbound_ = false;
    this->userelaxbound_ = false;
    this->explorebest_ = false;
    this->exploredepth_ = false;
    this->exploredepthbeforebest_ = true;
  }
  //
  // build a root node
  this->rootnode_ = new BBNode(*inst);
}
//
BBSolver::~BBSolver() {
  cplexrelax_.end();
  relax_.end();
  env_.end();
  cplexdual_.end();
  modeldual_.end();
  envdual_.end();
  delete rootnode_;
}
//
// main method : called to run the branch-and-bound algorithm
int BBSolver::solve() {
  //
  // run timer
  IloTimer cpuClockTotal(this->env_);
  IloTimer cpuClockInit(this->env_);
  cpuClockTotal.start();
  cpuClockInit.start();

  std::cout << std::endl;
  std::cout << "revorder: "
               "----------------------------------------------------------"
            << std::endl;
  std::cout << "\nrevorder: bb: Run preprocessing procedures "
            <<  std::endl << std::endl;
  //
  // try and reduce the size of the instance right from the beginnning
  this->inst_->computeneighbors();
  this->preallocatelowdegreevertices();
  //
  // enumerate the potential initial cliques
  if (!this->enumeratecliques(this->inst_->L() + 1)) {
    this->isfeasible_ = false;
    return false;
  }
  //
  // eliminate the redundant and dominated cliques
  this->eliminateredundantcliques();
  //
  // solve with greedy and remove cliques that cannot be completed
  this->isfeasible_ = this->greedysolve();
  if (!this->isfeasible_) return false;
  //
  // initialize the clique nodes
  for (Clique* c : this->cliques_) {
    BBNode* cliquenode =new BBNode(*inst_, rootnode_, *c,
                                   this->branchonsmallerindex_);
    this->bbnodesqueue_.push_back(cliquenode);
    cliquenode->primalbound_ = c->greedyobjvalue_;
    cliquenode->setid(this->bbnodes_);
    cliquenode->setnodecost(this->problem_);
    cliquenode->prepropagation();
    cliquenode->postpropagation();
    this->activenodes_[cliquenode->cost_].push_back(cliquenode);
    this->nbactivenodes_++;
    //
    // first propagate the partial order
    this->bbnodes_++;
  }
  std::cout << "revorder: bb: preprocessing created "
            <<  this->bbnodesqueue_.size()
            << " initial clique nodes" << std::endl;
  //
  // sort the bb nodes according to the choice of exploration method
  if (this->exploredepth_ || this->exploredepthbeforebest_) {
    std::make_heap(this->bbnodesqueue_.begin(), this->bbnodesqueue_.end(),
                   bbnodesnborderedcompare);
  } else if (this->explorebest_) {
    std::make_heap(this->bbnodesqueue_.begin(), this->bbnodesqueue_.end(),
                   bbnodesbestcompare);
  } else {
    std::cout << "revorder: bb: need to choose an exploration method in "
                 "branch-and-bound" << std::endl;
    throw;
  }
  //
#ifdef VERBOSE
  std::cout << "revorder: bb: greatest number of ordered vertices in a clique: ";
    std::cout << ", " << this->bbnodesqueue_.front()->nbordered() << " vertices" << std::endl;
#endif
  //
  // compute sets of vertices that must contain at least one
  // partially-referenced vertex
  this->computecliquecuts(this->cliquecutsmaxsize_);
  //
  // identify cycle of vertices with degree smaller than U+1: in each of
  // these cycles, at least one vertex is partially referenced
  this->enumeratelowdegreecycles();
  //
  // initialize the IP used for improved dual bound
  this->initializedualboundIP();
  //
  // set greedy solution as initial primal bound
  this->primalbound_ = this->objvalue_;
  //
  cpuClockInit.stop();

  // Run the branch-and-bound algorithm
  //
  // initialize the ip model to find dual bounds using the linear relaxation
//    IloModel model(this->env_);
  this->relax_ = IloModel(this->env_);
//    this->defineminpartialip(model);
  if (userelaxbound_) {
//        this->createrelaxationmodel(model, this->relax_);
    this->defineminpartialip(this->relax_);
    this->cplexrelax_ = IloCplex(this->env_);
    this->cplexrelax_.extract(this->relax_);
  }
  //
  // treat

  std::cout << std::endl;
  std::cout << "revorder: "
               "----------------------------------------------------------"
            << std::endl;
  std::cout << "\nrevorder: bb: Start the B&B algorithm " <<  std::endl
            << std::endl;

  while (!this->bbnodesqueue_.empty()) {
    this->treatnode(*(this->bbnodesqueue_.front()));
    if (cpuClockTotal.getTime() > this->timelimit_) {
      std::cout << "revorder: bb: time limit has been reached, stop the "
                   "solution algorithm" << std::endl;
      break;
    }
  }
  cpuClockTotal.stop();

  // Display the results
  this->totaltime_ = cpuClockTotal.getTime();
  this->relaxtime_ = 0.0;

  std::cout << std::endl;
  std::cout << "revorder: "
               "----------------------------------------------------------"
            << std::endl;
  std::cout << "revorder: "
               "----------------------------------------------------------"
            << std::endl;
  std::cout << "\nmdjeep: Branch-and-bound solution report: " << std::endl;

  if (isfeasible_)	{
    this->objvalue_ = this->primalbound_;

    // the solution is optimal only if all the bb nodes have been treated
    if (this->bbnodesqueue_.empty()) {
      isoptimal_ = true;
      std::cout << "revorder: the instance was solved to optimality"
                << std::endl;
    } else {
      std::cout << "revorder: the branch-and-bound was stopped before proving"
                   " optimality" << std::endl;
    }

    // reconstruct the solution by including the preallocated vertices
    objvalue_ += this->inst_->preallocatedvertices_.size();
    this->reconstructsolution();
    //
    // display the solution
    std::cout << "revorder: total cpu time                  = " << cpuClockTotal.getTime() << " s" << std::endl;
    std::cout << "revorder: initialization cpu time         = " << cpuClockInit.getTime() << " s" << std::endl;
    std::cout << "revorder: number of explored nodes        = " << this->treatednodes_ << std::endl;
    std::cout << "revorder: number of created nodes        = " << this->bbnodes_ << std::endl;
    std::cout << "revorder: value of the objective function = " << this->objvalue_ << " (after addition of preallocated vertices)" << std::endl;
    //
    // verify the resulting order is indeed a revorder
    this->verifyorder(this->bestrank_);
  }
  else {
    std::cout << "revorder: the instance is not discretizable!" << std::endl;
  }
  return isfeasible_;
}
//
// treat a branch-and-bound node (compute bounds and branch)
void BBSolver::treatnode(BBNode& node) {
  this->treatednodes_++;
  if (std::remainder(this->treatednodes_, 100) == 0) {
    std::cout << "revorder: bb: treated nodes = " << this->treatednodes_
              << ", nodes in the queue = " << this->bbnodesqueue_.size()
              << ", nodes in memory = " << this->nbactivenodes_
              << ", primal bound = " << this->primalbound_ <<  std::endl;
  }
  //
  // remove the bb node from the list
  //
  // make sure to maintain the heap first
  if (this->isfeasible_) {
    if (this->exploredepth_) {
      std::pop_heap(this->bbnodesqueue_.begin(),
                    this->bbnodesqueue_.end(), bbnodesnborderedcompare);
    } else if (this->explorebest_ || this->exploredepthbeforebest_) {
      std::pop_heap(this->bbnodesqueue_.begin(),
                    this->bbnodesqueue_.end(), bbnodesbestcompare);
    }
  } else {
    if (this->explorebest_) {
      std::pop_heap(this->bbnodesqueue_.begin(),
                    this->bbnodesqueue_.end(), bbnodesbestcompare);

    } else {
      std::pop_heap(this->bbnodesqueue_.begin(),
                    this->bbnodesqueue_.end(), bbnodesnborderedcompare);
    }
  }

  // actually remove the node from the queue
  this->bbnodesqueue_.pop_back();

  // update the primal bound and prune the node if it is a leaf of the
  // enumeration tree
  if (node.nbordered() == this->inst_->nbvertices_) {
#ifdef VERBOSE
    std::cout << "revorder: bb: reached a leaf at node " << node.id() << ": depth = " << node.depth();
#endif
    node.primalbound_ = this->getobjvalue(node.rank_, node.nbrefs_);
    //
    //  update the primal bound if improved
    if (node.primalbound_ < this->primalbound_) {
      //
      // swap the search method if this is the first feasible solution
      std::cout << "revorder: bb: the primal bound has been improved, value = "
                << node.primalbound_ << std::endl;
      if (!this->isfeasible_) {
        if (this->exploredepthbeforebest_) {
          std::make_heap(this->bbnodesqueue_.begin(),
                         this->bbnodesqueue_.end(), bbnodesbestcompare);
        }
        this->isfeasible_ = true;
      }
      this->primalbound_ = node.primalbound_;
      this->bestnbfullref_ = this->getnbfullref(node.rank_, node.nbrefs_);
      for (Vertex* v : inst_->vertices_) {
        this->bestrank_[v] = node.rank(v);
      }
    }
    this->erasenodefromall(&node);
  } else {
    // otherwise, compute dual and primal bound and prepare for next node

    // prune the node if no potential child node
    if (node.potentialchildren_.empty()) {
      //
#ifdef VERBOSE
      std::cout << "revorder: bb: prune node " << node.id() << ": ";
            std::cout << ": no feasible solution on this branch" << std::endl;
#endif
      //
      this->erasenodefromall(&node);
    } else {
      // compute a dual bound based on the ordered vertices
      this->computedualbound(node);
      //
      // prune the node by using bounds
      if (node.dualbound_ >= this->primalbound_) {
#ifdef VERBOSE
        std::cout << "revorder: bb: prune node " << node.id() << ": ";
                std::cout << "using bounds: primal = " << this->primalbound_;
                std::cout << " ; dual = " << node.dualbound_ << std::endl;
#endif
        this->erasenodefromall(&node);
      } else {
        // otherwise, create new nodes by branching
        this->branch(node);
        node.istreated_ = true;
//                if (!this->checkdominancealltree_) {
//                    this->erasenodefromall(&node);
//                }
      }
    }
  }
  //
  // treat next node in the queue
  // since it is a heap, the front node is always the greatest in the
  // implemented order
#ifdef VERBOSE
  if (!this->bbnodesqueue_.empty()) {
        BBNode* nextnode = this->bbnodesqueue_.front();
        std::cout << '\n' << "revorder: bb: treat node " << nextnode->id() << " ; depth = " << nextnode->depth() << " ; ";
        std::cout << nextnode->nbordered() << " ordered vertices ; " << nextnode->nbpartialref() << " partially referenced vertices" << std::endl;
    }
#endif
}

// create new nodes by branching
void BBSolver::branch(BBNode& node) {
  // create the children of the input node
  // propagate the order starting from each incomplete order to detect
  // dominatedorders
  std::vector<BBNode*> childnodes;
  std::map<BBNode*, bool> isdominated;
  std::map<Vertex*, bool> issymmetric;
  //
  // we start by checking diverse pruning rules that need to be applied
  // before dominance
  for (int i = 0; i < node.potentialchildren_.size() ; i++) {
    Vertex* u = node.potentialchildren_[i];
    issymmetric[u] = false;

    if (!this->prunesameneighbors_) continue;

    for (int j = 0; j < i; j++) {
      Vertex* v = node.potentialchildren_[j];
      if (issymmetric[v]) continue;

      // if two vertices that can be chosen for branching have the exact same
      // unordered neighbors, only that with smaller number of references
      // needs to be considered for branching at this stage
      bool sameneighbors = true;
      for (Vertex* neighbor : u->neighbors_) {
        if (!node.isordered(neighbor)) {
          if ( !v->isneighbor_[neighbor] ) {
            sameneighbors = false;
            break;
          }
        }
      }
      if (sameneighbors) {
#ifdef VERBOSE
        std::cout << "revorder: bb: prune node: two potential children have same neighbors: vertices " << u->id_ << " and " << v->id_ <<  std::endl;
#endif
        if (node.nbrefs_[u] < node.nbrefs_[v]) {
          issymmetric[v] = true;
        } else if (node.nbrefs_[v] < node.nbrefs_[u]) {
          issymmetric[u] = true;
        } else {
          if (u->id_ < v->id_) issymmetric[v] = true;
          else if (v->id_ < u->id_) issymmetric[u] =true;
        }
      }
    }
  }

  for (Vertex* v : node.potentialchildren_) {
    if (issymmetric[v]) continue;  // do not consider the vertices that are
    // symmetric

    BBNode* child = new BBNode(node);
    //
    // in MINPARTIAL, we can always make the arbitrary decision that if a
    // vertex must be ordered when it is still partially referenced, then it
    // is the vertex with smallest index among all the potential children; as
    // a consequence the vertices with smaller index must be removed from the
    // list of potential children
    if ( this->branchonsmallerindex_ ) {
      std::vector<Vertex*>::iterator itv = child->potentialchildren_.begin();
      while (itv < child->potentialchildren_.end()) {
        if ( ((*itv)->id_ < v->id_) ) {
          child->ispotentialchild_[*itv] = false;
          child->potentialchildren_.erase(itv);
          child->mustbefullref_[*itv] = true;
          //
          // if a vertex must be fully-referenced, but it has exactly U
          // neighbors, it will for sure come after all its neighbors in the
          // order
          if (child->maxrefs_[*itv] <= this->inst_->U()) {
            for (Vertex* u : v->neighbors_) {
              if (child->mustbefullref_[u]) {
                if (child->maxrefs_[u] >= this->inst_->U()+1) {
                  child->maxrefs_[u]--;
                }
              } else {
                child->maxrefs_[u]--;
              }
            }
          }
        } else {
          itv++;
        }
      }
    }

    child->assignnextrank(v);
    child->prepropagation();
    isdominated[child] = false;
    childnodes.push_back(child);
  }
  //
  // check for classical dominance
  for (BBNode* n1 : childnodes) {
    if (!this->checkdominance_) continue;  // do not check dominance if option
    // is deactivated

    if (isdominated[n1]) continue;
    for (BBNode* n2 : childnodes) {
      if (n2 == n1) {
        continue;
      } else if (isdominated[n2]) {
        continue;
      } else if (this->dominates(*n2, *n1)) {
        // classical domination
        isdominated[n1] = true;
        //
#ifdef VERBOSE
        std::cout << "revorder: bb: prune node: dominance among children" <<  std::endl;
#endif
        break;
      } else if (this->dominates(*n1, *n2)) {
        isdominated[n2] = true;
        //
#ifdef VERBOSE
        std::cout << "revorder: bb: prune node: dominance among children" <<  std::endl;
#endif
      }
    }
  }
  //
  // scan all the bb nodes in the queue to delete the dominated ones if
  // option is activated
  if ((this->checkdominancealltree_) && (this->checkdominance_)) {
    for (BBNode* n : childnodes) {
      if (isdominated[n]) continue;
      for (int deltacost = -this->maxdeltadominance_ ;
           deltacost <= this->maxdeltadominance_; deltacost++) {
        if (n->cost_ <= deltacost) continue;
        if (!isdominated[n]) {
          isdominated[n] =
              checkdominancewithlist(n,
                                     this->activenodes_[n->cost_ - deltacost]);
        }
      }
    }
  }
  //
  // delete the redundant nodes and add the others to the node queue
  for (BBNode* n : childnodes) {
    if (isdominated[n]) {
      delete n;
    } else {
      // make sure to maintain the heap while pushing the new node in the queue
      this->bbnodes_++;
      n->setid(this->bbnodes_);
      node.addtochildren(n);
      n->postpropagation();
      this->bbnodesqueue_.push_back(n);
      this->activenodes_[n->cost_].push_back(n);
      this->nbactivenodes_++;
      if (this->exploredepth_) {
        std::push_heap(this->bbnodesqueue_.begin(),
                       this->bbnodesqueue_.end(), bbnodesnborderedcompare);
      } else if (this->explorebest_) {
        std::push_heap(this->bbnodesqueue_.begin(),
                       this->bbnodesqueue_.end(), bbnodesbestcompare);
      } else if (this->exploredepthbeforebest_) {
        if (!this->isfeasible_) {
          std::push_heap(this->bbnodesqueue_.begin(),
                         this->bbnodesqueue_.end(), bbnodesnborderedcompare);
        } else {
          std::push_heap(this->bbnodesqueue_.begin(),
                         this->bbnodesqueue_.end(), bbnodesbestcompare);
        }
      }
    }
  }
#ifdef VERBOSE
  std::cout << "revorder: bb: " << this->bbnodesqueue_.size() << " nodes in the queue" << std::endl;
#endif
}
//
// check the dominance of input node with every node in the input vector
bool BBSolver::checkdominancewithlist(BBNode* n1,
                                      std::vector<BBNode*>& nodelist) {
  std::vector<BBNode*>::iterator itnode = nodelist.begin();

  while (itnode < nodelist.end()) {
    BBNode* n2 = (*itnode);
    if (this->dominates(*n2, *n1)) {
      //
#ifdef VERBOSE
      std::cout << "revorder: bb: prune node: dominance of potential child ";
            if (n2->istreated_) std::cout << " with treated node" <<  std::endl;
            else std::cout << "with queue " << std::endl;
            std::cout << "revorder: bb: depth: dominant = " << n2->depth() << ", dominated = " << n1->depth() << std::endl;
            std::cout << "revorder: bb: costs: dominant = " << n2->cost_ << ", dominated = " << n1->cost_ << std::endl;
#endif
      //
      return true;
    } else if (this->dominates(*n1, *n2)) {
      //
#ifdef VERBOSE
      std::cout << "revorder: bb: prune node: dominance of created node ";
            if (n2->istreated_) std::cout << " already treated" <<  std::endl;
            else std::cout << "still in queue" << std::endl;
            std::cout << "revorder: bb: depth: dominant = " << n1->depth() << ", dominated = " << n2->depth() << std::endl;
            std::cout << "revorder: bb: costs: dominant = " << n1->cost_ << ", dominated = " << n2->cost_ << std::endl;
#endif
      //
      erasenodefromall(*itnode);
    } else {
      itnode++;
    }
  }
  return false;
}

//
// erase a node from every list where it appears
void BBSolver::erasenodefromall(BBNode* node) {
  // first erase it from the list of children of its father
  for (auto itn = node->getfather()->children_.begin();
       itn < node->getfather()->children_.end(); itn++) {
    if (*itn == node) {
      node->getfather()->children_.erase(itn);
      break;
    }
  }

  // then erase its descendants if any
  for (BBNode* child: node->children_) {
    this->erasealldescendants(child);
  }

  // erase from active nodes
  for (auto itn = activenodes_[node->cost_].begin();
       itn < activenodes_[node->cost_].end(); itn++) {
    if (*itn == node) {
      activenodes_[node->cost_].erase(itn);
      this->nbactivenodes_--;
      break;
    }
  }
  if (!node->istreated_) {
    for (auto itn = bbnodesqueue_.begin(); itn < bbnodesqueue_.end(); itn++) {
      if (*itn == node) {
        bbnodesqueue_.erase(itn);
        break;
      }
    }
  }
  delete node;
}

// erase all the descendants of a node from all list of nodes and delete them
void BBSolver::erasealldescendants(BBNode* node) {

  for (BBNode* child: node->children_) {
    erasealldescendants(child);
  }
  for (auto itn = activenodes_[node->cost_].begin(); itn < activenodes_[node->cost_].end(); itn++) {
    if (*itn == node) {
      activenodes_[node->cost_].erase(itn);
      this->nbactivenodes_--;
      break;
    }
  }
  if (!node->istreated_) {
    for (auto itn = bbnodesqueue_.begin(); itn < bbnodesqueue_.end(); itn++) {
      if (*itn == node) {
        bbnodesqueue_.erase(itn);
        break;
      }
    }
  }
  delete node;
}

// compute a dual bound from the partial order described in the input node
double BBSolver::computedualbound(BBNode& node) {
  //
  // compute a simple bound by just looking at the ordered vertices
  //
  // first get the number of partially referenced vertices in the order
  int nbpartials = node.nbordered() - node.nbfullref() - this->inst_->L();
  //
  // then aknowledge that vertices with less than U neighbors cannot be
  // fully-referenced; but beware that we know that one potential child will
  // be partially-referenced, so do not count it twice
  int nbpotentialpartials = 1;
  for (Vertex* v: this->inst_->vertices_) {
    if (!node.isordered(v)) {
      if (v->degree_ <= this->inst_->U() - 1) {
        if (node.ispotentialchild_[v]) {
          nbpartials++;
          nbpotentialpartials = 0;
        }
        else{
          nbpartials++;
        }
      }
    }
  }

  // finally for each edge linking two vertices with exactly U neigbors, one
  // will be partially referenced; still need to beware with the potential
  // children of the node
  std::map<Vertex*,bool> inpartialedge;
  for (Vertex* u: this->inst_->vertices_) inpartialedge[u] = false;
  for (Vertex* u: this->inst_->vertices_) {
    if (!node.isordered(u)
        && (!inpartialedge[u])
        && (u->degree_ == this->inst_->U())) {
      for (Vertex* v: u->neighbors_) {
        if (!node.isordered(v)
            && (!inpartialedge[v])
            && (v->degree_ == this->inst_->U())) {
          nbpartials++;
          inpartialedge[u] = true;
          inpartialedge[v] = true;
          if (node.ispotentialchild_[v]) nbpotentialpartials = 0;
          break;
        }
      }
    }
  }
  nbpartials += nbpotentialpartials;
  int trivialbound = nbpartials;
  //
  // update current dual bound if improved
  node.dualbound_ = std::max(node.dualbound_, trivialbound);


  if ((!this->improvedualbound_) && (!this->userelaxbound_) ) {
    return node.dualbound_;
  }

  /////////////////////////////////////////////////////////////////////////////
  // Improve the trivial bound by considering several cuts
  /////////////////////////////////////////////////////////////////////////////

  if (this->improvedualbound_) {
    for (Vertex* v : this->inst_->vertices_) {
      if (node.isordered(v)) {
        if (node.nbrefs(v) >= this->inst_->U()) {
          this->ispartial_[v].setBounds(0, 0);
        }
        else {
          this->ispartial_[v].setBounds(1,1);
        }
      }
      else if (node.mustbefullref_[v]) {
        this->ispartial_[v].setBounds(0, 0);
      }
      else if (v->degree_ <= this->inst_->U() - 1) {
        this->ispartial_[v].setBounds(1,1);
      }
      else {
        this->ispartial_[v].setBounds(0, 1);
      }
    }
    //
    // at least one among the potential children will be partially referenced
    IloExpr sumpartialinpotentials(this->envdual_);
    for (Vertex* v : node.potentialchildren_) {
      sumpartialinpotentials += this->ispartial_[v];
    }
    IloRange ctPartialsInPotentials(this->envdual_, -sumpartialinpotentials, -1);
    this->modeldual_.add(ctPartialsInPotentials);
    sumpartialinpotentials.end();
    //
    // solve the IP
    this->cplexdual_.solve();
    IloAlgorithm::Status statusimproved = this->cplexdual_.getStatus();
    int improvedbound =  this->inst_->nbvertices_;
    if (statusimproved==IloAlgorithm::Infeasible) {
      node.dualbound_ =  this->inst_->nbvertices_;
#ifdef VERBOSE
      std::cout << "revorder: bb: improved dual bound IP is infeasible: prune the node" << std::endl;
#endif
    }
    else {
      improvedbound = ceil(this->cplexdual_.getObjValue()) - this->inst_->L();
      if (improvedbound > node.dualbound_ ){
        node.dualbound_ =  improvedbound;
      }
    }

    this->modeldual_.remove(ctPartialsInPotentials);
    ctPartialsInPotentials.end();

#ifdef VERBOSE
    std::cout << "revorder: node dual bound = " << improvedbound << " ; trivialbound = " << trivialbound << std::endl;
#endif
  }

  /////////////////////////////////////////////////////////////////////////////
  // Compute a dual bound by solving the LP relaxation of a chosen model
  /////////////////////////////////////////////////////////////////////////////

  // Start searching for better bounds only if trivial bound is not too far from primal bound and a significant number of nodes have been treated to improve primal bound
  if ( (this->userelaxbound_) && (this->treatednodes_ >= 1000) && (trivialbound * 2.0 >= this->primalbound_)) {
    // fix all the variables related to the vertices that already in the current incomplete order
    for (Vertex* v1 : this->inst_->vertices_) {
      if (node.rank(v1) >= this->inst_->L() + 2) {
        if (node.nbrefs(v1) >= this->inst_->U()) {
          isfullref_[v1].setBounds(1, 1);
        }
        else {
          isfullref_[v1].setBounds(0, 0);
        }
      }
      else if ((node.rank(v1) >= 1) && (node.rank(v1) <= this->inst_->L()+1)) {
        isfullref_[v1].setBounds(1, 1);
      }
      else {
        if (node.mustbefullref_[v1] == true) {
          isfullref_[v1].setBounds(1,1);
        }
        else {
          isfullref_[v1].setBounds(0, 1);
        }
      }
      for (Vertex* v2 : v1->neighbors_) {
        if ((node.rank(v1) < 0) && (node.rank(v2) < 0)) isbefore_[v1][v2].setBounds(0, 1);
        else if ( (node.rank(v1) < 0) && (node.rank(v2) >= 1)) isbefore_[v1][v2].setBounds(0, 0);
        else if ((node.rank(v1) >= 1) && (node.rank(v2) < 0)) isbefore_[v1][v2].setBounds(1, 1);
        else if ((node.rank(v1) >= 1) && (node.rank(v2) >= 1) && (node.rank(v1) < node.rank(v2)) ) isbefore_[v1][v2].setBounds(1, 1);
        else if ((node.rank(v1) >= 1) && (node.rank(v2) >= 1) && (node.rank(v1) > node.rank(v2)) ) isbefore_[v1][v2].setBounds(0, 0);
        else {
          cout << node.rank(v1) << "; " << node.rank(v2) << endl;
          throwError("abnormal rank values");
        }
      }
    }

    IloExpr sumnotallfullref(this->env_);
    int maxnbfullref;
    for (Vertex* v : node.potentialchildren_) {
      sumnotallfullref += isfullref_[v];
    }
    maxnbfullref = node.potentialchildren_.size() - 1;
    IloRange cons(this->env_, sumnotallfullref, maxnbfullref);
    relax_.add(cons);
    cplexrelax_.setParam(IloCplex::SimDisplay, 0);
//        cplexrelax_.setParam(IloCplex::Param::ParamDisplay, 0);
    cplexrelax_.setParam(IloCplex::Threads, 1);
    cplexrelax_.setParam(IloCplex::MIPDisplay, 0);
    cplexrelax_.setParam(IloCplex::TuningDisplay, 0);
    cplexrelax_.setOut(cplexrelax_.getEnv().getNullStream());
    bool updateprimal_ = false;
    if ( updateprimal_ &&  (node.nbordered() >= 0.7 * this->inst_->nbvertices_) ) {
      cplexrelax_.setParam(IloCplex::NodeLim, 9223372036800000000);
      cplexrelax_.setParam(IloCplex::MIPSearch, 1);
      if ((this->modeltype_ == CCG) || (this->modeltype_ == WITNESS)) {
        cplexrelax_.use(CyclesLazyConstraintsBB(cplexrelax_.getEnv(), *(this->inst_), *this));
      }
    }
    else {
      cplexrelax_.setParam(IloCplex::NodeLim, 0);
    }

    cplexrelax_.solve();
    IloAlgorithm::Status status = cplexrelax_.getStatus();
    //
    // if the relaxation is infeasible, the node can be pruned : set dual bound to negative value to state this
    if (status != IloAlgorithm::Infeasible) {
      int relaxbound = ceil(cplexrelax_.getBestObjValue());
      //
#ifdef VERBOSE
      std::cout << "revorder: bb: trivial dual bound = " << trivialbound << " ; relaxation bound = " << relaxbound;
            std::cout << " (best primal bound = " << this->primalbound_ << ")" << std::endl;
#endif
      if ( updateprimal_ &&  (node.nbordered() >= 0.7 * this->inst_->nbvertices_) ) {
        if (relaxbound < this->primalbound_) {
          this->primalbound_ = relaxbound;
          std::cout << "best primal bound updated = " << this->primalbound_ << std::endl;
          this->bestnbfullref_ = this->inst_->nbvertices_ - this->primalbound_;
          float tolerance = 1e-06;
          std::map<Vertex*, std::vector<Vertex*> > adjlist;
          for (Vertex* v : this->inst_->vertices_) {
            adjlist[v] = std::vector<Vertex*>();
          }

          for (Vertex* u : this->inst_->vertices_) {
            for (Vertex* v : u->neighbors_) {
              if (cplexrelax_.getValue(this->isbefore_[u][v]) >= 1 - tolerance) {
                adjlist[u].push_back(v);
              }
            }
          }
          //
          // search for the root of the digraph
          Vertex* root = nullptr;
          for (Vertex* v : this->inst_->vertices_) {
            if (node.rank(v) == 1) {
              root = v;
            }
          }
          // Search for a topological order that will be valid only if the digraph is
          // acyclic
          std::vector<Vertex*> reverseorder;
          std::map<Vertex*, int> rank;
          this->topologicalorder(root, adjlist, reverseorder, rank);
          // determine whether the digraph is cyclic or not
          //
          std::vector<std::pair<Vertex*, Vertex*> > reverseedges;
          bool iscyclic = this->getreverseedges(adjlist, reverseorder, rank, reverseedges);
          if (iscyclic) {
            std::cout << "revorder: error: the final integer solution is cyclic" << std::endl;
            throw;
          }
          for (Vertex* v : this->inst_->vertices_) {
            this->bestrank_[v] = rank[v];
          }
        }
      }
      //
      // update current dual bound if improved
      if (relaxbound > node.dualbound_) {
        node.dualbound_ = relaxbound;
        //
#ifdef VERBOSE
        std::cout << "revorder: bb: new dual bound = " << relaxbound << std::endl;
                std::cout << "revorder: bb: number of ordered vertices = " << node.nbordered() << std::endl;
#endif
        //
      }
    }
    else{
      node.dualbound_ =  this->inst_->nbvertices_;
#ifdef VERBOSE
      std::cout << "revorder: bb: dual bound relaxation is infeasible: prune the node" << std::endl;
#endif
    }

    relax_.remove(cons);
    cons.end();
  }
  return node.dualbound_;
}

// initialize the IP model for improved dual bound
void BBSolver::initializedualboundIP() {
  this->modeldual_ = IloModel(this->envdual_);
  this->cplexdual_ = IloCplex(this->modeldual_);

  IloExpr obj(this->envdual_);
  char name[256];
  for (Vertex* v : this->inst_->vertices_) {
    this->ispartial_[v] = IloBoolVar(this->envdual_);
    sprintf(name, "ispartial%i", v->id_);
    this->ispartial_[v].setName(name);
    obj += this->ispartial_[v];
  }
  this->modeldual_.add(IloMinimize(this->envdual_, obj));
  obj.end();
  //
  // if we could identify cliques that contain at least one partially-referenced vertex, add a constraint to enforce this; the two-cliques need not be checked
  for (Clique* c : this->cliqueswithpartialref_) {
    IloExpr sumpartialsinclique(this->envdual_);
    for (Vertex* v : c->vertices_) sumpartialsinclique += this->ispartial_[v];
    this->modeldual_.add(sumpartialsinclique >= this->nbpartialinclique_[c]);
    sumpartialsinclique.end();
  }
  //
  // if we could identify cycles composed of low degree vertices add a cut specifying that the vertices included in such cycles must include at least as many partially referenced vertices as there are cycles
  IloRangeArray ctaryLowDegreeCycles(this->envdual_);
  int nbcons = 0;
  for (int i = 0; i < this->lowdegreecycles_.size(); i++) {
    std::vector<Vertex*> cycle = this->lowdegreecycles_[i];
    IloExpr sumpartialsincycle(this->envdual_);
    for (Vertex* v : cycle) {
      sumpartialsincycle += this->ispartial_[v];
    }
    ctaryLowDegreeCycles.add(sumpartialsincycle >= this->nbpartialsincycle_[i]);
    sprintf(name, "ctaryLowDegreeCycles_%i", nbcons);
    ctaryLowDegreeCycles[nbcons++].setName(name);
    sumpartialsincycle.end();
  }
  this->modeldual_.add(ctaryLowDegreeCycles);
  ctaryLowDegreeCycles.end();
  //
  // load the integer problem and set display parameters
  this->cplexdual_.setParam(IloCplex::MIPDisplay, 0);
  this->cplexdual_.setParam(IloCplex::SimDisplay, 0);
  this->cplexdual_.setParam(IloCplex::TiLim, 10);
  this->cplexdual_.setParam(IloCplex::Threads, 1);
//    this->cplexdual_.setParam(IloCplex::Param::ParamDisplay, 0);
}