Scippy

SCIP

Solving Constraint Integer Programs

compute_symmetry_bliss.cpp
Go to the documentation of this file.
1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program and library */
4 /* SCIP --- Solving Constraint Integer Programs */
5 /* */
6 /* Copyright (C) 2002-2022 Konrad-Zuse-Zentrum */
7 /* fuer Informationstechnik Berlin */
8 /* */
9 /* SCIP is distributed under the terms of the ZIB Academic License. */
10 /* */
11 /* You should have received a copy of the ZIB Academic License */
12 /* along with SCIP; see the file COPYING. If not visit scipopt.org. */
13 /* */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15 
16 /**@file compute_symmetry_bliss.cpp
17  * @brief interface for symmetry computations to bliss
18  * @author Marc Pfetsch
19  * @author Thomas Rehn
20  * @author Fabian Wegscheider
21  */
22 
23 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
24 
25 #include "compute_symmetry.h"
26 
27 /* include bliss graph */
28 #include <bliss/defs.hh>
29 #include <bliss/graph.hh>
30 
31 #include <string.h>
32 #include <vector>
33 #include <list>
34 #include <math.h>
35 
36 #include "scip/expr_var.h"
37 #include "scip/expr_sum.h"
38 #include "scip/expr_pow.h"
39 #include "scip/expr.h"
40 #include "scip/cons_nonlinear.h"
41 #include "scip/cons_linear.h"
42 
43 using std::vector;
44 
45 /** struct for bliss callback */
46 struct BLISS_Data
47 {
48  SCIP* scip; /**< SCIP pointer */
49  int npermvars; /**< number of variables for permutations */
50  int nperms; /**< number of permutations */
51  int** perms; /**< permutation generators as (nperms x npermvars) matrix */
52  int nmaxperms; /**< maximal number of permutations */
53  int maxgenerators; /**< maximal number of generators constructed (= 0 if unlimited) */
54 };
55 
56 /* ------------------- map for operator types ------------------- */
57 
58 /** gets the key of the given element */
59 static
60 SCIP_DECL_HASHGETKEY(SYMhashGetKeyOptype)
61 { /*lint --e{715}*/
62  return elem;
63 }
64 
65 /** returns TRUE iff both keys are equal
66  *
67  * Compare the types of two operators according to their name, level and, in case of power, exponent.
68  */
69 static
70 SCIP_DECL_HASHKEYEQ(SYMhashKeyEQOptype)
71 {
72  SYM_OPTYPE* k1;
73  SYM_OPTYPE* k2;
74 
75  k1 = (SYM_OPTYPE*) key1;
76  k2 = (SYM_OPTYPE*) key2;
77 
78  /* first check operator name */
79  if ( SCIPexprGetHdlr(k1->expr) != SCIPexprGetHdlr(k2->expr) )
80  return FALSE;
81 
82  /* for pow expressions, also check exponent (TODO should that happen for signpow as well?) */
83  if ( SCIPisExprPower((SCIP*)userptr, k1->expr )
84  && SCIPgetExponentExprPow(k1->expr) != SCIPgetExponentExprPow(k2->expr) ) /*lint !e777*/
85  return FALSE;
86 
87  /* if still undecided, take level */
88  if ( k1->level != k2->level )
89  return FALSE;
90 
91  return TRUE;
92 }
93 
94 /** returns the hash value of the key */
95 static
96 SCIP_DECL_HASHKEYVAL(SYMhashKeyValOptype)
97 { /*lint --e{715}*/
98  SYM_OPTYPE* k;
99  SCIP_Real exponent;
100 
101  k = (SYM_OPTYPE*) key;
102 
103  if ( SCIPisExprPower((SCIP*)userptr, k->expr) )
104  exponent = SCIPgetExponentExprPow(k->expr);
105  else
106  exponent = 1.0;
107 
108  return SCIPhashTwo(SCIPrealHashCode(exponent), k->level),
109  (uint64_t) SCIPexprhdlrGetName(SCIPexprGetHdlr(k->expr));
110 }
111 
112 /* ------------------- map for constant types ------------------- */
113 
114 /** gets the key of the given element */
115 static
116 SCIP_DECL_HASHGETKEY(SYMhashGetKeyConsttype)
117 { /*lint --e{715}*/
118  return elem;
119 }
120 
121 /** returns TRUE iff both keys are equal
122  *
123  * Compare two constants according to their values.
124  */
125 static
126 SCIP_DECL_HASHKEYEQ(SYMhashKeyEQConsttype)
127 {
128  SYM_CONSTTYPE* k1;
129  SYM_CONSTTYPE* k2;
130 
131  k1 = (SYM_CONSTTYPE*) key1;
132  k2 = (SYM_CONSTTYPE*) key2;
133 
134  return (SCIP_Bool)(k1->value == k2->value); /*lint !e777*/
135 }
136 
137 /** returns the hash value of the key */
138 static
139 SCIP_DECL_HASHKEYVAL(SYMhashKeyValConsttype)
140 { /*lint --e{715}*/
141  SYM_CONSTTYPE* k;
142 
143  k = (SYM_CONSTTYPE*) key;
144 
145  return SCIPrealHashCode(k->value);
146 }
147 
148 /* ------------------- map for constraint side types ------------------- */
149 
150 /** gets the key of the given element */
151 static
152 SCIP_DECL_HASHGETKEY(SYMhashGetKeyRhstype)
153 { /*lint --e{715}*/
154  return elem;
155 }
156 
157 /** returns TRUE iff both keys are equal
158  *
159  * Compare two constraint sides according to lhs and rhs.
160  */
161 static
162 SCIP_DECL_HASHKEYEQ(SYMhashKeyEQRhstype)
163 {
164  SYM_RHSTYPE* k1;
165  SYM_RHSTYPE* k2;
166 
167  k1 = (SYM_RHSTYPE*) key1;
168  k2 = (SYM_RHSTYPE*) key2;
169 
170  if ( k1->lhs != k2->lhs ) /*lint !e777*/
171  return FALSE;
172 
173  return (SCIP_Bool)(k1->rhs == k2->rhs); /*lint !e777*/
174 }
175 
176 /** returns the hash value of the key */
177 static
178 SCIP_DECL_HASHKEYVAL(SYMhashKeyValRhstype)
179 { /*lint --e{715}*/
180  SYM_RHSTYPE* k;
181 
182  k = (SYM_RHSTYPE*) key;
183 
185 }
186 
187 /** callback function for bliss */
188 static
190  void* user_param, /**< parameter supplied at call to bliss */
191  unsigned int n, /**< size of aut vector */
192  const unsigned int* aut /**< automorphism */
193  )
194 {
195  assert( aut != NULL );
196  assert( user_param != NULL );
197 
198  BLISS_Data* data = static_cast<BLISS_Data*>(user_param);
199  assert( data->scip != NULL );
200  assert( data->npermvars < (int) n );
201  assert( data->maxgenerators >= 0);
202 
203  /* make sure we do not generate more that maxgenerators many permutations, if the limit in bliss is not available */
204  if ( data->maxgenerators != 0 && data->nperms >= data->maxgenerators )
205  return;
206 
207  /* copy first part of automorphism */
208  bool isIdentity = true;
209  int* p = 0;
210  if ( SCIPallocBlockMemoryArray(data->scip, &p, data->npermvars) != SCIP_OKAY )
211  return;
212 
213  for (int j = 0; j < data->npermvars; ++j)
214  {
215  /* convert index of variable-level 0-nodes to variable indices */
216  p[j] = (int) aut[j];
217  if ( p[j] != j )
218  isIdentity = false;
219  }
220 
221  /* ignore trivial generators, i.e. generators that only permute the constraints */
222  if ( isIdentity )
223  {
224  SCIPfreeBlockMemoryArray(data->scip, &p, data->npermvars);
225  return;
226  }
227 
228  /* check whether we should allocate space for perms */
229  if ( data->nmaxperms <= 0 )
230  {
231  if ( data->maxgenerators == 0 )
232  data->nmaxperms = 100; /* seems to cover many cases */
233  else
234  data->nmaxperms = data->maxgenerators;
235 
236  if ( SCIPallocBlockMemoryArray(data->scip, &data->perms, data->nmaxperms) != SCIP_OKAY )
237  return;
238  }
239  else if ( data->nperms >= data->nmaxperms ) /* check whether we need to resize */
240  {
241  int newsize = SCIPcalcMemGrowSize(data->scip, data->nperms + 1);
242  assert( newsize >= data->nperms );
243  assert( data->maxgenerators == 0 );
244 
245  if ( SCIPreallocBlockMemoryArray(data->scip, &data->perms, data->nmaxperms, newsize) != SCIP_OKAY )
246  return;
247 
248  data->nmaxperms = newsize;
249  }
250 
251  data->perms[data->nperms++] = p;
252 }
253 
254 /** Creates the nodes in the graph that correspond to variables. Each variable type gets a unique color
255  *
256  * @pre graph should be empty when this is called
257  */
258 static
260  SCIP* scip, /**< SCIP instance */
261  bliss::Graph* G, /**< Graph to be constructed */
262  SYM_MATRIXDATA* matrixdata, /**< data for MIP matrix (also contains the relevant variables) */
263  int& nnodes, /**< buffer to store number of nodes in graph */
264  const int& nedges, /**< buffer to store number of edges in graph */
265  int& nusedcolors /**< buffer to store number of used colors */
266  )
267 {
268  assert( scip != NULL );
269  assert( G != NULL );
270  assert( nnodes == 0 );
271  assert( nedges == 0 );
272  assert( nusedcolors == 0 );
273  SCIPdebugMsg(scip, "Creating graph with colored nodes for variables.\n");
274 
275  /* add nodes for variables */
276  for (int v = 0; v < matrixdata->npermvars; ++v)
277  {
278  const int color = matrixdata->permvarcolors[v];
279  assert( 0 <= color && color < matrixdata->nuniquevars );
280 
281 #ifndef NDEBUG
282  int node = (int) G->add_vertex((unsigned) color);
283  assert( node == v );
284 #else
285  (void) G->add_vertex((unsigned) color);
286 #endif
287 
288  ++nnodes;
289  }
290 
291  /* this is not exactly true, since we skip auxvars, but it doesn't matter if some colors are not used at all */
292  nusedcolors = matrixdata->nuniquevars;
293 
294  return SCIP_OKAY;
295 }
296 
297 /** Construct linear part of colored graph for symmetry computations
298  *
299  * Construct graph:
300  * - Each variable gets a different node.
301  * - Each constraint gets a different node.
302  * - Each matrix coefficient gets a different node that is connected to the two nodes
303  * corresponding to the respective constraint and variable.
304  *
305  * Each different variable, rhs, matrix coefficient gets a different color that is attached to the corresponding entries.
306  *
307  * @pre This method assumes that the nodes corresponding to permutation variables are already in the graph and that
308  * their node number is equal to their index.
309  */
310 static
312  SCIP* scip, /**< SCIP instance */
313  bliss::Graph* G, /**< Graph to be constructed */
314  SYM_MATRIXDATA* matrixdata, /**< data for MIP matrix */
315  int& nnodes, /**< buffer to store number of nodes in graph */
316  int& nedges, /**< buffer to store number of edges in graph */
317  int& nusedcolors, /**< buffer to store number of used colors */
318  SCIP_Bool& success /**< whether the construction was successful */
319  )
320 {
321  assert( nnodes == (int) G->get_nof_vertices() );
322  assert( nusedcolors <= nnodes );
323 
324  SCIPdebugMsg(scip, "Filling graph with colored coefficient nodes for linear part.\n");
325 
326  success = TRUE;
327 
328  /* add nodes for rhs of constraints */
329  for (int c = 0; c < matrixdata->nrhscoef; ++c)
330  {
331  const int color = matrixdata->rhscoefcolors[c];
332  assert( 0 <= color && color < matrixdata->nuniquerhs );
333 
334 #ifndef NDEBUG
335  int node = (int) G->add_vertex((unsigned) (nusedcolors + color));
336  assert( node == matrixdata->npermvars + c );
337 #else
338  (void) G->add_vertex((unsigned) (matrixdata->nuniquevars + color));
339 #endif
340 
341  ++nnodes;
342  }
343  assert( (int) G->get_nof_vertices() == matrixdata->npermvars + matrixdata->nrhscoef );
344  nusedcolors += matrixdata->nuniquerhs;
345 
346  /* Grouping of nodes depends on the number of nodes in the bipartite graph class.
347  * If there are more variables than constraints, we group by constraints.
348  * That is, given several variable nodes which are incident to one constraint node by the same color,
349  * we join these variable nodes to the constraint node by only one intermediate node.
350  */
351  const bool groupByConstraints = matrixdata->nrhscoef < matrixdata->npermvars;
352  if ( groupByConstraints )
353  SCIPdebugMsg(scip, "Group intermediate nodes by constraints.\n");
354  else
355  SCIPdebugMsg(scip, "Group intermediate nodes by variables.\n");
356 
357  /* "colored" edges based on all matrix coefficients - loop through ordered matrix coefficients */
358  int ninternodes;
359  if ( groupByConstraints )
360  ninternodes = matrixdata->nrhscoef;
361  else
362  ninternodes = matrixdata->npermvars;
363 
364  int* internodes = NULL;
365  SCIP_CALL( SCIPallocBufferArray(scip, &internodes, ninternodes) ); /*lint !e530*/
366  for (int l = 0; l < ninternodes; ++l)
367  internodes[l] = -1;
368 
369  /* We pass through the matrix coeficients, grouped by color, i.e., different coefficients. If the coeffients appear
370  * in the same row or column, it suffices to only generate a single node (depending on groupByConstraints). We store
371  * this node in the array internodes. In order to avoid reinitialization, we store the node number with increasing
372  * numbers for each color. The smallest number for the current color is stored in firstcolornodenumber. */
373  int oldcolor = -1;
374 #ifndef NDEBUG
375  SCIP_Real oldcoef = SCIP_INVALID;
376 #endif
377  int firstcolornodenumber = -1;
378  for (int j = 0; j < matrixdata->nmatcoef; ++j)
379  {
380  int idx = matrixdata->matidx[j];
381  assert( 0 <= idx && idx < matrixdata->nmatcoef );
382 
383  /* find color corresponding to matrix coefficient */
384  const int color = matrixdata->matcoefcolors[idx];
385  assert( 0 <= color && color < matrixdata->nuniquemat );
386 
387  assert( 0 <= matrixdata->matrhsidx[idx] && matrixdata->matrhsidx[idx] < matrixdata->nrhscoef );
388  assert( 0 <= matrixdata->matvaridx[idx] && matrixdata->matvaridx[idx] < matrixdata->npermvars );
389 
390  const int rhsnode = matrixdata->npermvars + matrixdata->matrhsidx[idx];
391  const int varnode = matrixdata->matvaridx[idx];
392  assert( matrixdata->npermvars <= rhsnode && rhsnode < matrixdata->npermvars + matrixdata->nrhscoef );
393  assert( rhsnode < (int) G->get_nof_vertices() );
394  assert( varnode < (int) G->get_nof_vertices() );
395 
396  /* if we have only one color, we do not need intermediate nodes */
397  if ( matrixdata->nuniquemat == 1 )
398  {
399  G->add_edge((unsigned) varnode, (unsigned) rhsnode);
400  ++nedges;
401  }
402  else
403  {
404  /* if new group of coefficients has been reached */
405  if ( color != oldcolor )
406  {
407  assert( ! SCIPisEQ(scip, oldcoef, matrixdata->matcoef[idx]) );
408  oldcolor = color;
409  firstcolornodenumber = nnodes;
410 #ifndef NDEBUG
411  oldcoef = matrixdata->matcoef[idx];
412 #endif
413  }
414  else
415  assert( SCIPisEQ(scip, oldcoef, matrixdata->matcoef[idx]) );
416 
417  int varrhsidx;
418  if ( groupByConstraints )
419  varrhsidx = matrixdata->matrhsidx[idx];
420  else
421  varrhsidx = matrixdata->matvaridx[idx];
422  assert( 0 <= varrhsidx && varrhsidx < ninternodes );
423 
424  if ( internodes[varrhsidx] < firstcolornodenumber )
425  {
426  internodes[varrhsidx] = (int) G->add_vertex((unsigned) (nusedcolors + color));
427  ++nnodes;
428  }
429  assert( internodes[varrhsidx] >= matrixdata->npermvars + matrixdata->nrhscoef );
430  assert( internodes[varrhsidx] >= firstcolornodenumber );
431 
432  /* determine whether graph would be too large for bliss (can only handle int) */
433  if ( nnodes >= INT_MAX/2 )
434  {
435  success = FALSE;
436  break;
437  }
438 
439  G->add_edge((unsigned) varnode, (unsigned) internodes[varrhsidx]);
440  G->add_edge((unsigned) rhsnode, (unsigned) internodes[varrhsidx]);
441  nedges += 2;
442  }
443  }
444 
445  nusedcolors += matrixdata->nuniquemat;
446 
447  SCIPfreeBufferArray(scip, &internodes);
448  return SCIP_OKAY;
449 }
450 
451 /** Construct non-linear part of colored graph for symmetry computations
452  *
453  * Construct graph:
454  * - Each node of the expression trees gets a different node.
455  * - Each coefficient of a sum expression gets its own node connected to the node of the corresponding child.
456  * - Each constraint (with lhs and (!) rhs) gets its own node connected to the corresponding node of the root expression.
457  *
458  * @note: In contrast to the linear part, lhs and rhs are treated together here, so that each unique combination of lhs
459  * and rhs gets its own node. This makes the implementation a lot simpler with the small downside, that different
460  * formulations of the same constraints would not be detected as equivalent, e.g. for
461  * 0 <= x1 + x2 <= 1
462  * 0 <= x3 + x4
463  * x3 + x4 <= 1
464  * there would be no symmetry between (x1,x2) and (x3,x4) detected.
465  *
466  * Each different constraint (sides), sum-expression coefficient, constant and operator type gets a
467  * different color that is attached to the corresponding entries.
468  *
469  * @pre This method assumes that the nodes corresponding to permutation variables are already in the graph and that
470  * their node number is equal to their index.
471  */
472 static
474  SCIP* scip, /**< SCIP instance */
475  bliss::Graph* G, /**< Graph to be constructed */
476  SYM_EXPRDATA* exprdata, /**< data for nonlinear constraints */
477  int& nnodes, /**< buffer to store number of nodes in graph */
478  int& nedges, /**< buffer to store number of edges in graph */
479  int& nusedcolors, /**< number of used colors ind the graph so far */
480  SCIP_Bool& success /**< whether the construction was successful */
481  )
482 {
483  SCIP_HASHTABLE* optypemap;
484  SCIP_HASHTABLE* consttypemap;
485  SCIP_HASHTABLE* sumcoefmap;
486  SCIP_HASHTABLE* rhstypemap;
487  SYM_OPTYPE* uniqueoparray = NULL;
488  SYM_CONSTTYPE* uniqueconstarray = NULL;
489  SYM_CONSTTYPE* sumcoefarray = NULL;
490  SYM_RHSTYPE* uniquerhsarray = NULL;
491  SCIP_CONSHDLR* conshdlr;
492  SCIP_CONS** conss;
493  int nconss;
494  int nuniqueops = 0;
495  int nuniqueconsts = 0;
496  int nuniquecoefs = 0;
497  int nuniquerhs = 0;
498  int oparraysize = exprdata->nuniqueoperators;
499  int constarraysize = exprdata->nuniqueconstants;
500  int coefarraysize = exprdata->nuniquecoefs;
501  int rhsarraysize;
502 
503  assert( scip != NULL );
504  assert( G != NULL );
505  assert( exprdata != NULL );
506  assert( nnodes == (int) G->get_nof_vertices() );
507  assert( nnodes >= nusedcolors );
508 
509  success = TRUE; /*lint !e838*/
510 
511  conshdlr = SCIPfindConshdlr(scip, "nonlinear");
512  nconss = conshdlr != NULL ? SCIPconshdlrGetNConss(conshdlr) : 0;
513  if ( nconss == 0 )
514  return SCIP_OKAY;
515 
516  conss = SCIPconshdlrGetConss(conshdlr);
517  rhsarraysize = nconss;
518 
519  SCIPdebugMsg(scip, "Filling graph with colored coefficient nodes for non-linear part.\n");
520 
521  /* create maps for optypes, constants, sum coefficients and rhs to indices */
522  SCIP_CALL( SCIPhashtableCreate(&optypemap, SCIPblkmem(scip), oparraysize, SYMhashGetKeyOptype,
523  SYMhashKeyEQOptype, SYMhashKeyValOptype, (void*) scip) );
524  SCIP_CALL( SCIPhashtableCreate(&consttypemap, SCIPblkmem(scip), constarraysize, SYMhashGetKeyConsttype,
525  SYMhashKeyEQConsttype, SYMhashKeyValConsttype, (void*) scip) );
526  SCIP_CALL( SCIPhashtableCreate(&sumcoefmap, SCIPblkmem(scip), coefarraysize, SYMhashGetKeyConsttype,
527  SYMhashKeyEQConsttype, SYMhashKeyValConsttype, (void*) scip) );
528  SCIP_CALL( SCIPhashtableCreate(&rhstypemap, SCIPblkmem(scip), rhsarraysize, SYMhashGetKeyRhstype,
529  SYMhashKeyEQRhstype, SYMhashKeyValRhstype, (void*) scip) );
530 
531  assert( optypemap != NULL );
532  assert( consttypemap != NULL );
533  assert( sumcoefmap != NULL );
534  assert( rhstypemap != NULL );
535 
536  /* allocate space for mappings from optypes, constants, sum coefficients and rhs to colors */
537  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &uniqueoparray, oparraysize) );
538  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &uniqueconstarray, constarraysize) );
539  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &sumcoefarray, coefarraysize) );
540  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &uniquerhsarray, rhsarraysize) );
541 
542  SCIP_EXPRITER* it;
543  SCIP_CALL( SCIPcreateExpriter(scip, &it) );
544 
545  /* iterate over all expressions and add the corresponding nodes to the graph */
546  for (int i = 0; i < nconss; ++i)
547  {
548  SCIP_EXPR* rootexpr;
549  vector<int> visitednodes(0);
550  vector<SCIP_Bool> ischildofsum(0);
551  int currentlevel = 0;
552 
553  rootexpr = SCIPgetExprNonlinear(conss[i]);
554 
557 
558  for (SCIP_EXPR* expr = SCIPexpriterGetCurrent(it); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it)) /*lint !e441*/ /*lint !e440*/
559  {
560  switch( SCIPexpriterGetStageDFS(it) )
561  {
562  /* upon entering an expression, check its type and add nodes and edges if neccessary */
564  {
565  int node = -1;
566  int parentnode = -1;
567  int color = -1;
568 
569  /* for variable expressions, get the corresponding node that is already in the graph */
570  if ( SCIPisExprVar(scip, expr) )
571  {
572  SCIP_VAR* var = SCIPgetVarExprVar(expr);
573 
574  /* check whether the variable is active; if not, then replace the inactive variable by its aggregation
575  * or its fixed value; note that this step is equivalent as representing an inactive variable as sum
576  * expression
577  */
578  if ( SCIPvarIsActive(var) )
579  {
580  node = SCIPvarGetProbindex(var);
581  assert( node < (int) G->get_nof_vertices() );
582  }
583  else
584  {
585  SCIP_VAR** vars = NULL;
586  SCIP_Real* vals = NULL;
587  SCIP_Real constant = 0;
588  int varsize = 1;
589  int requiredsize;
590  int k;
591 
592  SCIP_CALL( SCIPallocBufferArray(scip, &vars, varsize) );
593  SCIP_CALL( SCIPallocBufferArray(scip, &vals, varsize) );
594 
595  vars[0] = var;
596  vals[0] = 1.0;
597 
598  SCIP_CALL( SCIPgetProbvarLinearSum(scip, vars, vals, &varsize, varsize, &constant, &requiredsize, TRUE) );
599 
600  if ( requiredsize > varsize )
601  {
602  SCIP_CALL( SCIPreallocBufferArray(scip, &vars, requiredsize) );
603  SCIP_CALL( SCIPreallocBufferArray(scip, &vals, requiredsize) );
604 
605  SCIP_CALL( SCIPgetProbvarLinearSum(scip, vars, vals, &varsize, requiredsize, &constant, &requiredsize, TRUE) );
606  assert( requiredsize <= varsize );
607  }
608 
609  parentnode = visitednodes[visitednodes.size() - 1];
610  assert( parentnode < (int) G->get_nof_vertices() );
611 
612  /* create nodes for all aggregation variables and coefficients and connect them to the parent node */
613  for ( k = 0; k < requiredsize; ++k )
614  {
615  SYM_CONSTTYPE* ct;
616  int internode;
617 
618  assert( vars[k] != NULL );
619  assert( vals[k] != 0.0 );
620  assert( nuniquecoefs < coefarraysize );
621 
622  ct = &sumcoefarray[nuniquecoefs];
623  ct->value = vals[k];
624 
625  if ( !SCIPhashtableExists(sumcoefmap, (void *) ct) )
626  {
627  SCIP_CALL( SCIPhashtableInsert(sumcoefmap, (void *) ct) );
628  ct->color = nusedcolors++;
629  color = ct->color;
630  nuniquecoefs++;
631  }
632  else
633  {
634  color = ((SYM_CONSTTYPE*) SCIPhashtableRetrieve(sumcoefmap, (void *) ct))->color;
635  }
636 
637  /* add the intermediate node with the corresponding color */
638  internode = (int) G->add_vertex((unsigned) color);
639  ++nnodes;
640 
641  assert( internode < (int) G->get_nof_vertices() );
642 
643  G->add_edge((unsigned) internode, (unsigned) parentnode);
644  ++nedges;
645 
646  /* connect the intermediate node to its corresponding variable node */
647  node = SCIPvarGetProbindex(vars[k]);
648  assert( node < (int) G->get_nof_vertices() );
649 
650  G->add_edge((unsigned) node, (unsigned) internode);
651  ++nedges;
652  }
653 
654  /* add the node for the constant */
655  if ( constant != 0.0 )
656  {
657  SYM_CONSTTYPE* ct;
658 
659  /* check whether we have to resize */
660  if ( nuniqueconsts >= constarraysize )
661  {
662  int newsize = SCIPcalcMemGrowSize(scip, nuniqueconsts+1);
663  assert( newsize >= 0 );
664  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &uniqueconstarray, constarraysize, newsize) );
665  constarraysize = newsize;
666  }
667 
668  assert( nuniqueconsts < constarraysize );
669 
670  ct = &uniqueconstarray[nuniqueconsts];
671  ct->value = constant;
672 
673  if ( !SCIPhashtableExists(consttypemap, (void *) ct) )
674  {
675  SCIP_CALL( SCIPhashtableInsert(consttypemap, (void *) ct) );
676  ct->color = nusedcolors++;
677  color = ct->color;
678  nuniqueconsts++;
679  }
680  else
681  {
682  color = ((SYM_CONSTTYPE*) SCIPhashtableRetrieve(consttypemap, (void *) ct))->color;
683  }
684 
685  /* add the node with a new color */
686  node = (int) G->add_vertex((unsigned) color);
687  ++nnodes;
688 
689  assert( node < (int) G->get_nof_vertices() );
690 
691  G->add_edge((unsigned) node, (unsigned) parentnode);
692  ++nedges;
693  }
694 
695  SCIPfreeBufferArray(scip, &vals);
696  SCIPfreeBufferArray(scip, &vars);
697 
698  /* add a filler node since it will be removed in the next iteration anyway */
699  visitednodes.push_back(nnodes);
700  ischildofsum.push_back(FALSE);
701  ++currentlevel;
702 
703  break;
704  }
705  }
706  /* for constant expressions, get the color of its type (value) or assign a new one */
707  else if ( SCIPisExprValue(scip, expr) )
708  {
709  SYM_CONSTTYPE* ct;
710 
711  assert( nuniqueconsts < constarraysize );
712 
713  ct = &uniqueconstarray[nuniqueconsts];
714  ct->value = SCIPgetValueExprValue(expr);
715 
716  if ( !SCIPhashtableExists(consttypemap, (void *) ct) )
717  {
718  SCIP_CALL( SCIPhashtableInsert(consttypemap, (void *) ct) );
719  ct->color = nusedcolors++;
720  color = ct->color;
721  nuniqueconsts++;
722  }
723  else
724  {
725  color = ((SYM_CONSTTYPE*) SCIPhashtableRetrieve(consttypemap, (void *) ct))->color;
726  }
727  }
728  /* for all other expressions, get the color of its operator type or assign a new one */
729  else
730  {
731  SYM_OPTYPE* ot;
732 
733  assert( nuniqueops < oparraysize );
734 
735  ot = &uniqueoparray[nuniqueops];
736 
737  ot->expr = expr;
738  ot->level = currentlevel;
739 
740  if ( !SCIPhashtableExists(optypemap, (void *) ot) )
741  {
742  SCIP_CALL( SCIPhashtableInsert(optypemap, (void *) ot) );
743  ot->color = nusedcolors++;
744  color = ot->color;
745  nuniqueops++;
746  }
747  else
748  {
749  color = ((SYM_OPTYPE*) SCIPhashtableRetrieve(optypemap, (void *) ot))->color;
750  }
751  }
752 
753  /* if this is the root expression, add the constraint side node (will be parent of expression node) */
754  if ( SCIPexpriterGetParentDFS(it) == NULL )
755  {
756  /* add the node corresponding to the constraint */
757  SYM_RHSTYPE* rt;
758  int parentcolor;
759 
760  assert( nuniquerhs < rhsarraysize );
761 
762  rt = &uniquerhsarray[nuniquerhs];
763  rt->lhs = SCIPgetLhsNonlinear(conss[i]);
764  rt->rhs = SCIPgetRhsNonlinear(conss[i]);
765 
766  if ( !SCIPhashtableExists(rhstypemap, (void *) rt) )
767  {
768  SCIP_CALL( SCIPhashtableInsert(rhstypemap, (void *) rt) );
769  rt->color = nusedcolors++;
770  parentcolor = rt->color;
771  nuniquerhs++;
772  }
773  else
774  {
775  parentcolor = ((SYM_RHSTYPE*) SCIPhashtableRetrieve(rhstypemap, (void *) rt))->color;
776  }
777 
778  /* add the constraint side node with the corresponding color */
779  parentnode = (int) G->add_vertex((unsigned) parentcolor);
780  ++nnodes;
781 
782  assert( parentnode < (int) G->get_nof_vertices() );
783  }
784  /* otherwise, get the parentnode stored in visitednodes */
785  else
786  {
787  parentnode = visitednodes[visitednodes.size() - 1];
788  assert( parentnode < (int) G->get_nof_vertices() );
789  }
790 
791  /* in all cases apart from variable expressions, the new node is added with the corresponding color */
792  if ( color != -1 )
793  {
794  node = (int) G->add_vertex((unsigned) color);
795  ++nnodes;
796 
797  assert( node < (int) G->get_nof_vertices() );
798  }
799 
800  /* store the new node so that it can be used as parentnode later */
801  assert( node != -1 );
802  visitednodes.push_back(node);
803  ischildofsum.push_back(FALSE);
804 
805  /* connect the current node with its parent */
806  assert( parentnode != -1 );
807  G->add_edge((unsigned) node, (unsigned) parentnode);
808  ++nedges;
809 
810  /* for sum expression, also add intermediate nodes for the coefficients */
811  if ( SCIPisExprSum(scip, expr) )
812  {
813  SCIP_Real* coefs = SCIPgetCoefsExprSum(expr);
814  int internode;
815 
816  /* iterate over children from last to first, such that visitednodes array is in correct order */
817  for (int j = SCIPexprGetNChildren(expr) - 1; j >= 0; --j)
818  {
819  SYM_CONSTTYPE* ct;
820 
821  assert( nuniquecoefs < coefarraysize );
822 
823  ct = &sumcoefarray[nuniquecoefs];
824  ct->value = coefs[j];
825 
826  if ( !SCIPhashtableExists(sumcoefmap, (void *) ct) )
827  {
828  SCIP_CALL( SCIPhashtableInsert(sumcoefmap, (void *) ct) );
829  ct->color = nusedcolors++;
830  color = ct->color;
831  nuniquecoefs++;
832  }
833  else
834  {
835  color = ((SYM_CONSTTYPE*) SCIPhashtableRetrieve(sumcoefmap, (void *) ct))->color;
836  }
837 
838  /* add the intermediate node with the corresponding color */
839  internode = (int) G->add_vertex((unsigned) color);
840  ++nnodes;
841  visitednodes.push_back(internode);
842  ischildofsum.push_back(TRUE);
843 
844  assert( internode < (int) G->get_nof_vertices() );
845 
846  G->add_edge((unsigned) internode, (unsigned) node);
847  ++nedges;
848  }
849 
850  /* add node for the constant term of the sum expression */
851  SCIP_Real constval = SCIPgetConstantExprSum(expr);
852  if ( constval != 0.0 )
853  {
854  SYM_CONSTTYPE* ct;
855 
856  /* check whether we have to resize */
857  if ( nuniqueconsts >= constarraysize )
858  {
859  int newsize = SCIPcalcMemGrowSize(scip, nuniqueconsts+1);
860  assert( newsize >= 0 );
861  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &uniqueconstarray, constarraysize, newsize) );
862  constarraysize = newsize;
863  }
864 
865  assert( nuniqueconsts < constarraysize );
866 
867  ct = &uniqueconstarray[nuniqueconsts];
868  ct->value = constval;
869 
870  if ( !SCIPhashtableExists(consttypemap, (void *) ct) )
871  {
872  SCIP_CALL( SCIPhashtableInsert(consttypemap, (void *) ct) );
873  ct->color = nusedcolors++;
874  color = ct->color;
875  nuniqueconsts++;
876  }
877  else
878  {
879  color = ((SYM_CONSTTYPE*) SCIPhashtableRetrieve(consttypemap, (void *) ct))->color;
880  }
881 
882  /* add the node with a new color */
883  internode = (int) G->add_vertex((unsigned) color);
884  ++nnodes;
885 
886  assert( node < (int) G->get_nof_vertices() );
887 
888  G->add_edge((unsigned) internode, (unsigned) node);
889  ++nedges;
890  }
891  }
892 
893  currentlevel++;
894  break;
895  }
896 
897  /* when leaving an expression, the nodes that are not needed anymore are erased from the respective arrays */
899  {
900  visitednodes.pop_back();
901  ischildofsum.pop_back();
902  currentlevel--;
903 
904  /* When leaving the child of a sum expression, we have to pop again to get rid of the intermediate nodes
905  * used for the coefficients of summands
906  */
907  if ( !ischildofsum.empty() && ischildofsum[ischildofsum.size() - 1] )
908  {
909  visitednodes.pop_back();
910  ischildofsum.pop_back();
911  }
912 
913  break;
914  }
915 
916  default:
917  SCIPABORT(); /* we should never be called in this stage */
918  break;
919  }
920  }
921 
922  assert( currentlevel == 0 );
923  assert( visitednodes.empty() );
924  assert( ischildofsum.empty() );
925 
926  /* determine whether graph would be too large for bliss (can only handle int) */
927  if ( nnodes >= INT_MAX/2 )
928  {
929  success = FALSE; /*lint !e838*/
930  break;
931  }
932  }
933 
934  /* free everything */
935  SCIPfreeExpriter(&it);
936  SCIPfreeBlockMemoryArrayNull(scip, &uniquerhsarray, rhsarraysize);
937  SCIPfreeBlockMemoryArrayNull(scip, &sumcoefarray, coefarraysize);
938  SCIPfreeBlockMemoryArrayNull(scip, &uniqueconstarray, constarraysize);
939  SCIPfreeBlockMemoryArrayNull(scip, &uniqueoparray, oparraysize);
940  SCIPhashtableFree(&rhstypemap);
941  SCIPhashtableFree(&sumcoefmap);
942  SCIPhashtableFree(&consttypemap);
943  SCIPhashtableFree(&optypemap);
944 
945  return SCIP_OKAY;
946 }
947 
948 /** return whether symmetry can be computed */
950 {
951  return TRUE;
952 }
953 
954 /** static variable for holding the name of bliss */
955 static char blissname[100];
956 
957 /** return name of external program used to compute generators */
958 const char* SYMsymmetryGetName(void)
959 {
960 #ifdef BLISS_PATCH_PRESENT
961  (void) snprintf(blissname, 100, "bliss %sp", bliss::version);
962 #else
963  (void) snprintf(blissname, 100, "bliss %s", bliss::version);
964 #endif
965  return blissname;
966 }
967 
968 /** return description of external program used to compute generators */
969 const char* SYMsymmetryGetDesc(void)
970 {
971  return "Computing Graph Automorphism Groups by T. Junttila and P. Kaski (www.tcs.hut.fi/Software/bliss/)";
972 }
973 
974 /** compute generators of symmetry group */
976  SCIP* scip, /**< SCIP pointer */
977  int maxgenerators, /**< maximal number of generators constructed (= 0 if unlimited) */
978  SYM_MATRIXDATA* matrixdata, /**< data for MIP matrix */
979  SYM_EXPRDATA* exprdata, /**< data for nonlinear constraints */
980  int* nperms, /**< pointer to store number of permutations */
981  int* nmaxperms, /**< pointer to store maximal number of permutations (needed for freeing storage) */
982  int*** perms, /**< pointer to store permutation generators as (nperms x npermvars) matrix */
983  SCIP_Real* log10groupsize /**< pointer to store size of group */
984  )
985 {
986  assert( scip != NULL );
987  assert( matrixdata != NULL );
988  assert( exprdata != NULL );
989  assert( nperms != NULL );
990  assert( nmaxperms != NULL );
991  assert( perms != NULL );
992  assert( log10groupsize != NULL );
993  assert( maxgenerators >= 0 );
994 
995  /* init */
996  *nperms = 0;
997  *nmaxperms = 0;
998  *perms = NULL;
999  *log10groupsize = 0;
1000 
1001  int nnodes = 0;
1002  int nedges = 0;
1003  int nusedcolors = 0;
1004  SCIP_Bool success = FALSE;
1005 
1006  /* create bliss graph */
1007  bliss::Graph G(0);
1008 
1009  /* create nodes corresponding to variables */
1010  SCIP_CALL( createVariableNodes(scip, &G, matrixdata, nnodes, nedges, nusedcolors) );
1011 
1012  assert( nnodes == matrixdata->npermvars );
1013  assert( nusedcolors == matrixdata->nuniquevars );
1014 
1015  /* fill graph with nodes for variables and linear constraints */
1016  SCIP_CALL( fillGraphByLinearConss(scip, &G, matrixdata, nnodes, nedges, nusedcolors, success) );
1017 
1018  if ( !success )
1019  {
1020  SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, 0, "Graph construction failed during linear part.\n");
1021  return SCIP_OKAY;
1022  }
1023 
1024  /* add the nodes for nonlinear constraints to the graph */
1025  SCIP_CALL( fillGraphByNonlinearConss(scip, &G, exprdata, nnodes, nedges, nusedcolors, success) );
1026 
1027  if ( !success )
1028  {
1029  SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, 0, "Graph construction failed during non-linear part.\n");
1030  return SCIP_OKAY;
1031  }
1032 
1033 #ifdef SCIP_OUTPUT
1034  G.write_dot("debug.dot");
1035 #endif
1036 
1037  SCIPdebugMsg(scip, "Symmetry detection graph has %u nodes.\n", G.get_nof_vertices());
1038 
1039  /* compute automorphisms */
1040  bliss::Stats stats;
1041  BLISS_Data data;
1042  data.scip = scip;
1043  data.npermvars = matrixdata->npermvars;
1044  data.nperms = 0;
1045  data.nmaxperms = 0;
1047  data.perms = NULL;
1048 
1049  /* Prefer splitting partition cells corresponding to variables over those corresponding
1050  * to inequalities. This is because we are only interested in the action
1051  * of the automorphism group on the variables, and we need a base for this action */
1052  G.set_splitting_heuristic(bliss::Graph::shs_f);
1053  /* disable component recursion as advised by Tommi Junttila from bliss */
1054  G.set_component_recursion(false);
1055 
1056  /* do not use a node limit, but set generator limit */
1057 #ifdef BLISS_PATCH_PRESENT
1058  G.set_search_limits(0, (unsigned) maxgenerators);
1059 #endif
1060 
1061 #if BLISS_VERSION_MAJOR >= 1 || BLISS_VERSION_MINOR >= 76
1062  /* lambda function to have access to data and pass it to the blisshook above */
1063  auto reportglue = [&](unsigned int n, const unsigned int* aut) {
1064  blisshook((void*)&data, n, aut);
1065  };
1066 
1067  /* lambda function to have access to stats and terminate the search if maxgenerators are reached */
1068  auto term = [&]() {
1069  return (stats.get_nof_generators() >= (long unsigned int) maxgenerators);
1070  };
1071 
1072  /* start search */
1073  G.find_automorphisms(stats, reportglue, term);
1074 #else
1075  /* start search */
1076  G.find_automorphisms(stats, blisshook, (void*) &data);
1077 #endif
1078 
1079 
1080 #ifdef SCIP_OUTPUT
1081  (void) stats.print(stdout);
1082 #endif
1083 
1084  /* prepare return values */
1085  if ( data.nperms > 0 )
1086  {
1087  *perms = data.perms;
1088  *nperms = data.nperms;
1089  *nmaxperms = data.nmaxperms;
1090  }
1091  else
1092  {
1093  assert( data.perms == NULL );
1094  assert( data.nmaxperms == 0 );
1095  }
1096 
1097  /* determine log10 of symmetry group size */
1098  *log10groupsize = (SCIP_Real) log10l(stats.get_group_size_approx());
1099 
1100  return SCIP_OKAY;
1101 }
#define SCIPfreeBlockMemoryArray(scip, ptr, num)
Definition: scip_mem.h:101
#define SCIPreallocBlockMemoryArray(scip, ptr, oldnum, newnum)
Definition: scip_mem.h:90
SCIP_RETCODE SCIPexpriterInit(SCIP_EXPRITER *iterator, SCIP_EXPR *expr, SCIP_EXPRITER_TYPE type, SCIP_Bool allowrevisit)
Definition: expriter.c:491
#define SCIPallocBlockMemoryArray(scip, ptr, num)
Definition: scip_mem.h:84
SCIP_Real * matcoef
static SCIP_RETCODE fillGraphByNonlinearConss(SCIP *scip, bliss::Graph *G, SYM_EXPRDATA *exprdata, int &nnodes, int &nedges, int &nusedcolors, SCIP_Bool &success)
SCIP_Real lhs
SCIP_RETCODE SCIPhashtableInsert(SCIP_HASHTABLE *hashtable, void *element)
Definition: misc.c:2487
SCIP_CONSHDLR * SCIPfindConshdlr(SCIP *scip, const char *name)
Definition: scip_cons.c:877
int SCIPexprGetNChildren(SCIP_EXPR *expr)
Definition: expr.c:3798
SCIP_EXPR * SCIPexpriterGetParentDFS(SCIP_EXPRITER *iterator)
Definition: expriter.c:730
const char * SCIPexprhdlrGetName(SCIP_EXPRHDLR *exprhdlr)
Definition: expr.c:525
static SCIP_DECL_HASHKEYEQ(SYMhashKeyEQOptype)
int SCIPcalcMemGrowSize(SCIP *scip, int num)
Definition: scip_mem.c:130
SCIP_Real SCIPgetConstantExprSum(SCIP_EXPR *expr)
Definition: expr_sum.c:1172
static SCIP_DECL_HASHKEYVAL(SYMhashKeyValOptype)
SCIP_Real SCIPgetRhsNonlinear(SCIP_CONS *cons)
const char * SYMsymmetryGetName(void)
static SCIP_RETCODE fillGraphByLinearConss(SCIP *scip, bliss::Graph *G, SYM_MATRIXDATA *matrixdata, int &nnodes, int &nedges, int &nusedcolors, SCIP_Bool &success)
SCIP_CONS ** SCIPconshdlrGetConss(SCIP_CONSHDLR *conshdlr)
Definition: cons.c:4547
private functions to work with algebraic expressions
#define FALSE
Definition: def.h:87
SCIP_Real SCIPgetExponentExprPow(SCIP_EXPR *expr)
Definition: expr_pow.c:3343
#define TRUE
Definition: def.h:86
enum SCIP_Retcode SCIP_RETCODE
Definition: type_retcode.h:54
const char * SYMsymmetryGetDesc(void)
int SCIPvarGetProbindex(SCIP_VAR *var)
Definition: var.c:17600
SCIP_Bool SYMcanComputeSymmetry(void)
SCIP_Bool SCIPisEQ(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
#define SCIPfreeBufferArray(scip, ptr)
Definition: scip_mem.h:127
variable expression handler
#define SCIP_EXPRITER_ENTEREXPR
Definition: type_expr.h:667
#define SCIPdebugMsg
Definition: scip_message.h:69
SCIP_EXPR * SCIPexpriterGetCurrent(SCIP_EXPRITER *iterator)
Definition: expriter.c:673
SCIP_RETCODE SCIPhashtableCreate(SCIP_HASHTABLE **hashtable, BMS_BLKMEM *blkmem, int tablesize, SCIP_DECL_HASHGETKEY((*hashgetkey)), SCIP_DECL_HASHKEYEQ((*hashkeyeq)), SCIP_DECL_HASHKEYVAL((*hashkeyval)), void *userptr)
Definition: misc.c:2236
interface for symmetry computations
static void blisshook(void *user_param, unsigned int n, const unsigned int *aut)
SCIP_Real value
SCIP_Real * SCIPgetCoefsExprSum(SCIP_EXPR *expr)
Definition: expr_sum.c:1157
SCIP_RETCODE SYMcomputeSymmetryGenerators(SCIP *scip, int maxgenerators, SYM_MATRIXDATA *matrixdata, SYM_EXPRDATA *exprdata, int *nperms, int *nmaxperms, int ***perms, SCIP_Real *log10groupsize)
SCIP_VAR * SCIPgetVarExprVar(SCIP_EXPR *expr)
Definition: expr_var.c:403
static INLINE uint32_t SCIPrealHashCode(double x)
Definition: pub_misc.h:535
static char blissname[100]
BMS_BLKMEM * SCIPblkmem(SCIP *scip)
Definition: scip_mem.c:48
static SCIP_RETCODE createVariableNodes(SCIP *scip, bliss::Graph *G, SYM_MATRIXDATA *matrixdata, int &nnodes, const int &nedges, int &nusedcolors)
SCIP_Bool SCIPisExprValue(SCIP *scip, SCIP_EXPR *expr)
Definition: scip_expr.c:1432
static SCIP_DECL_HASHGETKEY(SYMhashGetKeyOptype)
SCIP_EXPR * SCIPgetExprNonlinear(SCIP_CONS *cons)
#define NULL
Definition: lpi_spx1.cpp:155
power and signed power expression handlers
SCIP_Bool SCIPisExprSum(SCIP *scip, SCIP_EXPR *expr)
Definition: scip_expr.c:1443
#define SCIP_CALL(x)
Definition: def.h:384
#define SCIPhashTwo(a, b)
Definition: pub_misc.h:510
SCIP_RETCODE SCIPgetProbvarLinearSum(SCIP *scip, SCIP_VAR **vars, SCIP_Real *scalars, int *nvars, int varssize, SCIP_Real *constant, int *requiredsize, SCIP_Bool mergemultiples)
Definition: scip_var.c:1735
void SCIPverbMessage(SCIP *scip, SCIP_VERBLEVEL msgverblevel, FILE *file, const char *formatstr,...)
Definition: scip_message.c:216
Definition: graph_load.c:93
int SCIPconshdlrGetNConss(SCIP_CONSHDLR *conshdlr)
Definition: cons.c:4590
SCIP_RETCODE SCIPcreateExpriter(SCIP *scip, SCIP_EXPRITER **iterator)
Definition: scip_expr.c:2300
SCIP_EXPR * expr
#define SCIPallocBufferArray(scip, ptr, num)
Definition: scip_mem.h:115
#define SCIP_Bool
Definition: def.h:84
SCIP_Real rhs
constraint handler for nonlinear constraints specified by algebraic expressions
SCIP_EXPR * SCIPexpriterGetNext(SCIP_EXPRITER *iterator)
Definition: expriter.c:848
SCIP_EXPRHDLR * SCIPexprGetHdlr(SCIP_EXPR *expr)
Definition: expr.c:3821
Constraint handler for linear constraints in their most general form, .
void * SCIPhashtableRetrieve(SCIP_HASHTABLE *hashtable, void *key)
Definition: misc.c:2548
SCIP_Bool SCIPisExprPower(SCIP *scip, SCIP_EXPR *expr)
Definition: scip_expr.c:1465
void SCIPhashtableFree(SCIP_HASHTABLE **hashtable)
Definition: misc.c:2286
void SCIPexpriterSetStagesDFS(SCIP_EXPRITER *iterator, SCIP_EXPRITER_STAGE stopstages)
Definition: expriter.c:654
void SCIPfreeExpriter(SCIP_EXPRITER **iterator)
Definition: scip_expr.c:2314
SCIP_EXPRITER_STAGE SCIPexpriterGetStageDFS(SCIP_EXPRITER *iterator)
Definition: expriter.c:686
SCIP_Bool SCIPisExprVar(SCIP *scip, SCIP_EXPR *expr)
Definition: scip_expr.c:1421
#define SCIP_Real
Definition: def.h:177
#define SCIP_INVALID
Definition: def.h:197
SCIP_Real SCIPgetValueExprValue(SCIP_EXPR *expr)
Definition: expr_value.c:285
SCIP_Bool SCIPhashtableExists(SCIP_HASHTABLE *hashtable, void *element)
Definition: misc.c:2599
#define SCIP_EXPRITER_LEAVEEXPR
Definition: type_expr.h:670
sum expression handler
#define nnodes
Definition: gastrans.c:65
#define SCIPfreeBlockMemoryArrayNull(scip, ptr, num)
Definition: scip_mem.h:102
SCIP_Bool SCIPexpriterIsEnd(SCIP_EXPRITER *iterator)
Definition: expriter.c:959
#define SCIPABORT()
Definition: def.h:356
SCIP_Real SCIPgetLhsNonlinear(SCIP_CONS *cons)
SCIP_Bool SCIPvarIsActive(SCIP_VAR *var)
Definition: var.c:17580
#define SCIPreallocBufferArray(scip, ptr, num)
Definition: scip_mem.h:119