# source:trunk/Cbc/src/CbcSymmetry.cpp@2049

Last change on this file since 2049 was 2049, checked in by forrest, 6 years ago

File size: 36.3 KB
Line
1/* \$Id: CbcSymmetry.cpp 925 2012-11-27 19:11:04Z stefan \$
2 *
3 * hacked from CouenneSymmetry.cpp
4 * Name:    CbcSymmetry.cpp
5 * Author:  Jim Ostrowski (the good bits - rest JJHF)
6 * Purpose: methods for exploiting symmetry
7 * Date:    October 13, 2010
8 *
10 */
11
12#include <stdio.h>
13
14#ifdef COIN_HAS_NTY
15
16#include <cassert>
17#include <vector>
18#include <algorithm>
19#include <ostream>
20#include <iterator>
21
22#include "CbcSymmetry.hpp"
23#include "CbcBranchingObject.hpp"
24#include "CoinTime.hpp"
25/* Deliberately not threadsafe to save effort
26   Just for statistics
27   and not worth gathering across threads
28   can redo later
29 */
30static int nautyBranchCalls_ = 0;
31static int nautyBranchSucceeded_ = 0;
32static int nautyFixCalls_ = 0;
33static int nautyFixSucceeded_ = 0;
34static double nautyTime_ = 0.0;
35static double nautyFixes_= 0.0;
36static double nautyOtherBranches_ = 0.0;
37
38void Node::node(int i, double c , double l, double u, int cod, int s){
39  index = i;
40  coeff = c;
41  lb = l;
42  ub = u;
43  color = -1;
44  code = cod;
45  sign = s;
46}
47
48inline bool CbcSymmetry::compare (register Node &a, register Node &b) const {
49  if(a.get_code() == b.get_code() )
50    if(a.get_coeff() == b.get_coeff() )
51      if(a.get_sign() == b.get_sign() )
52        if( fabs ( a.get_lb() - b.get_lb() ) <= COUENNE_HACKED_EPS )
53          if( fabs ( a.get_ub() - b.get_ub() ) <= COUENNE_HACKED_EPS )
54            return 1;
55  return 0;
56}
57
58void CbcSymmetry::Compute_Symmetry() const{
59
60
61  std::sort(node_info_. begin (), node_info_. end (), node_sort);
62
63  for (std::vector <Node>:: iterator i = node_info_. begin (); i != node_info_. end (); ++i)
64    (*i).color_vertex(-1);
65
66  int color = 1;
67  for (std::vector <Node>:: iterator i = node_info_. begin (); i != node_info_. end (); ++i) {
68    if( (*i).get_color() == -1){
69      (*i).color_vertex(color);
70#ifdef PRINT_MORE
71      printf ("Graph vertex %d is given color %d\n", (*i).get_index(), color);
72#endif
73      nauty_info_ -> color_node((*i).get_index(), color);
74      for (std::vector <Node>:: iterator j = i+1; j != node_info_. end (); ++j)
75        if( compare( (*i) , (*j) ) ==1){
76          (*j).color_vertex(color);
77          nauty_info_ -> color_node((*j).get_index(),color);
78#ifdef PRINT_MORE
79          printf ("Graph vertex %d is given color %d, the same as vertex %d\n", (*j).get_index(), color, (*i).get_index());
80#endif
81        }
82      //       else
83      // j = node_info_. end();
84      color++;
85    }
86  }
87
88  //Print_Orbits ();
89  nauty_info_ -> computeAuto();
90  //Print_Orbits ();
91}
92
93int
94CbcSymmetry::statsOrbits(CbcModel * model, int type) const
95{
96  char general[200];
97  int returnCode=0;
98  if (type) {
99    double branchSuccess=0.0;
100    if (nautyBranchSucceeded_)
101      branchSuccess = nautyOtherBranches_/nautyBranchSucceeded_;
102    double fixSuccess=0.0;
103    if (nautyFixSucceeded_)
104      fixSuccess = nautyFixes_/nautyFixSucceeded_;
105    sprintf(general,"Orbital branching tried %d times, succeeded %d times - average extra %7.3f, fixing %d times (%d, %7.3f) - %.2f seconds",
106            nautyBranchCalls_,nautyBranchSucceeded_,branchSuccess,
107            nautyFixCalls_,nautyFixSucceeded_,fixSuccess,nautyTime_);
108  } else {
109    returnCode = nauty_info_->getNumGenerators();
110    if (returnCode)
111      sprintf (general,"Nauty: %d orbits, %d generators, group size: %g - dense size %d, sparse %d - going %s",
112               nauty_info_->getNumOrbits(),
113               nauty_info_ -> getNumGenerators () ,
114               nauty_info_ -> getGroupSize (),
115               whichOrbit_[0],whichOrbit_[1],nauty_info_->isSparse() ? "sparse" : "dense");
116    else
117      sprintf(general,"Nauty did not find any useful orbits");
118  }
119  model->messageHandler()->message(CBC_GENERAL,
120                                   model->messages())
121    << general << CoinMessageEol ;
122  return returnCode;
123}
124
125void CbcSymmetry::Print_Orbits () const {
126
127  //printf ("num gens = %d, num orbits = %d \n", nauty_info_ -> getNumGenerators(), nauty_info_ -> getNumOrbits() );
128
129  std::vector<std::vector<int> > *new_orbits = nauty_info_->getOrbits();
130
131  printf ("Nauty: %d generators, group size: %.0g",
132          //  nauty_info_->getNumOrbits(),
133          nauty_info_ -> getNumGenerators () ,
134          nauty_info_ -> getGroupSize ());
135
136  int nNonTrivialOrbits = 0;
137
138  for (unsigned int i = 0; i < new_orbits -> size(); i++) {
139    if ((*new_orbits)[i].size() > 1)
140      nNonTrivialOrbits++;
141    else continue;
142
143    // int orbsize = (*new_orbits)[i].size();
144    // printf( "Orbit %d [size: %d] [", i, orbsize);
145    // copy ((*new_orbits)[i].begin(), (*new_orbits)[i].end(),
146    //    std::ostream_iterator<int>(std::cout, " "));
147    // printf("] \n");
148  }
149
150  printf (" (%d non-trivial orbits).\n", nNonTrivialOrbits);
151
152#if 1
153  if (nNonTrivialOrbits) {
154
155    int orbCnt = 0;
156
157    std::vector<std::vector<int> > *orbits = nauty_info_ -> getOrbits ();
158
159    for   (std::vector <std::vector<int> >::iterator i = orbits -> begin ();  i != orbits -> end (); ++i) {
160
161      printf ("Orbit %d: ", orbCnt++);
162
163      for (std::vector<int>::iterator j = i -> begin (); j != i -> end (); ++j)
164        printf (" %d", *j);
165
166      printf ("\n");
167    }
168  }
169#endif
170
171
172#if 0
173  if (nNonTrivialOrbits)
174    for (int i=0; i< nVars (); i++) {
175
176      std::vector< int > *branch_orbit = Find_Orbit (i);
177
178      if (branch_orbit -> size () > 1) {
179        printf ("x%04d: ", i);
180
181        for (std::vector<int>::iterator it = branch_orbit -> begin (); it != branch_orbit -> end (); ++it)
182          printf ("%d ", *it);
183        printf ("\n");
184      }
185    }
186#endif
187  delete new_orbits;
188}
189void
190CbcSymmetry::fillOrbits()
191{
192  for (int i=0;i<numberColumns_;i++)
193    whichOrbit_[i]=-1;
194  numberUsefulOrbits_=0;
195
196  std::vector<std::vector<int> > *orbits = nauty_info_ -> getOrbits ();
197
198  for   (std::vector <std::vector<int> >::iterator i = orbits -> begin ();  i != orbits -> end (); ++i) {
199    int nUseful=0;
200    int jColumn=-2;
201    for (std::vector<int>::iterator j = i -> begin (); j != i -> end (); ++j) {
202      int iColumn=*j;
203      if (iColumn<numberColumns_) {
204        whichOrbit_[iColumn]=numberUsefulOrbits_;
205        nUseful++;
206        jColumn=iColumn;
207      }
208    }
209    if (nUseful>1) {
210      numberUsefulOrbits_++;
211    } else if (jColumn>=0) {
212      assert (nUseful);
213      whichOrbit_[jColumn]=-2;
214    }
215  }
216  delete orbits;
217}
218int
219CbcSymmetry::largestOrbit(const double * lower, const double * upper) const
220{
221  int * counts = new int[numberUsefulOrbits_];
222  memset(counts,0,numberUsefulOrbits_*sizeof(int));
223  for (int i=0;i<numberColumns_;i++) {
224    int iOrbit=whichOrbit_[i];
225    if (iOrbit>=0) {
226      if (lower[i]==0.0&&upper[i]==1.0)
227        counts[iOrbit]++;
228    }
229  }
230  int iOrbit=-1;
231  int maxOrbit=0;
232  for (int i=0;i<numberUsefulOrbits_;i++) {
233    if (counts[i]>maxOrbit) {
234      maxOrbit=counts[i];
235      iOrbit=i;
236    }
237  }
238  delete [] counts;
239  return iOrbit;
240}
241
242std::vector<int> *CbcSymmetry::Find_Orbit(int index) const{
243
244  std::vector<int> *orbit = new std::vector <int>;
245  int which_orbit = -1;
246  std::vector<std::vector<int> > *new_orbits = nauty_info_->getOrbits();
247
248  for (unsigned int i = 0; i < new_orbits -> size(); i++) {
249    for (unsigned int j = 0; j < (*new_orbits)[i].size(); j++) {
250      //   for (std::vector <int>:: iterator j = new_orbits[i].begin(); new_orbits[i].end(); ++j){
251      if( (*new_orbits)[i][j] ==  index)
252        which_orbit = i;
253    }
254  }
255
256  //  for (std::vector <int>:: iterator j = new_orbits[which_orbit].begin(); new_orbits[which_orbit].end(), ++j)
257  for (unsigned int j = 0; j < (*new_orbits)[which_orbit].size(); j++)
258    orbit -> push_back ((*new_orbits)[which_orbit][j]);
259
260  delete new_orbits;
261
262  return orbit;
263}
264
265
266void CbcSymmetry::ChangeBounds (const double * new_lb, const double * new_ub,
267                                int num_cols, bool justFixedAtOne) const {
268  if (justFixedAtOne)
269    nautyFixCalls_++;
270  else
271    nautyBranchCalls_++;
272  std::sort(node_info_. begin (), node_info_. end (), index_sort);
273
274  for (int  i = 0; i < num_cols; i++) {
275    //   printf("Var %d  lower bound: %f   upper bound %f \n", i, new_lb[i], new_ub[i]);
276
277    assert (node_info_[i].get_index () == i);
278    double newLower = new_lb[i];
279    double newUpper = new_ub[i];
280    if (justFixedAtOne) {
281      // free up all fixed at zero
282      if (!newLower)
283        newUpper = 1.0;
284    }
285    node_info_[i ].bounds ( newLower , newUpper );
286    //printf("Var %d  INPUT lower bound: %f   upper bound %f \n", i, node_info_[i].get_lb(), node_info_[i].get_ub());
287  }
288}
289void CbcSymmetry::setupSymmetry (const OsiSolverInterface & solver) {
290  const double *objective = solver.getObjCoefficients() ;
291  const double *columnLower = solver.getColLower() ;
292  const double *columnUpper = solver.getColUpper() ;
293  int numberColumns = solver.getNumCols() ;
294  int numberRows = solver.getNumRows();
295  int iRow, iColumn;
296
297  // Row copy
298  CoinPackedMatrix matrixByRow(*solver.getMatrixByRow());
299  const double * elementByRow = matrixByRow.getElements();
300  const int * column = matrixByRow.getIndices();
301  const CoinBigIndex * rowStart = matrixByRow.getVectorStarts();
302  const int * rowLength = matrixByRow.getVectorLengths();
303
304  const double * rowLower = solver.getRowLower();
305  const double * rowUpper = solver.getRowUpper();
306  //  // Find Coefficients
307
308  /// initialize nauty
309
310  int num_affine = 0;
311
312  for (iColumn = 0; iColumn < numberColumns; iColumn++) {
313    if (objective[iColumn] && objective[iColumn]!=1.0)
314      num_affine++;
315  }
316  for (iRow = 0; iRow < numberRows; iRow++) {
317    for (CoinBigIndex j = rowStart[iRow];
318         j < rowStart[iRow] + rowLength[iRow]; j++) {
319      int jColumn = column[j];
320      double value = elementByRow[j];
321      if (value!=1.0)
322        num_affine++;
323    }
324  }
325
326  // Create Nauty object
327
328  int coef_count= numberRows + numberColumns +1;
329  int nc = num_affine + coef_count;
330  // create graph (part 1)
331
332  for (iColumn = 0; iColumn < numberColumns; iColumn++) {
333    Node var_vertex;
334    var_vertex.node(iColumn,0.0,columnLower[iColumn],columnUpper[iColumn],-1,-1 );
335    node_info_.push_back(var_vertex);
336  }
337  // objective
338  int index = numberColumns;
339  {
340    Node vertex;
341    vertex.node( index , 0.0 , -COIN_DBL_MAX, COIN_DBL_MAX,
342                 COUENNE_HACKED_EXPRGROUP, 0);
343    node_info_.push_back( vertex);
344  }
345
346  // compute space for sparse
347  size_t * v = NULL;
348  int * d = NULL;
349  int * e = NULL;
350  bool sparse=false;
351  double spaceDense = ((nc+WORDSIZE-1)*(nc+WORDSIZE-1))/WORDSIZE;
352  int spaceSparse = 0;
353  {
354    size_t numberElements = 0;
355    for (iColumn = 0; iColumn < numberColumns; iColumn++) {
356      double value = objective[iColumn];
357      if (value) {
358        if (value==1.0) {
359          numberElements+=2;
360        } else {
361          numberElements+=4;
362          coef_count ++;
363        }
364      }
365    }
366    for (iRow = 0; iRow < numberRows; iRow++) {
367      for (CoinBigIndex j = rowStart[iRow];
368           j < rowStart[iRow] + rowLength[iRow]; j++) {
369        int jColumn = column[j];
370        double value = elementByRow[j];
371        if (value==1.0) {
372          numberElements+=2;
373        } else {
374          numberElements+=4;
375          coef_count ++;
376        }
377      }
378    }
379    spaceSparse = 2*nc+numberElements;
380    //printf("Space for sparse is %d for dense %g\n",
381    //     spaceSparse,spaceDense);
382#ifdef NTY_TRACES
383    bool goSparse = true;
384#else
385    bool goSparse = (spaceSparse<spaceDense);
386#endif
387    // for now always sparse
388    goSparse=true;
389    if (goSparse) {
390      sparse=true;
391      v = new size_t [nc+1];
392      d = new int [nc];
393      e = new int [numberElements];
394      size_t * counts = new size_t [coef_count+1];
395      memset(counts,0,coef_count*sizeof(size_t));
396      coef_count= numberRows + numberColumns +1;
397      // create graph (part 2)
398      for (iColumn = 0; iColumn < numberColumns; iColumn++) {
399        double value = objective[iColumn];
400        if (value) {
401          if (value==1.0) {
402            counts[index]++;
403            counts[iColumn]++;
404          } else {
405            counts[index]++;
406            counts[coef_count]+=2;
407            counts[iColumn]++;
408            coef_count ++;
409          }
410        }
411      }
412      index++;
413      for (iRow = 0; iRow < numberRows; iRow++) {
414        for (CoinBigIndex j = rowStart[iRow];
415             j < rowStart[iRow] + rowLength[iRow]; j++) {
416          int jColumn = column[j];
417          double value = elementByRow[j];
418          if (value==1.0) {
419            counts[index]++;
420            counts[jColumn]++;
421          } else {
422            counts[index]++;
423            counts[coef_count]+=2;
424            counts[jColumn]++;
425            coef_count ++;
426          }
427        }
428        index++;
429      }
430      // create graph (part 3)
431      assert (nc==coef_count);
432      numberElements=0;
433      v[0]=0;
434      for (int i=0;i<nc;i++) {
435        int count=counts[i];
436        d[i]=count;
437        counts[i]=v[i];
438        numberElements+=count;;
439        v[i+1]=numberElements;
440      }
441      index = numberColumns;
442      coef_count= numberRows + numberColumns +1;
443      for (iColumn = 0; iColumn < numberColumns; iColumn++) {
444        double value = objective[iColumn];
445        if (value) {
446          int where;
447          if (value==1.0) {
448            where = counts[index];
449            counts[index]++;
450            e[where]=iColumn;
451            where = counts[iColumn];
452            counts[iColumn]++;
453            e[where]=index;
454          } else {
455            Node coef_vertex;
456            coef_vertex.node( coef_count, value, value, value, -2, 0 );
457            node_info_.push_back(coef_vertex);
458            where = counts[index];
459            counts[index]++;
460            e[where]=coef_count;
461            where = counts[coef_count];
462            counts[coef_count]++;
463            e[where]=index;
464            where = counts[coef_count];
465            counts[coef_count]++;
466            e[where]=iColumn;
467            where = counts[iColumn];
468            counts[iColumn]++;
469            e[where]=coef_count;
470            coef_count ++;
471          }
472      }
473      }
474      index++;
475      for (iRow = 0; iRow < numberRows; iRow++) {
476        Node vertex;
477        vertex.node( index , 0.0 , rowLower[iRow], rowUpper[iRow],
478                     COUENNE_HACKED_EXPRGROUP, 0);
479        node_info_.push_back( vertex);
480        for (CoinBigIndex j = rowStart[iRow];
481             j < rowStart[iRow] + rowLength[iRow]; j++) {
482          int jColumn = column[j];
483          double value = elementByRow[j];
484          int where;
485          if (value==1.0) {
486            where = counts[index];
487            counts[index]++;
488            e[where]=jColumn;
489            where = counts[jColumn];
490            counts[jColumn]++;
491            e[where]=index;
492          } else {
493            Node coef_vertex;
494            coef_vertex.node( coef_count, value, value, value, -2, 0 );
495            node_info_.push_back(coef_vertex);
496            where = counts[index];
497            counts[index]++;
498            e[where]=coef_count;
499            where = counts[coef_count];
500            counts[coef_count]++;
501            e[where]=index;
502            where = counts[coef_count];
503            counts[coef_count]++;
504            e[where]=jColumn;
505            where = counts[jColumn];
506            counts[jColumn]++;
507            e[where]=coef_count;
508            coef_count ++;
509          }
510        }
511        index++;
512      }
513      delete [] counts;
514    }
515  }
516
517  nauty_info_ = new CbcNauty(nc,v,d,e);
518  delete [] v;
519  delete [] d;
520  delete [] e;
521  if (!sparse) {
522    // create graph (part 2)
523    coef_count= numberRows + numberColumns +1;
524    for (iColumn = 0; iColumn < numberColumns; iColumn++) {
525      double value = objective[iColumn];
526      if (value) {
527        if (value==1.0) {
530        } else {
531          Node coef_vertex;
532          coef_vertex.node( coef_count, value, value, value, -2, 0 );
533          node_info_.push_back(coef_vertex);
538          coef_count ++;
539        }
540      }
541    }
542    index++;
543    for (iRow = 0; iRow < numberRows; iRow++) {
544      Node vertex;
545      vertex.node( index , 0.0 , rowLower[iRow], rowUpper[iRow],
546                   COUENNE_HACKED_EXPRGROUP, 0);
547      node_info_.push_back( vertex);
548      for (CoinBigIndex j = rowStart[iRow];
549           j < rowStart[iRow] + rowLength[iRow]; j++) {
550        int jColumn = column[j];
551        double value = elementByRow[j];
552        if (value==1.0) {
555        } else {
556          Node coef_vertex;
557          coef_vertex.node( coef_count, value, value, value, -2, 0 );
558          node_info_.push_back(coef_vertex);
563          coef_count ++;
564        }
565      }
566      index++;
567    }
568  }
569  numberColumns_ = numberColumns;
570  whichOrbit_ = new int [2*numberColumns_];
571  nautyBranchCalls_ = 0;
572  nautyBranchSucceeded_ = 0;
573  nautyFixCalls_ = 0;
574  nautyFixSucceeded_ = 0;
575  nautyTime_ = 0.0;
576  nautyFixes_= 0.0;
577  nautyOtherBranches_ = 0.0;
578  Compute_Symmetry ();
579  //Print_Orbits ();
580  // stats in array
581  whichOrbit_[0]=spaceDense;
582  whichOrbit_[1]=spaceSparse;
583}
584// Fixes variables using orbits (returns number fixed)
585int
586CbcSymmetry::orbitalFixing(OsiSolverInterface * solver)
587{
588  int numberColumns = solver->getNumCols();
589  char * status = new char [numberColumns];
590  ChangeBounds(solver->getColLower(),
591               solver->getColUpper(),
592               solver->getNumCols(),true);
593  Compute_Symmetry();
594  fillOrbits();
595  int n=0;
596  //#define PRINT_MORE 1
597  const int * alternativeOrbits = whichOrbit();
598  if (alternativeOrbits) {
599    for (int i=0;i<numberColumns;i++) {
600      char type='0';
601      if (solver->getColUpper()[i]) {
602        if (solver->getColLower()[i]) {
603          type='1';
604        } else {
605          double value=solver->getColSolution()[i];
606          if (value<0.0001)
607            type='L';
608          else if (value>0.9999)
609            type='U';
610          else
611            type='X';
612        }
613      }
614      status[i]=type;
615    }
616    n=0;
617    for (int i=0;i<numberColumns;i++) {
618      if (status[i]!='0'&&status[i]!='1') {
619        int iOrbit=alternativeOrbits[i];
620        for (int j=i+1;j<numberColumns;j++) {
621          if (status[j]=='0'&&alternativeOrbits[j]==iOrbit) {
622#if PRINT_MORE>1
623            printf("In alternative orbit %d - %d free (%c), %d fixed to 0\n",
624                   iOrbit,i,status[i],j);
625#endif
626            status[i]='0'; // can fix on both branches
627            solver->setColUpper(i,0.0);
628            n++;
629            break;
630          }
631        }
632      }
633    }
634  }
635  delete [] status;
636  if (n) {
637    nautyFixSucceeded_++;
638    nautyFixes_ += n;
639#if PRINT_MORE
640    printf("%d orbital fixes\n",n);
641#endif
642  }
643  return n;
644}
645// Default Constructor
646CbcSymmetry::CbcSymmetry ()
647  : nauty_info_(NULL),
648    numberColumns_(0),
649    numberUsefulOrbits_(0),
650    whichOrbit_(NULL)
651{
652}
653// Copy constructor
654CbcSymmetry::CbcSymmetry ( const CbcSymmetry & rhs)
655{
656  node_info_ = rhs.node_info_;
657  nauty_info_ = new CbcNauty(*rhs.nauty_info_);
658  numberUsefulOrbits_ = rhs.numberUsefulOrbits_;
659  numberColumns_ = rhs.numberColumns_;
660  if (rhs.whichOrbit_)
661    whichOrbit_=CoinCopyOfArray(rhs.whichOrbit_,numberColumns_);
662  else
663    whichOrbit_ = NULL;
664}
665
666// Assignment operator
667CbcSymmetry &
668CbcSymmetry::operator=( const CbcSymmetry & rhs)
669{
670  if (this != &rhs) {
671    delete nauty_info_;
672    node_info_ = rhs.node_info_;
673    nauty_info_ = new CbcNauty(*rhs.nauty_info_);
674    delete [] whichOrbit_;
675    numberColumns_ = rhs.numberColumns_;
676    numberUsefulOrbits_ = rhs.numberUsefulOrbits_;
677    if (rhs.whichOrbit_)
678      whichOrbit_=CoinCopyOfArray(rhs.whichOrbit_,numberColumns_);
679    else
680      whichOrbit_ = NULL;
681  }
682  return *this;
683}
684
685// Destructor
686CbcSymmetry::~CbcSymmetry ()
687{
688  delete nauty_info_;
689  delete [] whichOrbit_;
690}
691
692CbcNauty::CbcNauty(int vertices, const size_t * v, const int * d, const int * e)
693{
694  //printf("Need sparse nauty - wordsize %d\n",WORDSIZE);
695  n_ = vertices;
696  m_ = (n_ + WORDSIZE - 1)/WORDSIZE;
697  if (v)
698    nel_ = v[n_];
699  else
700    nel_ = 0;
701
702  //printf ("size of long = %d (%d)\nwordsize = %d\nn,m = %d,%d\n",
703  //          SIZEOF_LONG, sizeof (long), WORDSIZE, n_, m_);
704
705  nauty_check (WORDSIZE, m_, n_, NAUTYVERSIONID);
706
707  /// Apparently sizes are skewed on 64bit machines
708
709#define MULTIPLIER 1
710
711  if (!nel_) {
712    G_ = (graph *) malloc(MULTIPLIER * m_ * n_ * sizeof(int));
713    GSparse_ = NULL;
714  } else {
715    G_ = NULL;
716    GSparse_ = (sparsegraph *) malloc(sizeof(sparsegraph));
717    SG_INIT(*GSparse_);
718    SG_ALLOC(*GSparse_,n_,nel_,"malloc");
719    GSparse_->nv = n_; /* Number of vertices */
720    GSparse_->nde = nel_;
721  }
722  lab_ = (int *) malloc(MULTIPLIER * n_ * sizeof(int));
723  ptn_ = (int *) malloc(MULTIPLIER * n_ * sizeof(int));
724  active_ = NULL;
725  orbits_ = (int *) malloc(MULTIPLIER * n_ * sizeof(int));
726#ifndef NTY_TRACES
727  options_ = (optionblk *) malloc(MULTIPLIER * sizeof(optionblk));
728  stats_ = (statsblk *) malloc(MULTIPLIER * sizeof(statsblk));
729#else
730  options_ = (TracesOptions *) malloc(MULTIPLIER * sizeof(TracesOptions));
731  stats_ = (TracesStats *) malloc(MULTIPLIER * sizeof(TracesStats));
732#endif
733  worksize_ = 100*m_;
734  workspace_ = (setword *) malloc(MULTIPLIER * worksize_*sizeof(setword));
735  canonG_ = NULL;
736  if ((G_ == 0&&GSparse_ == 0) || lab_ == 0 || ptn_ == 0 ||
737      orbits_ == 0 || options_ == 0 || stats_ == 0 ||
738      workspace_ == 0) assert(0);
739
740  // Zero allocated memory
741  if (G_) {
742    memset(G_, 0, m_*n_*sizeof(int));
743  } else {
744    //for (int i=0;i<n_;i++) {
745    //GSparse_->v[i]=v[i];
746    //}
747    memcpy(GSparse_->v,v,n_*sizeof(size_t));
748    memcpy(GSparse_->d,d,n_*sizeof(int));
749    memcpy(GSparse_->e,e,nel_*sizeof(int));
750  }
751  memset(lab_, 0, n_*sizeof(int));
752  memset(ptn_, 0, n_*sizeof(int));
753  memset(orbits_, 0, n_*sizeof(int));
754  memset(workspace_, 0, worksize_*sizeof(setword));
755#ifndef NTY_TRACES
756  memset(options_, 0,MULTIPLIER * sizeof(optionblk));
757#else
758  memset(options_, 0,MULTIPLIER * sizeof(TracesOptions));
759#endif
760
761  // Set the options you want
762#ifndef NTY_TRACES
763  options_->getcanon = FALSE;
764  options_->digraph = FALSE;
765  options_->writeautoms = FALSE;
766  options_->writemarkers = FALSE;
767  options_->defaultptn = TRUE;
768  options_->cartesian = FALSE;
769  options_->linelength = 78;
770  options_->outfile = NULL;
771  options_->userrefproc = NULL;
772  options_->userautomproc = NULL;
773  options_->userlevelproc = NULL;
774  options_->usernodeproc = NULL;
775  //  options_->usertcellproc = NULL;
776  options_->invarproc = NULL;
777  options_->tc_level = 100;
778  options_->mininvarlevel = 0;
779  options_->maxinvarlevel = 1;
780  options_->invararg = 0;
781  options_->dispatch = &dispatch_graph;
782#else
783  options_->getcanon = FALSE;
784  options_->writeautoms = FALSE;
785  options_->cartesian = FALSE;
786  options_->digraph = FALSE;
787  options_->defaultptn = TRUE;
788  options_->linelength = 78;
789#endif
790  if (G_) {
791    // Make an empty graph
792    for (int j = 0; j < n_; j++) {
793      set *gv = GRAPHROW(G_, j, m_);
794      EMPTYSET(gv, m_);
795    }
796  }
797
798  vstat_ = new int[n_];
799  clearPartitions();
800  afp_ = NULL;
801}
802
803CbcNauty::~CbcNauty()
804{
805  if (G_) free(G_);
806  if (GSparse_) {
807    SG_FREE(*GSparse_);
808    free(GSparse_);
809  }
810  if (lab_) free(lab_);
811  if (ptn_) free(ptn_);
812  if (active_) free(active_);
813  if (orbits_) free(orbits_);
814  if (options_) free(options_);
815  if (stats_) free(stats_);
816  if (workspace_) free(workspace_);
817  if (canonG_) free(canonG_);
818  if (vstat_) delete [] vstat_;
819}
820// Copy constructor
821CbcNauty::CbcNauty ( const CbcNauty & rhs)
822{
823  n_ = rhs.n_;
824  m_ = rhs.m_;
825  nel_ = rhs.nel_;
826  G_ = NULL;
827  GSparse_ = NULL;
828  if (!nel_) {
829    G_ = (graph *) malloc(MULTIPLIER * m_ * n_ * sizeof(int));
830  } else {
831    GSparse_ = (sparsegraph *) malloc(sizeof(sparsegraph));
832    SG_INIT(*GSparse_);
833    SG_ALLOC(*GSparse_,n_,nel_,"malloc");
834    GSparse_->nv = n_; /* Number of vertices */
835    GSparse_->nde = nel_;
836  }
837  lab_ = (int *) malloc(MULTIPLIER * n_ * sizeof(int));
838  ptn_ = (int *) malloc(MULTIPLIER * n_ * sizeof(int));
839  orbits_ = (int *) malloc(MULTIPLIER * n_ * sizeof(int));
840#ifndef NTY_TRACES
841  options_ = (optionblk *) malloc(MULTIPLIER * sizeof(optionblk));
842  stats_ = (statsblk *) malloc(MULTIPLIER * sizeof(statsblk));
843#else
844  options_ = (TracesOptions *) malloc(MULTIPLIER * sizeof(TracesOptions));
845  stats_ = (TracesStats *) malloc(MULTIPLIER * sizeof(TracesStats));
846#endif
847  worksize_ = 100*m_;
848  workspace_ = (setword *) malloc(MULTIPLIER * worksize_*sizeof(setword));
849  vstat_ = new int[n_];
850  canonG_ = NULL;
851  if ((G_ == 0 && GSparse_ == 0) || lab_ == 0 || ptn_ == 0 ||
852      orbits_ == 0 || options_ == 0 || stats_ == 0 ||
853      workspace_ == 0) assert(0);
854
855  // Copy allocated memory
856  if (G_) {
857    memcpy(G_, rhs.G_, m_*n_*sizeof(int));
858  } else {
859    memcpy(GSparse_->v,rhs.GSparse_->v,n_*sizeof(size_t));
860    memcpy(GSparse_->d,rhs.GSparse_->d,n_*sizeof(int));
861    memcpy(GSparse_->e,rhs.GSparse_->e,nel_*sizeof(int));
862  }
863  memcpy(lab_, rhs.lab_, n_*sizeof(int));
864  memcpy(ptn_, rhs.ptn_, n_*sizeof(int));
865  memcpy(orbits_, rhs.orbits_, n_*sizeof(int));
866  memcpy(workspace_, rhs.workspace_, worksize_*sizeof(setword));
867#ifndef NTY_TRACES
868  memcpy(options_, rhs.options_, MULTIPLIER * sizeof(optionblk));
869  memcpy(stats_, rhs.stats_, MULTIPLIER * sizeof(statsblk));
870#else
871  memcpy(options_, rhs.options_,MULTIPLIER * sizeof(TracesOptions));
872  memcpy(stats_, rhs.stats_, MULTIPLIER * sizeof(TracesStats));
873#endif
874  memcpy(vstat_,rhs.vstat_,n_*sizeof(int));
875
876  // ? clearPartitions();
877  active_ = NULL;
878  afp_ = rhs.afp_; // ? no copy ?
879}
880
881// Assignment operator
882CbcNauty &
883CbcNauty::operator=( const CbcNauty & rhs)
884{
885  if (this != &rhs) {
886    if (G_) free(G_);
887    if (GSparse_) {
888      SG_FREE(*GSparse_);
889      free(GSparse_);
890    }
891    if (lab_) free(lab_);
892    if (ptn_) free(ptn_);
893    if (active_) free(active_);
894    if (orbits_) free(orbits_);
895    if (options_) free(options_);
896    if (stats_) free(stats_);
897    if (workspace_) free(workspace_);
898    if (canonG_) free(canonG_);
899    if (vstat_) delete [] vstat_;
900    {
901      n_ = rhs.n_;
902      m_ = rhs.m_;
903      nel_ = rhs.nel_;
904      G_ = NULL;
905      GSparse_ = NULL;
906      if (!nel_) {
907        G_ = (graph *) malloc(MULTIPLIER * m_ * n_ * sizeof(int));
908      } else {
909        GSparse_ = (sparsegraph *) malloc(sizeof(sparsegraph));
910        SG_INIT(*GSparse_);
911        SG_ALLOC(*GSparse_,n_,nel_,"malloc");
912        GSparse_->nv = n_; /* Number of vertices */
913        GSparse_->nde = nel_;
914      }
915      lab_ = (int *) malloc(MULTIPLIER * n_ * sizeof(int));
916      ptn_ = (int *) malloc(MULTIPLIER * n_ * sizeof(int));
917      orbits_ = (int *) malloc(MULTIPLIER * n_ * sizeof(int));
918#ifndef NTY_TRACES
919      options_ = (optionblk *) malloc(MULTIPLIER * sizeof(optionblk));
920      stats_ = (statsblk *) malloc(MULTIPLIER * sizeof(statsblk));
921#else
922      options_ = (TracesOptions *) malloc(MULTIPLIER * sizeof(TracesOptions));
923      stats_ = (TracesStats *) malloc(MULTIPLIER * sizeof(TracesStats));
924#endif
925      worksize_ = 100*m_;
926      workspace_ = (setword *) malloc(MULTIPLIER * worksize_*sizeof(setword));
927      vstat_ = new int[n_];
928      canonG_ = NULL;
929      if ((G_ == 0 && GSparse_ == 0) || lab_ == 0 || ptn_ == 0 ||
930          orbits_ == 0 || options_ == 0 || stats_ == 0 ||
931          workspace_ == 0) assert(0);
932
933      // Copy allocated memory
934      if (!nel_) {
935        memcpy(G_, rhs.G_, m_*n_*sizeof(int));
936      } else {
937        memcpy(GSparse_->v,rhs.GSparse_->v,n_*sizeof(size_t));
938        memcpy(GSparse_->d,rhs.GSparse_->d,n_*sizeof(int));
939        memcpy(GSparse_->e,rhs.GSparse_->e,nel_*sizeof(int));
940      }
941      memcpy(lab_, rhs.lab_, n_*sizeof(int));
942      memcpy(ptn_, rhs.ptn_, n_*sizeof(int));
943      memcpy(orbits_, rhs.orbits_, n_*sizeof(int));
944      memcpy(workspace_, rhs.workspace_, worksize_*sizeof(setword));
945#ifndef NTY_TRACES
946      memcpy(options_, rhs.options_, MULTIPLIER * sizeof(optionblk));
947      memcpy(stats_, rhs.stats_, MULTIPLIER * sizeof(statsblk));
948#else
949      memcpy(options_, rhs.options_,MULTIPLIER * sizeof(TracesOptions));
950      memcpy(stats_, rhs.stats_, MULTIPLIER * sizeof(TracesStats));
951#endif
952      memcpy(vstat_,rhs.vstat_,n_*sizeof(int));
953
954      // ? clearPartitions();
955      active_ = NULL;
956      afp_ = rhs.afp_; // ? no copy ?
957    }
958  }
959  return *this;
960}
961
962void
964{
965  // Right now die if bad index.  Can throw exception later
966  //printf("addelement %d %d \n", ix, jx);
967  assert(ix < n_ && jx < n_);
968  if(ix != jx){  //No Loops
969    set *gv = GRAPHROW(G_, ix, m_);
971    set *gv2 = GRAPHROW(G_, jx, m_);
973    autoComputed_ = false;
974  }
975}
976
977void
978CbcNauty::clearPartitions()
979{
980  for (int j = 0; j < n_; j++) {
981    vstat_[j] = 1;
982    //printf("vstat %d = %d", j, vstat_[j]);
983  }
984
985  autoComputed_ = false;
986}
987
988void
989CbcNauty::computeAuto()
990{
991
992  //  if (autoComputed_) return;
993
994  double startCPU = CoinCpuTime ();
995
996  options_->defaultptn = FALSE;
997
998  // Here we only implement the partitions
999  // [ fix1 | fix0 (union) free | constraints ]
1000  int ix = 0;
1001
1002  for( int color = 1; color <= n_; color++){
1003    for (int j = 0; j < n_; j++) {
1004      if (vstat_[j] == color) {
1005        lab_[ix] = j;
1006        ptn_[ix] = color;
1007        ix++;
1008      }
1009    }
1010     if (ix > 0) ptn_[ix-1] = 0;
1011  }
1012
1013  /*
1014  for (int j = 0; j < n_; j++)
1015    printf("ptn %d = %d      lab = %d \n", j, ptn_[j], lab_[j]);
1016  */
1017
1018  // Should be number of columns
1019  assert(ix == n_);
1020  // Now the constraints if needed
1021
1022  // Compute Partition
1023
1024  if (G_) {
1025#ifndef NTY_TRACES
1026    nauty(G_, lab_, ptn_, active_, orbits_, options_,
1027          stats_, workspace_, worksize_, m_, n_, canonG_);
1028#else
1029    abort();
1030#endif
1031  } else {
1032#ifndef NTY_TRACES
1033    options_->dispatch = &dispatch_sparse;
1034    sparsenauty(GSparse_, lab_, ptn_, orbits_, options_,
1035          stats_, NULL);
1036#else
1037    //options_->dispatch = &dispatch_sparse;
1038    Traces(GSparse_, lab_, ptn_, orbits_, options_,
1039          stats_, NULL);
1040#endif
1041  }
1042  autoComputed_ = true;
1043
1044  double endCPU = CoinCpuTime ();
1045
1046  nautyTime_ += endCPU - startCPU;
1047  // Need to make sure all generators are written
1048  if (afp_) fflush(afp_);
1049}
1050
1051void
1052CbcNauty::deleteElement(int ix, int jx)
1053{
1054  // Right now die if bad index.  Can throw exception later
1055  assert(ix < n_ && jx < n_);
1056  set *gv = GRAPHROW(G_, ix, m_);
1057  if (ISELEMENT(gv, jx)) {
1058    DELELEMENT(gv, jx);
1059  }
1060  autoComputed_ = false;
1061}
1062
1063double
1064CbcNauty::getGroupSize() const
1065{
1066  if (!autoComputed_) return -1.0;
1067  return( stats_->grpsize1 * pow(10.0, (double) stats_->grpsize2) );
1068}
1069
1070int
1071CbcNauty::getNumGenerators() const
1072{
1073  if (!autoComputed_) return -1;
1074  return(stats_->numgenerators);
1075}
1076
1077int
1078CbcNauty::getNumOrbits() const
1079{
1080  if (!autoComputed_) return -1;
1081  return(stats_->numorbits);
1082}
1083
1084std::vector<std::vector<int> >
1085*CbcNauty::getOrbits() const
1086{
1087  std::vector<std::vector<int> > *orb = new std::vector<std::vector<int> >;
1088  if (!autoComputed_) return orb;
1089  orb -> resize(getNumOrbits());
1090  std::multimap<int, int> orbmap;
1091  std::set<int> orbkeys;
1092  for (int j = 0; j < n_; j++) {
1093    orbkeys.insert(orbits_[j]);
1094    orbmap.insert(std::make_pair(orbits_[j], j));
1095  }
1096
1097  int orbix = 0;
1098  for (std::set<int>::iterator it = orbkeys.begin();
1099       it != orbkeys.end(); ++it) {
1100    std::multimap<int, int>::iterator pos;
1101    for (pos = orbmap.lower_bound(*it);
1102         pos != orbmap.upper_bound(*it); ++pos) {
1103      (*orb)[orbix].push_back(pos->second);
1104    }
1105    orbix++;
1106  }
1107
1108  assert(orbix == getNumOrbits());
1109  return orb;
1110}
1111
1112void
1113CbcNauty::getVstat(double *v, int nv)
1114{
1115  assert(nv == n_);
1116  memcpy(v, vstat_, nv * sizeof(VarStatus));
1117}
1118
1119/*
1120bool
1121CbcNauty::isAllFixOneOrbit(const std::vector<int> &orbit) const
1122{
1123
1124  for(std::vector<int>::const_iterator it = orbit.begin();
1125      it != orbit.end(); ++it) {
1126    if (*it >= n_) return false;
1127    if (vstat_[*it] != FIX_AT_ONE) return false;
1128  }
1129  return true;
1130}
1131
1132bool
1133CbcNauty::isAllFreeOrbit(const std::vector<int> &orbit) const
1134{
1135  for(std::vector<int>::const_iterator it = orbit.begin();
1136      it != orbit.end(); ++it) {
1137    if (*it >= n_) return false;
1138    if (vstat_[*it] != FREE) return false;
1139  }
1140  return true;
1141}
1142
1143bool
1144CbcNauty::isConstraintOrbit(const std::vector<int> &orbit) const
1145{
1146  for(std::vector<int>::const_iterator it = orbit.begin();
1147      it != orbit.end(); ++it) {
1148    if (*it >= n_) return true;
1149  }
1150  return false;
1151
1152}
1153
1154bool
1155CbcNauty::isMixedFreeZeroOrbit(const std::vector<int> &orbit) const
1156{
1157  bool containsFree = false;
1158  bool containsZero = false;
1159
1160  for(std::vector<int>::const_iterator it = orbit.begin();
1161      it != orbit.end(); ++it) {
1162    if (*it >= n_) return false;
1163    if (vstat_[*it] == FREE) containsFree = true;
1164    if (vstat_[*it] == FIX_AT_ZERO) containsZero = true;
1165    if (containsFree && containsZero) break;
1166  }
1167  return (containsFree && containsZero);
1168}
1169*/
1170
1171void
1172CbcNauty::setWriteAutoms(const std::string &fname)
1173{
1174  afp_ = fopen(fname.c_str(), "w");
1175  options_->writeautoms = TRUE;
1176#ifndef NTY_TRACES
1177  options_->writemarkers = FALSE;
1178#endif
1179  options_->outfile = afp_;
1180
1181}
1182
1183void
1184CbcNauty::unsetWriteAutoms()
1185{
1186  fclose(afp_);
1187  options_->writeautoms = FALSE;
1188}
1189
1190// Default Constructor
1191CbcOrbitalBranchingObject::CbcOrbitalBranchingObject()
1192        : CbcBranchingObject(),
1193          column_(-1),
1194          numberOther_(0),
1195          numberExtra_(0),
1196          fixToZero_(NULL)
1197{
1198}
1199
1200// Useful constructor
1201CbcOrbitalBranchingObject::CbcOrbitalBranchingObject (CbcModel * model, int column,
1202                                                      int way ,
1203                                                      int numberExtra,
1204                                                      const int * extraToZero)
1205  : CbcBranchingObject(model, -1, way, 0.5),
1206    column_(column),
1207    numberOther_(0),
1208    numberExtra_(0),
1209    fixToZero_(NULL)
1210{
1211  CbcSymmetry * symmetryInfo = model->symmetryInfo();
1212  assert (symmetryInfo);
1213  // Filled in (hopefully)
1214  const int * orbit = symmetryInfo->whichOrbit();
1215  int iOrbit=orbit[column];
1216  assert (iOrbit>=0);
1217  int numberColumns = model->getNumCols();
1218  numberOther_=-1;
1219  for (int i=0;i<numberColumns;i++) {
1220    if (orbit[i]==iOrbit)
1221      numberOther_++;
1222  }
1223  assert (numberOther_>0);
1224  nautyBranchSucceeded_++;
1225  nautyOtherBranches_ += numberOther_;
1226  numberExtra_ = numberExtra;
1227  fixToZero_ = new int [numberOther_+numberExtra_];
1228  int n=0;
1229  for (int i=0;i<numberColumns;i++) {
1230    if (orbit[i]==iOrbit && i != column)
1231      fixToZero_[n++]=i;
1232  }
1233  for (int i=0;i<numberExtra;i++) {
1234      fixToZero_[n++]=extraToZero[i];
1235  }
1236}
1237
1238// Copy constructor
1239CbcOrbitalBranchingObject::CbcOrbitalBranchingObject (const CbcOrbitalBranchingObject & rhs)
1240        : CbcBranchingObject(rhs),
1241          column_(rhs.column_),
1242          numberOther_(rhs.numberOther_),
1243          numberExtra_(rhs.numberExtra_)
1244{
1245  fixToZero_ = CoinCopyOfArray(rhs.fixToZero_,numberOther_+numberExtra_);
1246}
1247
1248// Assignment operator
1249CbcOrbitalBranchingObject &
1250CbcOrbitalBranchingObject::operator=( const CbcOrbitalBranchingObject & rhs)
1251{
1252    if (this != &rhs) {
1253        CbcBranchingObject::operator=(rhs);
1254        delete [] fixToZero_;
1255        column_=rhs.column_;
1256        numberOther_=rhs.numberOther_;
1257        numberExtra_=rhs.numberExtra_;
1258        fixToZero_ = CoinCopyOfArray(rhs.fixToZero_,numberOther_+numberExtra_);
1259    }
1260    return *this;
1261}
1262CbcBranchingObject *
1263CbcOrbitalBranchingObject::clone() const
1264{
1265    return (new CbcOrbitalBranchingObject(*this));
1266}
1267
1268
1269// Destructor
1270CbcOrbitalBranchingObject::~CbcOrbitalBranchingObject ()
1271{
1272  delete [] fixToZero_;
1273}
1274
1275double
1276CbcOrbitalBranchingObject::branch()
1277{
1278  decrementNumberBranchesLeft();
1279  if (model_->logLevel()>1)
1280    print();
1281  OsiSolverInterface * solver = model_->solver();
1282  if (way_ < 0) {
1283    solver->setColUpper(column_,0.0);
1284    for ( int i = 0; i < numberOther_+numberExtra_; i++) {
1285      solver->setColUpper(fixToZero_[i],0.0);
1286    }
1287    way_ = 1;     // Swap direction
1288 } else {
1289    solver->setColLower(column_,1.0);
1290    for (int i = numberOther_; i < numberOther_+numberExtra_; i++) {
1291      solver->setColUpper(fixToZero_[i],0.0);
1292    }
1293    way_ = -1;    // Swap direction
1294  }
1295  return 0.0;
1296}
1297/* Update bounds in solver as in 'branch' and update given bounds.
1298   branchState is -1 for 'down' +1 for 'up' */
1299void
1300CbcOrbitalBranchingObject::fix(OsiSolverInterface * solver,
1301                           double * lower, double * upper,
1302                           int branchState) const
1303{
1304  if (branchState < 0) {
1305    upper[column_]=0.0;
1306    for ( int i = 0; i < numberOther_+numberExtra_; i++) {
1307      upper[fixToZero_[i]]=0.0;;
1308    }
1309  } else {
1310    lower[column_]=1.0;
1311    for (int i = numberOther_; i < numberOther_+numberExtra_; i++) {
1312      upper[fixToZero_[i]]=0.0;;
1313    }
1314  }
1315}
1316// Print what would happen
1317void
1318CbcOrbitalBranchingObject::print()
1319{
1320    if (way_ < 0) {
1321      printf("Orbital Down - to zero %d",column_);
1322      for ( int i = 0; i < numberOther_+numberExtra_; i++) {
1323        printf(" %d",fixToZero_[i]);
1324      }
1325    } else {
1326      printf("Orbital Up - to one %d, to zero",column_);
1327      for (int i = numberOther_; i < numberOther_+numberExtra_; i++) {
1328        printf(" %d",fixToZero_[i]);
1329      }
1330    }
1331    printf("\n");
1332}
1333
1334/** Compare the original object of \c this with the original object of \c
1335    brObj. Assumes that there is an ordering of the original objects.
1336    This method should be invoked only if \c this and brObj are of the same
1337    type.
1338    Return negative/0/positive depending on whether \c this is
1339    smaller/same/larger than the argument.
1340*/
1341int
1342CbcOrbitalBranchingObject::compareOriginalObject
1343(const CbcBranchingObject* brObj) const
1344{
1345  const CbcOrbitalBranchingObject* br =
1346    dynamic_cast<const CbcOrbitalBranchingObject*>(brObj);
1347  assert(!br);
1348  abort();
1349  return 0;
1350}
1351
1352/** Compare the \c this with \c brObj. \c this and \c brObj must be os the
1353    same type and must have the same original object, but they may have
1354    different feasible regions.
1355    Return the appropriate CbcRangeCompare value (first argument being the
1356    sub/superset if that's the case). In case of overlap (and if \c
1357    replaceIfOverlap is true) replace the current branching object with one
1358    whose feasible region is the overlap.
1359*/
1360CbcRangeCompare
1361CbcOrbitalBranchingObject::compareBranchingObject
1362(const CbcBranchingObject* brObj, const bool replaceIfOverlap)
1363{
1364  const CbcOrbitalBranchingObject* br =
1365    dynamic_cast<const CbcOrbitalBranchingObject*>(brObj);
1366  assert(!br);
1367  abort();
1368  return CbcRangeDisjoint;
1369}
1370#endif
1371
Note: See TracBrowser for help on using the repository browser.