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