Scippy

SCIP

Solving Constraint Integer Programs

sepa_eccuts.c
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-2016 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 email to scip@zib.de. */
13 /* */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15 
16 /**@file sepa_eccuts.c
17  * @brief edge concave cut separator
18  * @author Benjamin Müller
19  */
20 
21 /**@todo only count number of fixed variables in the edge concave terms */
22 /**@todo only add nonlinear row aggregations where at least ...% of the variables (bilinear terms?) are in edge concave
23  * terms */
24 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
25 
26 #include <assert.h>
27 #include <string.h>
28 
29 #include "scip/scipdefplugins.h"
30 #include "scip/sepa_eccuts.h"
31 #include "scip/cons_xor.h"
32 #include "scip/nlp.h"
33 #include "tclique/tclique.h"
34 
35 #define SEPA_NAME "eccuts"
36 #define SEPA_DESC "separator for edge-concave functions"
37 #define SEPA_PRIORITY -13000
38 #define SEPA_FREQ -1
39 #define SEPA_MAXBOUNDDIST 1.0
40 #define SEPA_USESSUBSCIP FALSE /**< does the separator use a secondary SCIP instance? */
41 #define SEPA_DELAY FALSE /**< should separation method be delayed, if other separators found cuts? */
42 
43 #define CLIQUE_MAXFIRSTNODEWEIGHT 1000 /**< maximum weight of branching nodes in level 0; 0 if not used for cliques
44  * with at least one fractional node) */
45 #define CLIQUE_MINWEIGHT 0 /**< lower bound for weight of generated cliques */
46 #define CLIQUE_MAXNTREENODES 10000 /**< maximal number of nodes of b&b tree */
47 #define CLIQUE_BACKTRACKFREQ 10000 /**< frequency to backtrack to first level of tree (0: no premature backtracking) */
48 
49 #define DEFAULT_DYNAMICCUTS TRUE /**< should generated cuts be removed from the LP if they are no longer tight? */
50 #define DEFAULT_MAXROUNDS 10 /**< maximal number of separation rounds per node (-1: unlimited) */
51 #define DEFAULT_MAXROUNDSROOT 250 /**< maximal number of separation rounds in the root node (-1: unlimited) */
52 #define DEFAULT_MAXDEPTH -1 /**< maximal depth at which the separator is applied */
53 #define DEFAULT_MAXSEPACUTS 10 /**< maximal number of e.c. cuts separated per separation round */
54 #define DEFAULT_MAXSEPACUTSROOT 50 /**< maximal number of e.c. cuts separated per separation round in root node */
55 #define DEFAULT_CUTMAXRANGE 1e+7 /**< maximal coefficient range of a cut (maximal coefficient divided by minimal
56  * coefficient) in order to be added to LP relaxation */
57 #define DEFAULT_MINVIOLATION 0.3 /**< minimal violation of an e.c. cut to be separated */
58 #define DEFAULT_MINAGGRSIZE 3 /**< search for e.c. aggregation of at least this size (has to be >= 3) */
59 #define DEFAULT_MAXAGGRSIZE 4 /**< search for e.c. aggregation of at most this size (has to be >= minaggrsize) */
60 #define DEFAULT_MAXBILINTERMS 500 /**< maximum number of bilinear terms allowed to be in a quadratic constraint */
61 #define DEFAULT_MAXSTALLROUNDS 5 /**< maximum number of unsuccessful rounds in the e.c. aggregation search */
62 
63 #define SUBSCIP_NODELIMIT 100LL /**< node limit to solve the sub-SCIP */
64 
65 #define ADJUSTFACETTOL 1e-6 /**< adjust resulting facets in checkRikun() up to a violation of this value */
66 #define USEDUALSIMPLEX TRUE /**< use dual or primal simplex algorithm? */
67 
68 /** first values for 2^n */
69 static const int poweroftwo[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192 };
70 
71 /*
72  * Data structures
73  */
74 
75 /** data to store a single edge-concave aggregations; an edge-concave aggregation of a quadratic constraint is a subset
76  * of nonconvex bilinear terms
77  */
78 struct EcAggr
79 {
80  SCIP_VAR** vars; /**< variables */
81  int nvars; /**< number of variables */
82 
83  SCIP_Real* termcoefs; /**< coefficients of bilinear terms */
84  int* termvars1; /**< index of the first variable of each bilinear term */
85  int* termvars2; /**< index of the second variable of each bilinear term*/
86  int nterms; /**< number of bilinear terms in the aggregation */
87 };
88 typedef struct EcAggr SCIP_ECAGGR;
89 
90 /** data to store all edge-concave aggregations and the remaining part of a nonlinear row of the form g(x) <= rhs */
91 struct NlrowAggr
92 {
93  SCIP_NLROW* nlrow; /**< nonlinear row aggregation */
94  SCIP_Bool rhsaggr; /**< consider nonlinear row aggregation for g(x) <= rhs (TRUE) or
95  * g(x) >= lhs (FALSE) */
96 
97  SCIP_ECAGGR** ecaggr; /**< array with all edge-concave aggregations */
98  int necaggr; /**< number of edge-concave aggregation */
99 
100  SCIP_VAR** linvars; /**< linear variables */
101  SCIP_Real* lincoefs; /**< linear coefficients */
102  int nlinvars; /**< number of linear variables */
104  SCIP_VAR** quadvars; /**< quadratic variables */
105  int* quadvar2aggr; /**< stores in which edge-concave aggregation the i-th quadratic variable
106  * is contained (< 0: in no edge-concave aggregation) */
107  int nquadvars; /**< number of quadratic variables */
108 
109  SCIP_VAR** remtermvars1; /**< first quadratic variable of remaining bilinear terms */
110  SCIP_VAR** remtermvars2; /**< second quadratic variable of remaining bilinear terms */
111  SCIP_Real* remtermcoefs; /**< coefficients for each remaining bilinear term */
112  int nremterms; /**< number of remaining bilinear terms */
114  SCIP_Real rhs; /**< rhs of the nonlinear row */
115  SCIP_Real constant; /**< constant part of the nonlinear row */
116 };
117 typedef struct NlrowAggr SCIP_NLROWAGGR;
118 
119 /** separator data */
120 struct SCIP_SepaData
121 {
122  SCIP_NLROWAGGR** nlrowaggrs; /**< array containing all nonlinear row aggregations */
123  int nnlrowaggrs; /**< number of nonlinear row aggregations */
124  SCIP_Bool searchedforaggr; /**< flag if we already searched for nlrow aggregation candidates */
125  int minaggrsize; /**< only search for e.c. aggregations of at least this size (has to be >= 3) */
126  int maxaggrsize; /**< only search for e.c. aggregations of at most this size (has to be >= minaggrsize) */
127  int maxecsize; /**< largest edge concave aggregation size */
128  int maxbilinterms; /**< maximum number of bilinear terms allowed to be in a quadratic constraint */
129  int maxstallrounds; /**< maximum number of unsuccessful rounds in the e.c. aggregation search */
130 
131  SCIP_LPI* lpi; /**< LP interface to solve the LPs to compute the facets of the convex envelopes */
132  int lpisize; /**< maximum size of e.c. aggregations which can be handled by the LP interface */
133 
134  SCIP_Real cutmaxrange; /**< maximal coef range of a cut (maximal coefficient divided by minimal
135  * coefficient) in order to be added to LP relaxation */
136  SCIP_Bool dynamiccuts; /**< should generated cuts be removed from the LP if they are no longer tight? */
137  SCIP_Real minviolation; /**< minimal violation of an e.c. cut to be separated */
138 
139  int maxrounds; /**< maximal number of separation rounds per node (-1: unlimited) */
140  int maxroundsroot; /**< maximal number of separation rounds in the root node (-1: unlimited) */
141  int maxdepth; /**< maximal depth at which the separator is applied */
142  int maxsepacuts; /**< maximal number of e.c. cuts separated per separation round */
143  int maxsepacutsroot; /**< maximal number of e.c. cuts separated per separation round in root node */
144 
145 #ifdef SCIP_STATISTIC
146  SCIP_Real aggrsearchtime; /**< total time spent for searching edge concave aggregations */
147  int nlhsnlrowaggrs; /**< number of found nonlinear row aggregations for SCIP_NLROWs of the form g(x) <= rhs */
148  int nrhsnlrowaggrs; /**< number of found nonlinear row aggregations for SCIP_NLROWs of the form g(x) >= lhs */
149 #endif
150 };
151 
152 
153 /*
154  * Local methods
155  */
156 
157 /** creates and empty edge-concave aggregation (without bilinear terms) */
158 static
160  SCIP* scip, /**< SCIP data structure */
161  SCIP_ECAGGR** ecaggr, /**< pointer to store the edge-concave aggregation */
162  int nquadvars, /**< number of quadratic variables */
163  int nquadterms /**< number of bilinear terms */
164  )
165 {
166  assert(scip != NULL);
167  assert(ecaggr != NULL);
168  assert(nquadvars > 0);
169  assert(nquadterms >= nquadvars);
170 
171  SCIP_CALL( SCIPallocMemory(scip, ecaggr) );
172 
173  (*ecaggr)->nvars = 0;
174  (*ecaggr)->nterms = 0;
175 
176  /* allocate enough memory for the quadratic variables and bilinear terms */
177  SCIP_CALL( SCIPallocMemoryArray(scip, &(*ecaggr)->vars, nquadvars) );
178  SCIP_CALL( SCIPallocMemoryArray(scip, &(*ecaggr)->termcoefs, nquadterms) );
179  SCIP_CALL( SCIPallocMemoryArray(scip, &(*ecaggr)->termvars1, nquadterms) );
180  SCIP_CALL( SCIPallocMemoryArray(scip, &(*ecaggr)->termvars2, nquadterms) );
181 
182  return SCIP_OKAY;
183 }
184 
185 /** frees and edge-concave aggregation */
186 static
188  SCIP* scip, /**< SCIP data structure */
189  SCIP_ECAGGR** ecaggr /**< pointer to store the edge-concave aggregation */
190  )
191 {
192  assert(scip != NULL);
193  assert(ecaggr != NULL);
194 
195  SCIPfreeMemoryArray(scip, &((*ecaggr)->termcoefs));
196  SCIPfreeMemoryArray(scip, &((*ecaggr)->termvars1));
197  SCIPfreeMemoryArray(scip, &((*ecaggr)->termvars2));
198  SCIPfreeMemoryArray(scip, &((*ecaggr)->vars));
199 
200  SCIPfreeMemory(scip, ecaggr);
201  *ecaggr = NULL;
202 
203  return SCIP_OKAY;
204 }
205 
206 /** adds a quadratic variable to an edge-concave aggregation */
207 static
209  SCIP_ECAGGR* ecaggr, /**< pointer to store the edge-concave aggregation */
210  SCIP_VAR* x /**< first variable */
211  )
212 {
213  ecaggr->vars[ ecaggr->nvars++ ] = x;
214  return SCIP_OKAY;
215 }
216 
217 /** adds a bilinear term to an edge-concave aggregation */
218 static
220  SCIP* scip, /**< SCIP data structure */
221  SCIP_ECAGGR* ecaggr, /**< pointer to store the edge-concave aggregation */
222  SCIP_VAR* x, /**< first variable */
223  SCIP_VAR* y, /**< second variable */
224  SCIP_Real coef /**< bilinear coefficient */
225  )
226 {
227  int idx1;
228  int idx2;
229  int i;
230 
231  assert(x != NULL);
232  assert(y != NULL);
233  assert(ecaggr->nterms + 1 <= ((ecaggr->nvars + 1) * ecaggr->nvars) / 2);
234  assert(!SCIPisZero(scip, coef));
235 
236  idx1 = -1;
237  idx2 = -1;
238 
239  /* search for the quadratic variables in the e.c. aggregation */
240  for( i = 0; i < ecaggr->nvars && (idx1 == -1 || idx2 == -1); ++i )
241  {
242  if( ecaggr->vars[i] == x )
243  idx1 = i;
244  if( ecaggr->vars[i] == y )
245  idx2 = i;
246  }
247 
248  assert(idx1 != -1 && idx2 != -1);
249 
250  ecaggr->termcoefs[ ecaggr->nterms ] = coef;
251  ecaggr->termvars1[ ecaggr->nterms ] = idx1;
252  ecaggr->termvars2[ ecaggr->nterms ] = idx2;
253  ++(ecaggr->nterms);
254 
255  return SCIP_OKAY;
256 }
257 
258 #ifdef SCIP_DEBUG
259 /** prints an edge-concave aggregation */
260 static
261 void ecaggrPrint(
262  SCIP* scip, /**< SCIP data structure */
263  SCIP_ECAGGR* ecaggr /**< pointer to store the edge-concave aggregation */
264  )
265 {
266  int i;
267 
268  assert(scip != NULL);
269  assert(ecaggr != NULL);
270 
271  SCIPdebugMessage(" nvars = %d nterms = %d\n", ecaggr->nvars, ecaggr->nterms);
272  SCIPdebugMessage(" vars: ");
273  for( i = 0; i < ecaggr->nvars; ++i )
274  SCIPdebugPrintf("%s ", SCIPvarGetName(ecaggr->vars[i]));
275  SCIPdebugPrintf("\n");
276 
277  SCIPdebugMessage(" terms: ");
278  for( i = 0; i < ecaggr->nterms; ++i )
279  {
280  SCIP_VAR* x;
281  SCIP_VAR* y;
282 
283  x = ecaggr->vars[ ecaggr->termvars1[i] ];
284  y = ecaggr->vars[ ecaggr->termvars2[i] ];
285  SCIPdebugPrintf("%e %s * %s ", ecaggr->termcoefs[i], SCIPvarGetName(x), SCIPvarGetName(y) );
286  }
287  SCIPdebugPrintf("\n");
288 }
289 #endif
290 
291 /** stores linear terms in a given nonlinear row aggregation */
292 static
294  SCIP* scip, /**< SCIP data structure */
295  SCIP_NLROWAGGR* nlrowaggr, /**< nonlinear row aggregation */
296  SCIP_VAR** linvars, /**< linear variables */
297  SCIP_Real* lincoefs, /**< linear coefficients */
298  int nlinvars /**< number of linear variables */
299  )
300 {
301  assert(scip != NULL);
302  assert(nlrowaggr != NULL);
303  assert(linvars != NULL || nlinvars == 0);
304  assert(lincoefs != NULL || nlinvars == 0);
305  assert(nlinvars >= 0);
306 
307  nlrowaggr->nlinvars = nlinvars;
308  nlrowaggr->linvars = NULL;
309  nlrowaggr->lincoefs = NULL;
310 
311  if( nlinvars > 0 )
312  {
313  SCIP_CALL( SCIPallocMemoryArray(scip, &nlrowaggr->linvars, nlinvars) );
314  SCIP_CALL( SCIPallocMemoryArray(scip, &nlrowaggr->lincoefs, nlinvars) );
315  BMScopyMemoryArray(nlrowaggr->linvars, linvars, nlinvars);
316  BMScopyMemoryArray(nlrowaggr->lincoefs, lincoefs, nlinvars);
317  }
318 
319  /* if we have a nlrow of the form g(x) >= lhs, multiply every coefficient by -1 */
320  if( !nlrowaggr->rhsaggr )
321  {
322  int i;
323 
324  for( i = 0; i < nlrowaggr->nlinvars; ++i )
325  nlrowaggr->lincoefs[i] *= -1.0;
326  }
327 
328  return SCIP_OKAY;
329 }
330 
331 /** stores quadratic variables in a given nonlinear row aggregation */
332 static
334  SCIP* scip, /**< SCIP data structure */
335  SCIP_NLROWAGGR* nlrowaggr, /**< nonlinear row aggregation */
336  SCIP_VAR** quadvars, /**< quadratic variables */
337  int nquadvars /**< number of quadratic variables */
338  )
339 {
340  assert(scip != NULL);
341  assert(nlrowaggr != NULL);
342  assert(quadvars != NULL);
343  assert(nquadvars > 0);
344 
345  nlrowaggr->nquadvars = nquadvars;
346  SCIP_CALL( SCIPallocMemoryArray(scip, &nlrowaggr->quadvars, nquadvars) );
347  BMScopyMemoryArray(nlrowaggr->quadvars, quadvars, nquadvars);
348 
349  return SCIP_OKAY;
350 }
351 
352 /** adds a remaining bilinear term to a given nonlinear row aggregation */
353 static
355  SCIP* scip, /**< SCIP data structure */
356  SCIP_NLROWAGGR* nlrowaggr, /**< nonlinear row aggregation */
357  SCIP_VAR* x, /**< first variable */
358  SCIP_VAR* y, /**< second variable */
359  SCIP_Real coef /**< bilinear coefficient */
360  )
361 {
362  assert(nlrowaggr != NULL);
363  assert(x != NULL);
364  assert(y != NULL);
365  assert(coef != 0.0);
366 
367  nlrowaggr->remtermcoefs[ nlrowaggr->nremterms ] = coef;
368  nlrowaggr->remtermvars1[ nlrowaggr->nremterms ] = x;
369  nlrowaggr->remtermvars2[ nlrowaggr->nremterms ] = y;
370  ++(nlrowaggr->nremterms);
371 
372  return SCIP_OKAY;
373 }
374 
375 /** creates a nonlinear row aggregation */
376 static
378  SCIP* scip, /**< SCIP data structure */
379  SCIP_NLROW* nlrow, /**< nonlinear row */
380  SCIP_NLROWAGGR** nlrowaggr, /**< pointer to store the nonlinear row aggregation */
381  int* quadvar2aggr, /**< mapping between quadratic variables and edge-concave aggregation
382  * stores a negative value if the quadratic variables does not belong
383  * to any aggregation */
384  int nfound, /**< number of edge-concave aggregations */
385  SCIP_Bool rhsaggr /**< consider nonlinear row aggregation for g(x) <= rhs (TRUE) or
386  * lhs <= g(x) (FALSE) */
387  )
388 {
389  int* aggrnvars; /* count the number of variables in each e.c. aggregations */
390  int* aggrnterms; /* count the number of bilinear terms in each e.c. aggregations */
391  int nquadelems;
392  int nquadvars;
393  int nremterms;
394  int i;
395 
396  assert(scip != NULL);
397  assert(nlrow != NULL);
398  assert(nlrowaggr != NULL);
399  assert(quadvar2aggr != NULL);
400  assert(nfound > 0);
401 
402  nquadelems = SCIPnlrowGetNQuadElems(nlrow);
403  nquadvars = SCIPnlrowGetNQuadVars(nlrow);
404  nremterms = 0;
405 
406  SCIP_CALL( SCIPallocBufferArray(scip, &aggrnvars, nfound) );
407  SCIP_CALL( SCIPallocBufferArray(scip, &aggrnterms, nfound) );
408 
409  /* create an empty nonlinear row aggregation */
410  SCIP_CALL( SCIPallocMemory(scip, nlrowaggr) );
411  (*nlrowaggr)->nlrow = nlrow;
412  (*nlrowaggr)->rhsaggr = rhsaggr;
413  (*nlrowaggr)->nquadvars = nquadvars;
414  (*nlrowaggr)->rhs = rhsaggr ? SCIPnlrowGetRhs(nlrow) : -SCIPnlrowGetLhs(nlrow);
415  (*nlrowaggr)->constant = rhsaggr ? SCIPnlrowGetConstant(nlrow) : -SCIPnlrowGetConstant(nlrow);
416 
417  (*nlrowaggr)->quadvars = NULL;
418  (*nlrowaggr)->quadvar2aggr = NULL;
419  (*nlrowaggr)->remtermcoefs = NULL;
420  (*nlrowaggr)->remtermvars1 = NULL;
421  (*nlrowaggr)->remtermvars2 = NULL;
422  (*nlrowaggr)->nquadvars = 0;
423  (*nlrowaggr)->nremterms = 0;
424 
425  /* copy quadvar2aggr array */
426  SCIP_CALL( SCIPallocMemoryArray(scip, &(*nlrowaggr)->quadvar2aggr, nquadvars) );
427  BMScopyMemoryArray((*nlrowaggr)->quadvar2aggr, quadvar2aggr, nquadvars);
428 
429  /* store all linear terms */
431  SCIPnlrowGetNLinearVars(nlrow)) );
432 
433  /* store all quadratic variables */
435  assert((*nlrowaggr)->nquadvars == nquadvars);
436 
437  for( i = 0; i < nfound; ++i )
438  {
439  aggrnvars[i] = 0;
440  aggrnterms[i] = 0;
441  }
442 
443  /* count the number of variables in each e.c. aggregation */
444  for( i = 0; i < nquadvars; ++i )
445  {
446  if( quadvar2aggr[i] >= 0)
447  ++aggrnvars[ quadvar2aggr[i] ];
448  }
449 
450  /* count the number of bilinear terms in each e.c. aggregation */
451  for( i = 0; i < nquadelems; ++i )
452  {
453  SCIP_QUADELEM* quadelem;
454  SCIP_Real coef;
455  int idx1;
456  int idx2;
457 
458  quadelem = &SCIPnlrowGetQuadElems(nlrow)[i];
459  idx1 = quadvar2aggr[quadelem->idx1];
460  idx2 = quadvar2aggr[quadelem->idx2];
461  coef = rhsaggr ? quadelem->coef : -quadelem->coef;
462 
463  /* variables has to belong to the same e.c. aggregation; bilinear / quadratic term has to be concave */
464  if( idx1 >= 0 && idx2 >= 0 && idx1 == idx2 && (quadelem->idx1 != quadelem->idx2 || SCIPisNegative(scip, coef) ) )
465  ++aggrnterms[idx1];
466  else
467  ++nremterms;
468  }
469 
470  /* create all edge-concave aggregations (empty) and remaining terms */
471  SCIP_CALL( SCIPallocMemoryArray(scip, &(*nlrowaggr)->ecaggr, nfound) );
472  if( nremterms > 0 )
473  {
474  SCIP_CALL( SCIPallocMemoryArray(scip, &(*nlrowaggr)->remtermcoefs, nremterms) );
475  SCIP_CALL( SCIPallocMemoryArray(scip, &(*nlrowaggr)->remtermvars1, nremterms) );
476  SCIP_CALL( SCIPallocMemoryArray(scip, &(*nlrowaggr)->remtermvars2, nremterms) );
477  }
478  (*nlrowaggr)->necaggr = nfound;
479 
480  for( i = 0; i < nfound; ++i )
481  {
482  SCIP_CALL( ecaggrCreateEmpty(scip, &(*nlrowaggr)->ecaggr[i], aggrnvars[i], aggrnterms[i]) );
483  }
484 
485  /* add quadratic variables to the edge-concave aggregations */
486  for( i = 0; i < nquadvars; ++i )
487  {
488  int idx;
489 
490  idx = quadvar2aggr[i];
491 
492  if( idx >= 0)
493  {
494  SCIPdebugMessage("add quadvar %d to aggr. %d\n", i, idx);
495  SCIP_CALL( ecaggrAddQuadvar((*nlrowaggr)->ecaggr[idx], SCIPnlrowGetQuadVars(nlrow)[i]) );
496  }
497  }
498 
499  /* add the bilinear /quadratic terms to the edge-concave aggregations or in the remaining part */
500  for( i = 0; i < nquadelems; ++i )
501  {
502  SCIP_QUADELEM* quadelem;
503  SCIP_VAR* x;
504  SCIP_VAR* y;
505  SCIP_Real coef;
506  int idx1;
507  int idx2;
508 
509  quadelem = &SCIPnlrowGetQuadElems(nlrow)[i];
510  x = SCIPnlrowGetQuadVars(nlrow)[quadelem->idx1];
511  y = SCIPnlrowGetQuadVars(nlrow)[quadelem->idx2];
512  idx1 = quadvar2aggr[quadelem->idx1];
513  idx2 = quadvar2aggr[quadelem->idx2];
514  coef = rhsaggr ? quadelem->coef : -quadelem->coef;
515 
516  if( idx1 >= 0 && idx2 >= 0 && idx1 == idx2 && (quadelem->idx1 != quadelem->idx2 || SCIPisNegative(scip, coef) ) )
517  {
518  SCIP_CALL( ecaggrAddBilinTerm(scip, (*nlrowaggr)->ecaggr[idx1], x, y, coef) );
519  SCIPdebugMessage("add term %e *%d*%d to aggr. %d\n", coef, quadelem->idx1, quadelem->idx2, idx1);
520  }
521  else
522  {
523  SCIP_CALL( nlrowaggrAddRemBilinTerm(scip, *nlrowaggr, x, y, coef) );
524  SCIPdebugMessage("add term %e *%d*%d to the remaining part\n", coef, quadelem->idx1, quadelem->idx2);
525  }
526  }
527 
528  /* free allocated memory */
529  SCIPfreeBufferArray(scip, &aggrnterms);
530  SCIPfreeBufferArray(scip, &aggrnvars);
531 
532  return SCIP_OKAY;
533 }
534 
535 /** frees a nonlinear row aggregation */
536 static
538  SCIP* scip, /**< SCIP data structure */
539  SCIP_NLROWAGGR** nlrowaggr /**< pointer to free the nonlinear row aggregation */
540  )
541 {
542  int i;
543 
544  assert(scip != NULL);
545  assert(nlrowaggr != NULL);
546  assert(*nlrowaggr != NULL);
547  (*nlrowaggr)->nlrow = NULL;
548  assert((*nlrowaggr)->quadvars != NULL);
549  assert((*nlrowaggr)->nquadvars > 0);
550  assert((*nlrowaggr)->nremterms >= 0);
551 
552  /* free remaining part */
553  SCIPfreeMemoryArrayNull(scip, &(*nlrowaggr)->remtermcoefs);
554  SCIPfreeMemoryArrayNull(scip, &(*nlrowaggr)->remtermvars1);
555  SCIPfreeMemoryArrayNull(scip, &(*nlrowaggr)->remtermvars2);
556 
557  /* free quadratic variables */
558  SCIPfreeMemoryArray(scip, &(*nlrowaggr)->quadvars);
559  SCIPfreeMemoryArray(scip, &(*nlrowaggr)->quadvar2aggr);
560  (*nlrowaggr)->quadvars = NULL;
561  (*nlrowaggr)->quadvar2aggr = NULL;
562  (*nlrowaggr)->nquadvars = 0;
563 
564  /* free linear part */
565  if( (*nlrowaggr)->nlinvars > 0 )
566  {
567  SCIPfreeMemoryArray(scip, &(*nlrowaggr)->linvars);
568  SCIPfreeMemoryArray(scip, &(*nlrowaggr)->lincoefs);
569  (*nlrowaggr)->linvars = 0;
570  (*nlrowaggr)->linvars = NULL;
571  (*nlrowaggr)->lincoefs = NULL;
572  }
573 
574  /* free edge-concave aggregations */
575  for( i = 0; i < (*nlrowaggr)->necaggr; ++i )
576  {
577  SCIP_CALL( ecaggrFree(scip, &(*nlrowaggr)->ecaggr[i]) );
578  }
579  SCIPfreeMemoryArray(scip, &(*nlrowaggr)->ecaggr);
580 
581  /* free nlrow aggregation */
582  SCIPfreeMemory(scip, nlrowaggr);
583 
584  return SCIP_OKAY;
585 }
586 
587 #ifdef SCIP_DEBUG
588 /** prints a nonlinear row aggregation */
589 static
590 void nlrowaggrPrint(
591  SCIP* scip, /**< SCIP data structure */
592  SCIP_NLROWAGGR* nlrowaggr /**< nonlinear row aggregation */
593  )
594 {
595  int i;
596 
597  SCIPdebugMessage(" nlrowaggr rhs = %e\n", nlrowaggr->rhs);
598  SCIPdebugMessage(" #remaining terms = %d\n", nlrowaggr->nremterms);
599 
600  SCIPdebugMessage("remaining terms: ");
601  for( i = 0; i < nlrowaggr->nremterms; ++i )
602  SCIPdebugPrintf("%e %s * %s + ", nlrowaggr->remtermcoefs[i], SCIPvarGetName(nlrowaggr->remtermvars1[i]),
603  SCIPvarGetName(nlrowaggr->remtermvars2[i]) );
604  for( i = 0; i < nlrowaggr->nlinvars; ++i )
605  SCIPdebugPrintf("%e %s + ", nlrowaggr->lincoefs[i], SCIPvarGetName(nlrowaggr->linvars[i]) );
606  SCIPdebugPrintf("\n");
607 
608  for( i = 0; i < nlrowaggr->necaggr; ++i )
609  {
610  SCIPdebugMessage("print e.c. aggr %d\n", i);
611  ecaggrPrint(scip, nlrowaggr->ecaggr[i]);
612  }
613  return;
614 }
615 #endif
616 
617 /** creates separator data */
618 static
620  SCIP* scip, /**< SCIP data structure */
621  SCIP_SEPADATA** sepadata /**< pointer to store separator data */
622  )
623 {
624  assert(scip != NULL);
625  assert(sepadata != NULL);
626 
627  SCIP_CALL( SCIPallocMemory(scip, sepadata) );
628 
629  (*sepadata)->nlrowaggrs = NULL;
630  (*sepadata)->nnlrowaggrs = 0;
631  (*sepadata)->searchedforaggr = FALSE;
632  (*sepadata)->maxecsize = 0;
633 
634  (*sepadata)->lpi = NULL;
635  (*sepadata)->lpisize = 0;
636 
637 #ifdef SCIP_STATISTIC
638  (*sepadata)->aggrsearchtime = 0.0;
639  (*sepadata)->nrhsnlrowaggrs = 0;
640  (*sepadata)->nlhsnlrowaggrs = 0;
641 #endif
642 
643  return SCIP_OKAY;
644 }
645 
646 /** frees all nonlinear row aggregations */
647 static
649  SCIP* scip, /**< SCIP data structure */
650  SCIP_SEPADATA* sepadata /**< pointer to store separator data */
651  )
652 {
653  assert(scip != NULL);
654  assert(sepadata != NULL);
655 
656  /* free nonlinear row aggregations */
657  if( sepadata->nlrowaggrs != NULL )
658  {
659  int i;
660 
661  for( i = sepadata->nnlrowaggrs - 1; i >= 0; --i )
662  {
663  SCIP_CALL( nlrowaggrFree(scip, &sepadata->nlrowaggrs[i]) );
664  }
665 
666  SCIPfreeMemoryArray(scip, &sepadata->nlrowaggrs);
667 
668  sepadata->nlrowaggrs = NULL;
669  sepadata->nnlrowaggrs = 0;
670  }
671 
672  return SCIP_OKAY;
673 }
674 
675 /** frees separator data */
676 static
678  SCIP* scip, /**< SCIP data structure */
679  SCIP_SEPADATA** sepadata /**< pointer to store separator data */
680  )
681 {
682  assert(scip != NULL);
683  assert(sepadata != NULL);
684  assert(*sepadata != NULL);
685 
686  /* free nonlinear row aggregations */
687  SCIP_CALL( sepadataFreeNlrows(scip, *sepadata) );
688 
689  /* free LP interface */
690  if( (*sepadata)->lpi != NULL )
691  {
692  SCIP_CALL( SCIPlpiFree(&((*sepadata)->lpi)) );
693  (*sepadata)->lpisize = 0;
694  }
695 
696  SCIPfreeMemory(scip, sepadata);
697 
698  return SCIP_OKAY;
699 }
700 
701 /** adds a nonlinear row aggregation to the separator data */
702 static
704  SCIP* scip, /**< SCIP data structure */
705  SCIP_SEPADATA* sepadata, /**< separator data */
706  SCIP_NLROWAGGR* nlrowaggr /**< non-linear row aggregation */
707  )
708 {
709  int i;
710 
711  assert(scip != NULL);
712  assert(sepadata != NULL);
713  assert(nlrowaggr != NULL);
714 
715  if( sepadata->nnlrowaggrs == 0 )
716  {
717  SCIP_CALL( SCIPallocMemoryArray(scip, &sepadata->nlrowaggrs, 1) ); /*lint !e506*/
718  }
719  else
720  {
721  SCIP_CALL( SCIPreallocMemoryArray(scip, &sepadata->nlrowaggrs, sepadata->nnlrowaggrs + 1) ); /*lint !e776*/
722  }
723 
724  sepadata->nlrowaggrs[ sepadata->nnlrowaggrs ] = nlrowaggr;
725  ++(sepadata->nnlrowaggrs);
726 
727  /* update maximum e.c. aggregation size */
728  for( i = 0; i < nlrowaggr->necaggr; ++i )
729  sepadata->maxecsize = MAX(sepadata->maxecsize, nlrowaggr->ecaggr[i]->nvars);
730 
731 #ifdef SCIP_STATISTIC
732  /* update statistics */
733  if( nlrowaggr->rhsaggr )
734  ++(sepadata->nrhsnlrowaggrs);
735  else
736  ++(sepadata->nlhsnlrowaggrs);
737 #endif
738 
739  return SCIP_OKAY;
740 }
741 
742 /** returns min{val-lb,ub-val} / (ub-lb) */
743 static
744 SCIP_Real phi(
745  SCIP* scip, /**< SCIP data structure */
746  SCIP_Real val, /**< solution value */
747  SCIP_Real lb, /**< lower bound */
748  SCIP_Real ub /**< upper bound */
749  )
750 {
751  if( SCIPisFeasEQ(scip, lb, ub) )
752  return 0.0;
753 
754  /* adjust */
755  val = MAX(val, lb);
756  val = MIN(val, ub);
757 
758  return MIN(ub - val, val - lb) / (ub - lb);
759 }
760 
761 /** creates an MIP to search for cycles with an odd number of positive edges in the graph representation of a nonlinear
762  * row; the model uses directed binary arc flow variables; we introduce for all quadratic elements a forward and
763  * backward edge; if the term is quadratic (e.g., loop in the graph) we fix the corresponding variables to zero; this
764  * leads to an easy mapping of quadratic elements and the variables of the MIP
765  */
766 static
768  SCIP* scip, /**< SCIP data structure */
769  SCIP* subscip, /**< auxiliary SCIP to search aggregations */
770  SCIP_SEPADATA* sepadata, /**< separator data */
771  SCIP_NLROW* nlrow, /**< nonlinear row */
772  SCIP_Bool rhsaggr, /**< consider nonlinear row aggregation for g(x) <= rhs (TRUE) or
773  * lhs <= g(x) (FALSE) */
774  SCIP_VAR** forwardarcs, /**< array to store all forward arc variables */
775  SCIP_VAR** backwardarcs, /**< array to store all backward arc variables */
776  SCIP_Real* nodeweights, /**< weights for each node of the graph */
777  int* nedges /**< pointer to store the number of nonexcluded edges in the graph */
778  )
779 {
780  SCIP_VAR** oddcyclearcs;
781  SCIP_CONS** flowcons;
782  SCIP_CONS* cyclelengthcons;
783  SCIP_CONS* oddcyclecons;
784  char name[SCIP_MAXSTRLEN];
785  int noddcyclearcs;
786  int nnodes;
787  int narcs;
788  int i;
789 
790  assert(subscip != NULL);
791  assert(forwardarcs != NULL);
792  assert(backwardarcs != NULL);
793  assert(nedges != NULL);
794  assert(sepadata->minaggrsize <= sepadata->maxaggrsize);
795 
796  narcs = SCIPnlrowGetNQuadElems(nlrow);
797  nnodes = SCIPnlrowGetNQuadVars(nlrow);
798  *nedges = 0;
799 
800  assert(narcs > 0);
801  assert(nnodes > 0);
802 
803  noddcyclearcs = 0;
804  SCIP_CALL( SCIPallocBufferArray(subscip, &oddcyclearcs, 2*narcs) );
805 
806  /* create problem with default plug-ins */
807  SCIP_CALL( SCIPcreateProbBasic(subscip, "E.C. aggregation MIP") );
810 
811  /* create forward and backward arc variables; loops are fixed to zero */
812  for( i = 0; i < narcs; ++i )
813  {
814  SCIP_CONS* noparallelcons;
815  SCIP_QUADELEM* quadelem;
816  SCIP_Real edgeweight;
817  SCIP_Real ub;
818 
819  quadelem = &SCIPnlrowGetQuadElems(nlrow)[i];
820 
821  edgeweight = (quadelem->idx1 == quadelem->idx2) ? 0.0 : nodeweights[quadelem->idx1] + nodeweights[quadelem->idx2];
822  SCIPdebugMessage("edge {%d,%d} = {%s,%s} coeff=%e edgeweight=%e\n", quadelem->idx1, quadelem->idx2,
823  SCIPvarGetName(SCIPnlrowGetQuadVars(nlrow)[quadelem->idx1]),
824  SCIPvarGetName(SCIPnlrowGetQuadVars(nlrow)[quadelem->idx2]), quadelem->coef, edgeweight);
825 
826  ub = (quadelem->idx1 == quadelem->idx2) ? 0.0 : 1.0;
827 
828  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "x#%d#%d", quadelem->idx1, quadelem->idx2);
829  SCIP_CALL( SCIPcreateVarBasic(subscip, &forwardarcs[i], name, 0.0, ub, 0.01 + edgeweight, SCIP_VARTYPE_BINARY) );
830  SCIP_CALL( SCIPaddVar(subscip, forwardarcs[i]) );
831 
832  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "x#%d#%d", quadelem->idx2, quadelem->idx1);
833  SCIP_CALL( SCIPcreateVarBasic(subscip, &backwardarcs[i], name, 0.0, ub, 0.01 + edgeweight, SCIP_VARTYPE_BINARY) );
834  SCIP_CALL( SCIPaddVar(subscip, backwardarcs[i]) );
835 
836  /* do not create redundant constraints for loops */
837  if( quadelem->idx1 == quadelem->idx2 )
838  continue;
839 
840  ++(*nedges);
841 
842  /* store all arcs which are important for the odd cycle property (no loops) */
843  if( rhsaggr && SCIPisPositive(scip, quadelem->coef) )
844  {
845  oddcyclearcs[noddcyclearcs++] = forwardarcs[i];
846  oddcyclearcs[noddcyclearcs++] = backwardarcs[i];
847  }
848 
849  if( !rhsaggr && SCIPisNegative(scip, quadelem->coef) )
850  {
851  oddcyclearcs[noddcyclearcs++] = forwardarcs[i];
852  oddcyclearcs[noddcyclearcs++] = backwardarcs[i];
853  }
854 
855  /* add constraints to ensure no parallel edges */
856  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "cons_noparalleledges");
857  SCIP_CALL( SCIPcreateConsBasicLinear(subscip, &noparallelcons, name, 0, NULL, NULL, 0.0, 1.0) );
858  SCIP_CALL( SCIPaddCoefLinear(subscip, noparallelcons, forwardarcs[i], 1.0) );
859  SCIP_CALL( SCIPaddCoefLinear(subscip, noparallelcons, backwardarcs[i], 1.0) );
860  SCIP_CALL( SCIPaddCons(subscip, noparallelcons) );
861  SCIP_CALL( SCIPreleaseCons(subscip, &noparallelcons) );
862  }
863 
864  /* odd cycle property constraint */
865  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "cons_oddcycle");
866  SCIP_CALL( SCIPcreateConsBasicXor(subscip, &oddcyclecons, name, TRUE, noddcyclearcs, oddcyclearcs) );
867  SCIP_CALL( SCIPaddCons(subscip, oddcyclecons) );
868  SCIP_CALL( SCIPreleaseCons(subscip, &oddcyclecons) );
869  SCIPfreeBufferArray(subscip, &oddcyclearcs);
870 
871  /* cycle length constraint */
872  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "cons_cyclelength");
873  SCIP_CALL( SCIPcreateConsBasicLinear(subscip, &cyclelengthcons, name, 0, NULL, NULL,
874  (SCIP_Real) sepadata->minaggrsize, (SCIP_Real) sepadata->maxaggrsize) );
875 
876  for( i = 0; i < narcs; ++i )
877  {
878  SCIP_CALL( SCIPaddCoefLinear(subscip, cyclelengthcons, forwardarcs[i], 1.0) );
879  SCIP_CALL( SCIPaddCoefLinear(subscip, cyclelengthcons, backwardarcs[i], 1.0) );
880  }
881 
882  SCIP_CALL( SCIPaddCons(subscip, cyclelengthcons) );
883  SCIP_CALL( SCIPreleaseCons(subscip, &cyclelengthcons) );
884 
885  /* create flow conservation constraints */
886  SCIP_CALL( SCIPallocBufferArray(subscip, &flowcons, nnodes) );
887 
888  for( i = 0; i < nnodes; ++i )
889  {
890  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "cons_flowconservation#%d", i);
891  SCIP_CALL( SCIPcreateConsBasicLinear(subscip, &flowcons[i], name, 0, NULL, NULL, 0.0, 0.0) );
892  }
893 
894  for( i = 0; i < narcs; ++i )
895  {
896  int u;
897  int v;
898 
899  u = SCIPnlrowGetQuadElems(nlrow)[i].idx1;
900  assert(u >= 0 && u < SCIPnlrowGetNQuadVars(nlrow));
901  v = SCIPnlrowGetQuadElems(nlrow)[i].idx2;
902  assert(v >= 0 && v < SCIPnlrowGetNQuadVars(nlrow));
903 
904  SCIP_CALL( SCIPaddCoefLinear(subscip, flowcons[u], forwardarcs[i], 1.0) );
905  SCIP_CALL( SCIPaddCoefLinear(subscip, flowcons[u], backwardarcs[i], -1.0) );
906 
907  SCIP_CALL( SCIPaddCoefLinear(subscip, flowcons[v], forwardarcs[i], -1.0) );
908  SCIP_CALL( SCIPaddCoefLinear(subscip, flowcons[v], backwardarcs[i], 1.0) );
909  }
910 
911  for( i = 0; i < nnodes; ++i )
912  {
913  SCIP_CALL( SCIPaddCons(subscip, flowcons[i]) );
914  SCIP_CALL( SCIPreleaseCons(subscip, &flowcons[i]) );
915  }
916 
917  SCIPfreeBufferArray(subscip, &flowcons);
918 
919  return SCIP_OKAY;
920 }
921 
922 /** fixed all arc variables (u,v) for which u or v is already in an edge-concave aggregation */
923 static
925  SCIP* subscip, /**< auxiliary SCIP to search aggregations */
926  SCIP_NLROW* nlrow, /**< nonlinear row */
927  SCIP_VAR** forwardarcs, /**< forward arc variables */
928  SCIP_VAR** backwardarcs, /**< backward arc variables */
929  int* quadvar2aggr, /**< mapping of quadvars to e.c. aggr. index (< 0: in no aggr.) */
930  int* nedges /**< pointer to store the number of nonexcluded edges */
931  )
932 {
933  int i;
934 
935  assert(subscip != NULL);
936  assert(nlrow != NULL);
937  assert(forwardarcs != NULL);
938  assert(backwardarcs != NULL);
939  assert(quadvar2aggr != NULL);
940  assert(nedges != NULL);
941 
942  SCIP_CALL( SCIPfreeTransform(subscip) );
943 
944  /* recompute the number of edges */
945  *nedges = 0;
946 
947  /* fix each arc to 0 if at least one of its nodes is contained in an e.c. aggregation */
948  for( i = 0; i < SCIPnlrowGetNQuadElems(nlrow); ++i )
949  {
950  SCIP_QUADELEM* quadelem;
951 
952  quadelem = &SCIPnlrowGetQuadElems(nlrow)[i];
953 
954  if( quadvar2aggr[quadelem->idx1] != -1 || quadvar2aggr[quadelem->idx2] != -1 )
955  {
956  SCIP_CALL( SCIPchgVarUb(subscip, forwardarcs[i], 0.0) );
957  SCIP_CALL( SCIPchgVarUb(subscip, backwardarcs[i], 0.0) );
958  }
959  else
960  *nedges += (quadelem->idx1 != quadelem->idx2) ? 1 : 0;
961  }
962 
963  return SCIP_OKAY;
964 }
965 
966 /** stores the best edge-concave aggregation found by the MIP model */
967 static
969  SCIP* subscip, /**< auxiliary SCIP to search aggregations */
970  SCIP_NLROW* nlrow, /**< nonlinear row */
971  SCIP_VAR** forwardarcs, /**< forward arc variables */
972  SCIP_VAR** backwardarcs, /**< backward arc variables */
973  int* quadvar2aggr, /**< mapping of quadvars to e.c. aggr. index (< 0: in no aggr.) */
974  int nfoundsofar /**< number of e.c. aggregation found so far */
975  )
976 {
977  SCIP_SOL* sol;
978  int i;
979 
980  assert(subscip != NULL);
981  assert(nlrow != NULL);
982  assert(forwardarcs != NULL);
983  assert(backwardarcs != NULL);
984  assert(quadvar2aggr != NULL);
985  assert(nfoundsofar >= 0);
986  assert(SCIPgetStatus(subscip) < SCIP_STATUS_INFEASIBLE);
987  assert(SCIPgetNSols(subscip) > 0);
988 
989  sol = SCIPgetBestSol(subscip);
990  assert(sol != NULL);
991 
992  for( i = 0; i < SCIPnlrowGetNQuadElems(nlrow); ++i )
993  {
994  SCIP_QUADELEM* quadelem;
995 
996  quadelem = &SCIPnlrowGetQuadElems(nlrow)[i];
997 
998  if( SCIPisGT(subscip, SCIPgetSolVal(subscip, sol, forwardarcs[i]), 0.5)
999  || SCIPisGT(subscip, SCIPgetSolVal(subscip, sol, backwardarcs[i]), 0.5) )
1000  {
1001  assert(quadvar2aggr[quadelem->idx1] == -1 || quadvar2aggr[quadelem->idx1] == nfoundsofar);
1002  assert(quadvar2aggr[quadelem->idx2] == -1 || quadvar2aggr[quadelem->idx2] == nfoundsofar);
1003 
1004  quadvar2aggr[quadelem->idx1] = nfoundsofar;
1005  quadvar2aggr[quadelem->idx2] = nfoundsofar;
1006  }
1007  }
1008 
1009  return SCIP_OKAY;
1010 }
1011 
1012 /** searches for edge-concave aggregations with an MIP model based on binary flow variables */
1013 static
1015  SCIP* subscip, /**< SCIP data structure */
1016  SCIP_Real timelimit, /**< time limit to solve the MIP */
1017  int nedges, /**< number of nonexcluded undirected edges */
1018  SCIP_Bool* aggrleft, /**< pointer to store if there might be a left aggregation */
1019  SCIP_Bool* found /**< pointer to store if we have found an aggregation */
1020  )
1021 {
1022  assert(subscip != NULL);
1023  assert(aggrleft != NULL);
1024  assert(found != NULL);
1025  assert(nedges >= 0);
1026 
1027  *aggrleft = TRUE;
1028  *found = FALSE;
1029 
1030  if( SCIPisLE(subscip, timelimit, 0.0) )
1031  return SCIP_OKAY;
1032 
1033  /* set working limits */
1034  SCIP_CALL( SCIPsetRealParam(subscip, "limits/time", timelimit) );
1035  SCIP_CALL( SCIPsetLongintParam(subscip, "limits/totalnodes", SUBSCIP_NODELIMIT) );
1036 
1037  /* set heuristics to aggressive */
1039 
1040  /* disable output to console in optimized mode, enable in SCIP's debug mode */
1041 #ifdef SCIP_DEBUG
1042  SCIP_CALL( SCIPsetIntParam(subscip, "display/verblevel", 5) );
1043  SCIP_CALL( SCIPsetIntParam(subscip, "display/freq", 1) );
1044 #else
1045  SCIP_CALL( SCIPsetIntParam(subscip, "display/verblevel", 0) );
1046 #endif
1047 
1048  SCIP_CALL( SCIPsolve(subscip) );
1049 
1050  /* no more aggregation left if the MIP is infeasible */
1051  if( SCIPgetStatus(subscip) >= SCIP_STATUS_INFEASIBLE )
1052  {
1053  *found = FALSE;
1054  *aggrleft = FALSE;
1055  return SCIP_OKAY;
1056  }
1057 
1058  if( SCIPgetNSols(subscip) > 0 )
1059  {
1060  *found = TRUE;
1061  *aggrleft = TRUE;
1062 
1063 #ifdef SCIP_DEBUG
1064  if( SCIPgetNSols(subscip) > 0 )
1065  {
1066  SCIP_CALL( SCIPprintSol(subscip, SCIPgetBestSol(subscip), NULL , FALSE) );
1067  }
1068 #endif
1069  }
1070 
1071  return SCIP_OKAY;
1072 }
1073 
1074 /** creates a tclique graph from a given nonlinear row
1075  *
1076  * SCIP's clique code can only handle integer node weights; all node weights are scaled by a factor of 100; since the
1077  * clique code ignores nodes with weight of zero, we add an offset of 100 to each weight
1078  */
1079 static
1081  SCIP* scip, /**< SCIP data structure */
1082  SCIP_NLROW* nlrow, /**< nonlinear row */
1083  TCLIQUE_GRAPH** graph, /**< TCLIQUE graph structure */
1084  SCIP_Real* nodeweights /**< weights for each quadratic variable (nodes in the graph) */
1085  )
1086 {
1087  int i;
1088 
1089  assert(graph != NULL);
1090  assert(nlrow != NULL);
1091 
1092  /* create the tclique graph */
1093  if( !tcliqueCreate(graph) )
1094  {
1095  SCIPerrorMessage("could not create clique graph\n");
1096  return SCIP_ERROR;
1097  }
1098 
1099  /* add all nodes to the tclique graph */
1100  for( i = 0; i < SCIPnlrowGetNQuadVars(nlrow); ++i )
1101  {
1102  int nodeweight;
1103 
1104  /* note: clique code can only handle integer weights */
1105  nodeweight = 100 + (int)(100 * nodeweights[i]);
1106  /* SCIPdebugMessage("%d (%s): nodeweight %d \n", i, SCIPvarGetName(SCIPnlrowGetQuadVars(nlrow)[i]), nodeweight); */
1107 
1108  if( !tcliqueAddNode(*graph, i, nodeweight) )
1109  {
1110  SCIPerrorMessage("could not add node to clique graph\n");
1111  return SCIP_ERROR;
1112  }
1113  }
1114 
1115  /* add all edges */
1116  for( i = 0; i < SCIPnlrowGetNQuadElems(nlrow); ++i )
1117  {
1118  SCIP_QUADELEM* quadelem;
1119  SCIP_VAR* bilinvar1;
1120  SCIP_VAR* bilinvar2;
1121 
1122  quadelem = &SCIPnlrowGetQuadElems(nlrow)[i];
1123  assert(quadelem != NULL);
1124  assert(!SCIPisZero(scip, quadelem->coef));
1125 
1126  bilinvar1 = SCIPnlrowGetQuadVars(nlrow)[quadelem->idx1];
1127  bilinvar2 = SCIPnlrowGetQuadVars(nlrow)[quadelem->idx2];
1128  assert(bilinvar1 != NULL);
1129  assert(bilinvar2 != NULL);
1130 
1131  /* do not add the edge {i,i} */
1132  if( bilinvar1 == bilinvar2 )
1133  continue;
1134 
1135  assert(quadelem->idx1 != quadelem->idx2);
1136 
1137 #ifdef SCIP_DEBUG_DETAILED
1138  SCIPdebugMessage(" add edge (%d, %d) = (%s,%s) to tclique graph\n",
1139  SCIPvarGetIndex(bilinvar1), SCIPvarGetIndex(bilinvar2),
1140  SCIPvarGetName(bilinvar1), SCIPvarGetName(bilinvar2));
1141 #endif
1142 
1143  if( !tcliqueAddEdge(*graph, quadelem->idx1, quadelem->idx2) )
1144  {
1145  SCIPerrorMessage("could not add edge to clique graph\n");
1146  return SCIP_ERROR;
1147  }
1148  }
1149 
1150  /* flush the clique graph */
1151  if( !tcliqueFlush(*graph) )
1152  {
1153  SCIPerrorMessage("could not flush the clique graph\n");
1154  return SCIP_ERROR;
1155  }
1156 
1157  return SCIP_OKAY;
1158 }
1159 
1160 /** searches for edge-concave aggregations by computing cliques in the graph representation of a given nonlinear row
1161  * update graph, compute clique, store clique; after computing a clique we heuristically check if the clique contains
1162  * at least one good cycle
1163  */
1164 static
1166  SCIP* scip, /**< SCIP data structure */
1167  TCLIQUE_GRAPH* graph, /**< TCLIQUE graph structure */
1168  SCIP_SEPADATA* sepadata, /**< separator data */
1169  SCIP_NLROW* nlrow, /**< nonlinear row */
1170  int* quadvar2aggr, /**< mapping of quadvars to e.c. aggr. index (< 0: in no aggr.) */
1171  int nfoundsofar, /**< number of e.c. aggregation found so far */
1172  SCIP_Bool rhsaggr, /**< consider nonlinear row aggregation for g(x) <= rhs (TRUE) or
1173  * lhs <= g(x) (FALSE) */
1174  SCIP_Bool* foundaggr, /**< pointer to store if we have found an aggregation */
1175  SCIP_Bool* foundclique /**< pointer to store if we have found a clique */
1176  )
1177 {
1178  SCIP_HASHMAP* cliquemap;
1179  TCLIQUE_STATUS status;
1180  int* maxcliquenodes;
1181  int* degrees;
1182  int nmaxcliquenodes;
1183  int maxcliqueweight;
1184  int noddcycleedges;
1185  int ntwodegrees;
1186  int aggrsize;
1187  int i;
1188 
1189  assert(graph != NULL);
1190  assert(nfoundsofar >= 0);
1191  assert(foundaggr != NULL);
1192  assert(foundclique != NULL);
1193  assert(SCIPnlrowGetNQuadVars(nlrow) == tcliqueGetNNodes(graph));
1194 
1195  cliquemap = NULL;
1196  *foundaggr = FALSE;
1197  *foundclique = FALSE;
1198 
1199  /* exclude all nodes which are already in an edge-concave aggregation (no flush is needed) */
1200  for( i = 0; i < SCIPnlrowGetNQuadVars(nlrow); ++i )
1201  {
1202  if( quadvar2aggr[i] != -1 )
1203  {
1204  SCIPdebugMessage("exclude node %d from clique graph\n", i);
1205  tcliqueChangeWeight(graph, i, 0);
1206  }
1207  }
1208 
1209  SCIP_CALL( SCIPallocBufferArray(scip, &maxcliquenodes, SCIPnlrowGetNQuadVars(nlrow)) );
1210 
1211  /* solve clique problem */
1212  tcliqueMaxClique(tcliqueGetNNodes, tcliqueGetWeights, tcliqueIsEdge, tcliqueSelectAdjnodes, graph, NULL, NULL,
1213  maxcliquenodes, &nmaxcliquenodes, &maxcliqueweight, CLIQUE_MAXFIRSTNODEWEIGHT, CLIQUE_MINWEIGHT,
1214  CLIQUE_MAXNTREENODES, CLIQUE_BACKTRACKFREQ, 0, -1, NULL, &status);
1215 
1216  if( status != TCLIQUE_OPTIMAL || nmaxcliquenodes < sepadata->minaggrsize )
1217  goto TERMINATE;
1218 
1219  *foundclique = TRUE;
1220  aggrsize = MIN(sepadata->maxaggrsize, nmaxcliquenodes);
1221  SCIP_CALL( SCIPhashmapCreate(&cliquemap, SCIPblkmem(scip), SCIPcalcHashtableSize(aggrsize)) );
1222 
1223  for( i = 0; i < aggrsize; ++i )
1224  {
1225  SCIP_CALL( SCIPhashmapInsert(cliquemap, (void*) (size_t) maxcliquenodes[i], NULL) );
1226  }
1227 
1228  /* count the degree of good cycle edges for each node in the clique */
1229  SCIP_CALL( SCIPallocBufferArray(scip, &degrees, aggrsize) );
1230  BMSclearMemoryArray(degrees, aggrsize);
1231  ntwodegrees = 0;
1232 
1233  /* count the number of positive or negative edges (depending on <= rhs or >= lhs) */
1234  noddcycleedges = 0;
1235  for( i = 0; i < SCIPnlrowGetNQuadElems(nlrow); ++i )
1236  {
1237  SCIP_QUADELEM* quadelem;
1238  SCIP_Bool isoddcycleedge;
1239 
1240  quadelem = &SCIPnlrowGetQuadElems(nlrow)[i];
1241  isoddcycleedge = (rhsaggr && SCIPisPositive(scip, quadelem->coef))
1242  || (!rhsaggr && SCIPisNegative(scip, quadelem->coef));
1243 
1244  if( isoddcycleedge
1245  && SCIPhashmapExists(cliquemap, (void*) (size_t) quadelem->idx1)
1246  && SCIPhashmapExists(cliquemap, (void*) (size_t) quadelem->idx2) )
1247  {
1248  ++noddcycleedges;
1249  ++degrees[quadelem->idx1];
1250  ++degrees[quadelem->idx2];
1251  }
1252  }
1253 
1254  /* count the number of nodes with exactly two incident odd cycle edges */
1255  for( i = 0; i < aggrsize; ++i )
1256  if( degrees[i] == 2 )
1257  ++ntwodegrees;
1258 
1259  /* check cases for which we are sure that there are no good cycles in the clique */
1260  if( noddcycleedges == 0 || (aggrsize == 3 && noddcycleedges == 2) || (aggrsize == 4 && ntwodegrees == 4) )
1261  *foundaggr = FALSE;
1262  else
1263  *foundaggr = TRUE;
1264 
1265  /* add the found clique as an edge-concave aggregation or exclude the nodes from the remaining search */
1266  for( i = 0; i < aggrsize; ++i )
1267  {
1268  quadvar2aggr[ maxcliquenodes[i] ] = *foundaggr ? nfoundsofar : -2;
1269  SCIPdebugMessage("%s %d\n", *foundaggr ? "aggregate node: " : "exclude node: ", maxcliquenodes[i]);
1270  }
1271 
1272  SCIPfreeBufferArray(scip, &degrees);
1273 
1274 TERMINATE:
1275  if( cliquemap != NULL )
1276  SCIPhashmapFree(&cliquemap);
1277  SCIPfreeBufferArray(scip, &maxcliquenodes);
1278 
1279  return SCIP_OKAY;
1280 }
1281 
1282 /** computes a partitioning into edge-concave aggregations for a given (quadratic) nonlinear row; each aggregation has
1283  * to contain a cycle with an odd number of positive weighted edges (good cycles) in the corresponding graph representation
1284  *
1285  * For this we use the following algorithm:
1286  *
1287  * -# use a MIP model based on binary flow variables to compute good cycles and store the implied subgraphs as an e.c. aggr.
1288  * -# if we find a good cycle, store the implied subgraph, delete it from the graph representation and go to 1)
1289  * -# if the MIP model is infeasible (there are no good cycles), STOP
1290  * -# we compute a large clique C if the MIP model fails (because of working limits, etc)
1291  * -# if we find a good cycle in C, store the implied subgraph of C, delete it from the graph representation and go to 1)
1292  * -# if C is not large enough, STOP
1293  */
1294 static
1296  SCIP* scip, /**< SCIP data structure */
1297  SCIP_SEPADATA* sepadata, /**< separator data */
1298  SCIP_NLROW* nlrow, /**< nonlinear row */
1299  SCIP_SOL* sol, /**< current solution (might be NULL) */
1300  SCIP_Bool rhsaggr, /**< consider nonlinear row aggregation for g(x) <= rhs (TRUE) or g(x) >= lhs (FALSE) */
1301  int* quadvar2aggr, /**< array to store for each quadratic variable in which edge-concave
1302  * aggregation it is stored (< 0: in no aggregation); size has to be at
1303  * least SCIPnlrowGetNQuadVars(nlrow) */
1304  int* nfound /**< pointer to store the number of found e.c. aggregations */
1305  )
1306 {
1307  SCIP* subscip;
1308  TCLIQUE_GRAPH* graph;
1309  SCIP_VAR** forwardarcs;
1310  SCIP_VAR** backwardarcs;
1311  SCIP_VAR** quadvars;
1312  SCIP_Real* nodeweights;
1313  SCIP_Real timelimit;
1314  int nquadelems;
1315  int nquadvars;
1316  int nunsucces;
1317  int nedges;
1318  int i;
1319 
1320  assert(quadvar2aggr != NULL);
1321  assert(nfound != NULL);
1322 
1323  quadvars = SCIPnlrowGetQuadVars(nlrow);
1324  nquadvars = SCIPnlrowGetNQuadVars(nlrow);
1325  nquadelems = SCIPnlrowGetNQuadElems(nlrow);
1326 
1327  graph = NULL;
1328  *nfound = 0;
1329  nunsucces = 0;
1330 
1331  /* inititialize mapping from quadvars to e.c. aggregation index (-1: quadvar is in no aggregation); compute node
1332  * weights
1333  */
1334  SCIP_CALL( SCIPallocBufferArray(scip, &nodeweights, nquadvars) );
1335  for( i = 0; i < SCIPnlrowGetNQuadVars(nlrow); ++i )
1336  {
1337  SCIP_VAR* var = quadvars[i];
1338  quadvar2aggr[i] = -1;
1339  nodeweights[i] = phi(scip, SCIPgetSolVal(scip, sol, var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var) );
1340  SCIPdebugMessage("%s = %e (%e in [%e, %e])\n", SCIPvarGetName(var), nodeweights[i], SCIPgetSolVal(scip, sol, var),
1342  }
1343 
1344  /* create and set up a sub-SCIP */
1345  SCIP_CALL( SCIPcreate(&subscip) );
1346 
1347  /* arrays to store all arc variables of the MIP model; note that we introduce variables even for loops in the graph
1348  * to have an easy mapping from the edges of the graph to the quadratic elements
1349  */
1350  SCIP_CALL( SCIPallocMemoryArray(subscip, &forwardarcs, nquadelems) );
1351  SCIP_CALL( SCIPallocMemoryArray(subscip, &backwardarcs, nquadelems) );
1352 
1353  SCIP_CALL( createMIP(scip, subscip, sepadata, nlrow, rhsaggr, forwardarcs, backwardarcs, nodeweights, &nedges) );
1354  assert(nedges >= 0);
1355  SCIPdebugMessage("nedges (without loops) = %d\n", nedges);
1356 
1357  SCIP_CALL( SCIPgetRealParam(scip, "limits/time", &timelimit) );
1358 
1359  /* main loop to search for edge-concave aggregations */
1360  while( !SCIPisStopped(scip) )
1361  {
1362  SCIP_Bool aggrleft;
1363  SCIP_Bool found;
1364 
1365  SCIPdebugMessage("#remaining edges = %d\n", nedges);
1366 
1367  /* not enough edges left */
1368  if( nedges < sepadata->minaggrsize )
1369  break;
1370 
1371  /* check whether there is enough time left; update the remaining time */
1372  if( !SCIPisInfinity(scip, timelimit) )
1373  {
1374  timelimit -= SCIPgetSolvingTime(scip);
1375  if( timelimit <= 0.0 )
1376  {
1377  SCIPdebugMessage("skip aggregation search since no time left\n");
1378  goto TERMINATE;
1379  }
1380  }
1381 
1382  /* 1.a - search for edge-concave aggregation with the help of the MIP model */
1383  SCIP_CALL( searchEcAggrWithMIP(subscip, timelimit, nedges, &aggrleft, &found) );
1384 
1385  /* 1.b - there are no more edge-concave aggregations left */
1386  if( !aggrleft )
1387  {
1388  SCIPdebugMessage("no more aggregation left\n");
1389  break;
1390  }
1391 
1392  if( found )
1393  {
1394  SCIP_CALL( storeAggrFromMIP(subscip, nlrow, forwardarcs, backwardarcs, quadvar2aggr, *nfound) );
1395  ++(*nfound);
1396  nunsucces = 0;
1397  }
1398  /* try to find an edge-concave aggregation by computing cliques */
1399  else
1400  {
1401  SCIP_Bool foundaggr;
1402  SCIP_Bool foundclique;
1403 
1404  ++nunsucces;
1405 
1406  /* create graph if necessary */
1407  if( graph == NULL )
1408  {
1409  SCIP_CALL( createTcliqueGraph(scip, nlrow, &graph, nodeweights) );
1410  assert(graph != NULL);
1411  }
1412 
1413  /* 2.a - search and store a single edge-concave aggregation by computing a clique with a good cycle */
1414  SCIP_CALL( searchEcAggrWithCliques(scip, graph, sepadata, nlrow, quadvar2aggr, *nfound, rhsaggr,
1415  &foundaggr, &foundclique) );
1416 
1417  if( foundaggr )
1418  {
1419  assert(foundclique);
1420  ++(*nfound);
1421  nunsucces = 0;
1422  }
1423  else
1424  ++nunsucces;
1425 
1426  /* 2.b - no clique of at least minaggrsize size found */
1427  if( !foundclique )
1428  {
1429  assert(!foundaggr);
1430  SCIPdebugMessage("did not find a clique to exclude -> leave aggregation search\n");
1431  break;
1432  }
1433  }
1434 
1435  /* leave the algorithm if we did not find something for maxstallrounds many iterations */
1436  if( nunsucces >= sepadata->maxstallrounds && *nfound == 0 )
1437  {
1438  SCIPdebugMessage("did not find an e.c. aggregation for %d iterations\n", nunsucces);
1439  break;
1440  }
1441 
1442  /* exclude all edges used in the last aggregation and nodes found in the clique solution */
1443  SCIP_CALL( updateMIP(subscip, nlrow, forwardarcs, backwardarcs, quadvar2aggr, &nedges) );
1444  }
1445 
1446 TERMINATE:
1447 
1448 #ifdef SCIP_DEBUG
1449  SCIPdebugMessage("aggregations found:\n");
1450  for( i = 0; i < nquadvars; ++i )
1451  {
1452  SCIPdebugMessage(" %d in %d\n", i, quadvar2aggr[i]);
1453  }
1454 #endif
1455 
1456  /* free clique graph */
1457  if( graph != NULL )
1458  tcliqueFree(&graph);
1459 
1460  /* free sub-SCIP */
1461  for( i = 0; i < nquadelems; ++i )
1462  {
1463  SCIP_CALL( SCIPreleaseVar(subscip, &forwardarcs[i]) );
1464  SCIP_CALL( SCIPreleaseVar(subscip, &backwardarcs[i]) );
1465  }
1466 
1467  SCIPfreeMemoryArray(subscip, &backwardarcs);
1468  SCIPfreeMemoryArray(subscip, &forwardarcs);
1469  SCIP_CALL( SCIPfree(&subscip) );
1470 
1471  SCIPfreeBufferArray(scip, &nodeweights);
1472 
1473  return SCIP_OKAY;
1474 }
1475 
1476 /** returns whether a given nonlinear row can be used to compute edge-concave aggregations for which their convex
1477  * envelope could dominate the termwise bilinear relaxation; this is the case if there exists at least one cycle with
1478  * an odd number of positive edges in the corresponding graph representation of the nonlinear row
1479  */
1480 static
1482  SCIP* scip, /**< SCIP data structure */
1483  SCIP_SEPADATA* sepadata, /**< separator data */
1484  SCIP_NLROW* nlrow, /**< nonlinear row representation of a nonlinear constraint */
1485  SCIP_Bool* rhscandidate, /**< pointer to store if we should compute edge-concave aggregations for
1486  * the <= rhs case */
1487  SCIP_Bool* lhscandidate /**< pointer to store if we should compute edge-concave aggregations for
1488  * the >= lhs case */
1489  )
1490 {
1491  int* degrees;
1492  int ninterestingnodes;
1493  int nposedges;
1494  int nnegedges;
1495  int i;
1496 
1497  assert(rhscandidate != NULL);
1498  assert(lhscandidate != NULL);
1499 
1500  *rhscandidate = TRUE;
1501  *lhscandidate = TRUE;
1502 
1503  /* skip if the nlrow is not in the NLP, there are other nonlinearities, or too few quadratic variables */
1504  if( !SCIPnlrowIsInNLP(nlrow) || SCIPnlrowGetExprtree(nlrow) != NULL
1505  || SCIPnlrowGetNQuadVars(nlrow) < sepadata->minaggrsize )
1506  {
1507  *rhscandidate = FALSE;
1508  *lhscandidate = FALSE;
1509  return SCIP_OKAY;
1510  }
1511 
1512  /* check for infinite rhs or lhs */
1513  if( SCIPisInfinity(scip, REALABS(SCIPnlrowGetRhs(nlrow))) )
1514  *rhscandidate = FALSE;
1515  if( SCIPisInfinity(scip, REALABS(SCIPnlrowGetLhs(nlrow))) )
1516  *lhscandidate = FALSE;
1517 
1518  SCIP_CALL( SCIPallocBufferArray(scip, &degrees, SCIPnlrowGetNQuadVars(nlrow)) );
1519  BMSclearMemoryArray(degrees, SCIPnlrowGetNQuadVars(nlrow));
1520 
1521  ninterestingnodes = 0;
1522  nposedges = 0;
1523  nnegedges = 0;
1524 
1525  for( i = 0; i < SCIPnlrowGetNQuadElems(nlrow); ++i )
1526  {
1527  SCIP_QUADELEM* quadelem;
1528  SCIP_VAR* x;
1529  SCIP_VAR* y;
1530 
1531  quadelem = &SCIPnlrowGetQuadElems(nlrow)[i];
1532  x = SCIPnlrowGetQuadVars(nlrow)[quadelem->idx1];
1533  y = SCIPnlrowGetQuadVars(nlrow)[quadelem->idx2];
1534 
1535  /* do not consider loops or global fixed variables */
1536  if( quadelem->idx1 != quadelem->idx2
1538  && !SCIPisEQ(scip, SCIPvarGetLbGlobal(y), SCIPvarGetUbGlobal(y)) )
1539  {
1540  ++degrees[quadelem->idx1];
1541  ++degrees[quadelem->idx2];
1542 
1543  /* count the number of nodes with a degree of at least 2 */
1544  if( degrees[quadelem->idx1] == 2 )
1545  ++ninterestingnodes;
1546  if( degrees[quadelem->idx2] == 2 )
1547  ++ninterestingnodes;
1548 
1549  nposedges += SCIPisPositive(scip, quadelem->coef) ? 1 : 0;
1550  nnegedges += SCIPisNegative(scip, quadelem->coef) ? 1 : 0;
1551  }
1552  }
1553 
1554  SCIPfreeBufferArray(scip, &degrees);
1555 
1556  SCIPdebugMessage("nlrow contains: %d edges\n", nposedges + nnegedges);
1557 
1558  /* too many edges, too few edges, or to few nodes with degree at least 2 in the graph */
1559  if( nposedges + nnegedges > sepadata->maxbilinterms || nposedges + nnegedges < sepadata->minaggrsize
1560  || ninterestingnodes < sepadata->minaggrsize )
1561  {
1562  *rhscandidate = FALSE;
1563  *lhscandidate = FALSE;
1564  return SCIP_OKAY;
1565  }
1566 
1567  /* check if there are enough positive/negative edges; for a 3-clique there has to be an odd number of those edges */
1568  if( nposedges == 0 || (nposedges + nnegedges == 3 && (nposedges % 2) == 0) )
1569  *rhscandidate = FALSE;
1570  if( nnegedges == 0 || (nposedges + nnegedges == 3 && (nnegedges % 2) == 0) )
1571  *lhscandidate = FALSE;
1572 
1573  return SCIP_OKAY;
1574 }
1575 
1576 /** finds and stores edge-concave aggregations for a given nonlinear row */
1577 static
1579  SCIP* scip, /**< SCIP data structure */
1580  SCIP_SEPADATA* sepadata, /**< separator data */
1581  SCIP_NLROW* nlrow, /**< nonlinear row */
1582  SCIP_SOL* sol /**< current solution (might be NULL) */
1583  )
1584 {
1585  int* quadvar2aggr;
1586  SCIP_Bool rhscandidate;
1587  SCIP_Bool lhscandidate;
1588 
1589  assert(scip != NULL);
1590  assert(nlrow != NULL);
1591  assert(sepadata != NULL);
1592 
1593  SCIP_CALL( SCIPallocBufferArray(scip, &quadvar2aggr, SCIPnlrowGetNQuadVars(nlrow)) ); /*lint !e705*/
1594 
1595 #ifdef SCIP_DEBUG
1596  SCIPdebugMessage("search for edge-concave aggregation for the nonlinear row: \n");
1597  SCIP_CALL( SCIPnlrowPrint(nlrow, SCIPgetMessagehdlr(scip), NULL) );
1598 #endif
1599 
1600  /* check obvious conditions for existing cycles with an odd number of positive/negative edges */
1601  SCIP_CALL( isCandidate(scip, sepadata, nlrow, &rhscandidate, &lhscandidate) );
1602  SCIPdebugMessage("rhs candidate = %u lhs candidate = %u\n", rhscandidate, lhscandidate);
1603 
1604  /* search for edge-concave aggregations (consider <= rhs) */
1605  if( rhscandidate )
1606  {
1607  SCIP_NLROWAGGR* nlrowaggr;
1608  int nfound;
1609 
1610  assert(!SCIPisInfinity(scip, REALABS(SCIPnlrowGetRhs(nlrow))));
1611 
1612  SCIPdebugMessage("consider <= rhs\n");
1613  SCIP_CALL( searchEcAggr(scip, sepadata, nlrow, sol, TRUE, quadvar2aggr, &nfound) );
1614 
1615  if( nfound > 0 )
1616  {
1617  SCIP_CALL( nlrowaggrCreate(scip, nlrow, &nlrowaggr, quadvar2aggr, nfound, TRUE) );
1618  assert(nlrow != NULL);
1619  SCIPdebug(nlrowaggrPrint(scip, nlrowaggr));
1620  SCIP_CALL( sepadataAddNlrowaggr(scip, sepadata, nlrowaggr) );
1621  }
1622  }
1623 
1624  /* search for edge-concave aggregations (consider <= lhs) */
1625  if( lhscandidate )
1626  {
1627  SCIP_NLROWAGGR* nlrowaggr;
1628  int nfound;
1629 
1630  assert(!SCIPisInfinity(scip, REALABS(SCIPnlrowGetLhs(nlrow))));
1631 
1632  SCIPdebugMessage("consider >= lhs\n");
1633  SCIP_CALL( searchEcAggr(scip, sepadata, nlrow, sol, FALSE, quadvar2aggr, &nfound) );
1634 
1635  if( nfound > 0 )
1636  {
1637  SCIP_CALL( nlrowaggrCreate(scip, nlrow, &nlrowaggr, quadvar2aggr, nfound, FALSE) );
1638  assert(nlrow != NULL);
1639  SCIPdebug(nlrowaggrPrint(scip, nlrowaggr));
1640  SCIP_CALL( sepadataAddNlrowaggr(scip, sepadata, nlrowaggr) );
1641  }
1642  }
1643 
1644  SCIPfreeBufferArray(scip, &quadvar2aggr);
1645  return SCIP_OKAY;
1646 }
1647 
1648 /*
1649  * methods to compute edge-concave cuts
1650  */
1651 
1652 #ifdef SCIP_DEBUG
1653 /** prints a given facet (candidate) */
1654 static
1655 void printFacet(
1656  SCIP* scip, /**< SCIP data structure */
1657  SCIP_VAR** vars, /**< variables contained in the edge-concave aggregation */
1658  int nvars, /**< number of variables contained in the edge-concave aggregation */
1659  SCIP_Real* facet, /**< current facet candidate */
1660  SCIP_Real facetval /**< facet evaluated at the current solution */
1661  )
1662 {
1663  int i;
1664 
1665  SCIPdebugMessage("print facet (val=%e): ", facetval);
1666  for( i = 0; i < nvars; ++i )
1667  SCIPdebugPrintf("%e %s + ", facet[i], SCIPvarGetName(vars[i]));
1668  SCIPdebugPrintf("%e\n", facet[nvars]);
1669 }
1670 #endif
1671 
1672 /** checks if a facet is really an underestimate for all corners of the domain [l,u]; because of numerics it can happen
1673  * that a facet violates a corner of the domain; to make the facet valid we subtract the maximum violation from the
1674  * constant part of the facet; its a good excersise to write a comment describing the gray code...
1675  */
1676 static
1678  SCIP* scip, /**< SCIP data structure */
1679  SCIP_ECAGGR* ecaggr, /**< edge-concave aggregation data */
1680  SCIP_Real* fvals, /**< array containing all corner values of the aggregation */
1681  SCIP_Real* facet /**< current facet candidate (of dimension ecaggr->nvars + 1) */
1682  )
1683 {
1684  SCIP_Real maxviolation;
1685  SCIP_Real val;
1686  unsigned int i;
1687  unsigned int ncorner;
1688  unsigned int prev;
1689 
1690  assert(scip != NULL);
1691  assert(ecaggr != NULL);
1692  assert(fvals != NULL);
1693  assert(facet != NULL);
1694 
1695  ncorner = (unsigned int) poweroftwo[ecaggr->nvars];
1696  maxviolation = 0.0;
1697 
1698  /* check for the origin */
1699  val = facet[ecaggr->nvars];
1700  for( i = 0; i < (unsigned int) ecaggr->nvars; ++i )
1701  val += facet[i] * SCIPvarGetLbLocal(ecaggr->vars[i]);
1702 
1703  /* update maximum violation */
1704  maxviolation = MAX(val - fvals[0], maxviolation);
1705  assert(SCIPisFeasEQ(scip, maxviolation, 0.0));
1706 
1707  prev = 0;
1708  for( i = 1; i < ncorner; ++i )
1709  {
1710  unsigned int gray;
1711  unsigned int diff;
1712  unsigned int pos;
1713 
1714  gray = i ^ (i >> 1);
1715  diff = gray ^ prev;
1716 
1717  /* compute position of unique 1 of diff */
1718  pos = 0;
1719  while( (diff >>= 1) != 0 )
1720  ++pos;
1721 
1722  if( gray > prev )
1723  val += facet[pos] * (SCIPvarGetUbLocal(ecaggr->vars[pos]) - SCIPvarGetLbLocal(ecaggr->vars[pos]));
1724  else
1725  val -= facet[pos] * (SCIPvarGetUbLocal(ecaggr->vars[pos]) - SCIPvarGetLbLocal(ecaggr->vars[pos]));
1726 
1727 
1728  /* update maximum violation */
1729  maxviolation = MAX(val - fvals[gray], maxviolation);
1730  assert(SCIPisFeasEQ(scip, maxviolation, 0.0));
1731 
1732  prev = gray;
1733  }
1734 
1735  SCIPdebugMessage("maximum violation of facet: %2.8e\n", maxviolation);
1736 
1737  /* there seem to be numerical problems if the violation is too large; in this case we reject the facet */
1738  if( maxviolation > ADJUSTFACETTOL )
1739  return FALSE;
1740 
1741  /* adjust constant part of the facet */
1742  facet[ecaggr->nvars] -= maxviolation;
1743 
1744  return TRUE;
1745 }
1746 
1747 /** set up LP interface to solve LPs to compute the facet of the convex envelope */
1748 static
1750  SCIP* scip, /**< SCIP data structure */
1751  SCIP_SEPADATA* sepadata /**< separation data */
1752  )
1753 {
1754  SCIP_Real* obj;
1755  SCIP_Real* lb;
1756  SCIP_Real* ub;
1757  SCIP_Real* val;
1758  int* beg;
1759  int* ind;
1760  int nnonz;
1761  int ncols;
1762  int nrows;
1763  int i;
1764  int k;
1765 
1766  assert(scip != NULL);
1767  assert(sepadata != NULL);
1768  assert(sepadata->nnlrowaggrs > 0);
1769 
1770  /* LP interface has been already created with enough rows/columns*/
1771  if( sepadata->lpi != NULL && sepadata->lpisize >= sepadata->maxecsize )
1772  return SCIP_OKAY;
1773 
1774  /* size of lpi is too small; reconstruct lpi */
1775  if( sepadata->lpi != NULL )
1776  {
1777  SCIP_CALL( SCIPlpiFree(&sepadata->lpi) );
1778  sepadata->lpi = NULL;
1779  }
1780 
1781  assert(sepadata->lpi == NULL);
1782  SCIP_CALL( SCIPlpiCreate(&(sepadata->lpi), SCIPgetMessagehdlr(scip), "e.c. LP", SCIP_OBJSEN_MINIMIZE) );
1783  sepadata->lpisize = sepadata->maxecsize;
1784 
1785  nrows = sepadata->maxecsize + 1;
1786  ncols = poweroftwo[nrows - 1];
1787  nnonz = (ncols * (nrows + 1)) / 2;
1788  k = 0;
1789 
1790  /* allocate necessary memory */
1791  SCIP_CALL( SCIPallocBufferArray(scip, &obj, ncols) );
1792  SCIP_CALL( SCIPallocBufferArray(scip, &lb, ncols) );
1793  SCIP_CALL( SCIPallocBufferArray(scip, &ub, ncols) );
1794  SCIP_CALL( SCIPallocBufferArray(scip, &beg, ncols) );
1795  SCIP_CALL( SCIPallocBufferArray(scip, &val, nnonz) );
1796  SCIP_CALL( SCIPallocBufferArray(scip, &ind, nnonz) );
1797 
1798  /* calculate nonzero entries in the LP; set obj, lb, and ub to zero */
1799  for( i = 0; i < ncols; ++i )
1800  {
1801  int row;
1802  int a;
1803 
1804  obj[i] = 0.0;
1805  lb[i] = 0.0;
1806  ub[i] = 0.0;
1807 
1808  SCIPdebugMessage("col %i starts at position %d\n", i, k);
1809  beg[i] = k;
1810  row = 0;
1811  a = 1;
1812 
1813  /* iterate through the bit representation of i */
1814  while( a <= i )
1815  {
1816  if( (a & i) != 0 )
1817  {
1818  val[k] = 1.0;
1819  ind[k] = row;
1820 
1821  SCIPdebugMessage(" val[%d][%d] = 1 (position %d)\n", row, i, k);
1822 
1823  ++k;
1824  }
1825 
1826  a <<= 1; /*lint !e701*/
1827  ++row;
1828  assert(poweroftwo[row] == a);
1829  }
1830 
1831  /* put 1 as a coefficient for sum_{i} \lambda_i = 1 row (last row) */
1832  val[k] = 1.0;
1833  ind[k] = nrows - 1;
1834  ++k;
1835  SCIPdebugMessage(" val[%d][%d] = 1 (position %d)\n", nrows - 1, i, k);
1836  }
1837  assert(k == nnonz);
1838 
1839  /*
1840  * add all columns to the LP interface
1841  * CPLEX needs the row to exist before adding columns, so we create the rows with dummy sides
1842  * note that the assert is not needed once somebody fixes the LPI
1843  */
1844  assert(nrows <= ncols);
1845  SCIP_CALL( SCIPlpiAddRows(sepadata->lpi, nrows, obj, obj, NULL, 0, NULL, NULL, NULL) );
1846  SCIP_CALL( SCIPlpiAddCols(sepadata->lpi, ncols, obj, lb, ub, NULL, nnonz, beg, ind, val) );
1847 
1848  /* free allocated memory */
1849  SCIPfreeBufferArray(scip, &ind);
1850  SCIPfreeBufferArray(scip, &val);
1851  SCIPfreeBufferArray(scip, &beg);
1852  SCIPfreeBufferArray(scip, &ub);
1853  SCIPfreeBufferArray(scip, &lb);
1854  SCIPfreeBufferArray(scip, &obj);
1855 
1856  return SCIP_OKAY;
1857 }
1858 
1859 /** evaluates an edge-concave aggregation at a corner of the domain [l,u] */
1860 static
1862  SCIP_ECAGGR* ecaggr, /**< edge-concave aggregation data */
1863  int k /**< k-th corner */
1864  )
1865 {
1866  SCIP_Real val;
1867  int i;
1868 
1869  assert(ecaggr != NULL);
1870  assert(k >= 0 && k < poweroftwo[ecaggr->nvars]);
1871 
1872  val = 0.0;
1873 
1874  for( i = 0; i < ecaggr->nterms; ++i )
1875  {
1876  SCIP_Real coef;
1877  SCIP_Real bound1;
1878  SCIP_Real bound2;
1879  int idx1;
1880  int idx2;
1881 
1882  idx1 = ecaggr->termvars1[i];
1883  idx2 = ecaggr->termvars2[i];
1884  coef = ecaggr->termcoefs[i];
1885  assert(idx1 >= 0 && idx1 < ecaggr->nvars);
1886  assert(idx2 >= 0 && idx2 < ecaggr->nvars);
1887 
1888  bound1 = ((poweroftwo[idx1]) & k) == 0 ? SCIPvarGetLbLocal(ecaggr->vars[idx1]) : SCIPvarGetUbLocal(ecaggr->vars[idx1]);
1889  bound2 = ((poweroftwo[idx2]) & k) == 0 ? SCIPvarGetLbLocal(ecaggr->vars[idx2]) : SCIPvarGetUbLocal(ecaggr->vars[idx2]);
1890 
1891  val += coef * bound1 * bound2;
1892  }
1893 
1894  return val;
1895 }
1896 
1897 /** returns (val - lb) / (ub - lb) for a in [lb, ub] */
1898 static
1900  SCIP* scip, /**< SCIP data structure */
1901  SCIP_Real lb, /**< lower bound */
1902  SCIP_Real ub, /**< upper bound */
1903  SCIP_Real val /**< value in [lb,ub] */
1904  )
1905 {
1906  assert(scip != NULL);
1907  assert(!SCIPisInfinity(scip, -lb));
1908  assert(!SCIPisInfinity(scip, ub));
1909  assert(!SCIPisInfinity(scip, REALABS(val)));
1910  assert(!SCIPisFeasEQ(scip, ub - lb, 0.0)); /* this would mean that a variable has been fixed */
1911 
1912  /* adjust val */
1913  val = MIN(val, ub);
1914  val = MAX(val, lb);
1915 
1916  val = (val - lb) / (ub - lb);
1917  assert(val >= 0.0 && val <= 1.0);
1918 
1919  return val;
1920 }
1921 
1922 /** computes a facet of the convex envelope of an edge concave aggregation
1923  *
1924  * The algorithm solves the following LP:
1925  * \f{eqnarray}{
1926  * min & \sum_i \lambda_i f(v_i)\\
1927  * s.t. & \sum_i \lambda_i v_i = x\\
1928  * & \sum_i \lambda_i = 1\\
1929  * & \lambda_i \geq 0
1930  * \f}
1931  * where f is an edge concave function, \f$x\f$ in \f$[l,u]\f$ is a solution of the current relaxation, and \f$v_i\f$ are the vertices
1932  * of \f$[l,u]\f$; the method transforms the problem to the domain \f$[0,1]^n\f$, computes a facet, and transforms this facet to the
1933  * original space; the dual solution of the LP above are the coefficients of the facet
1934  *
1935  * The complete algorithm works as follows:
1936  *
1937  * -# compute f(v_i) for each corner v_i of [l,u]
1938  * -# set up the described LP for the transformed space
1939  * -# solve the LP and store the resulting facet for the transformed space
1940  * -# transform the facet to original space
1941  * -# adjust and check facet with the algorithm of Rikun et al.
1942  */
1943 static
1945  SCIP* scip, /**< SCIP data structure */
1946  SCIP_SEPADATA* sepadata, /**< separation data */
1947  SCIP_SOL* sol, /**< solution (might be NULL) */
1948  SCIP_ECAGGR* ecaggr, /**< edge-concave aggregation data */
1949  SCIP_Real* facet, /**< array to store the coefficients of the resulting facet; size has to be at least (ecaggr->nvars + 1) */
1950  SCIP_Real* facetval, /**< pointer to store the value of the facet evaluated at the current solution */
1951  SCIP_Bool* success /**< pointer to store if we have found a facet */
1952  )
1953 {
1954  SCIP_Real* fvals;
1955  SCIP_Real* side;
1956  SCIP_Real* lb;
1957  SCIP_Real* ub;
1958  SCIP_Real perturbation;
1959  int* inds;
1960  int ncorner;
1961  int ncols;
1962  int nrows;
1963  int i;
1964 
1965  assert(scip != NULL);
1966  assert(sepadata != NULL);
1967  assert(ecaggr != NULL);
1968  assert(facet != NULL);
1969  assert(facetval != NULL);
1970  assert(success != NULL);
1971  assert(ecaggr->nvars <= sepadata->maxecsize);
1972 
1973  *facetval = -SCIPinfinity(scip);
1974  *success = FALSE;
1975 
1976  /* create LP if this has not been done yet */
1977  SCIP_CALL( createLP(scip, sepadata) );
1978 
1979  assert(sepadata->lpi != NULL);
1980  assert(sepadata->lpisize >= ecaggr->nvars);
1981 
1982  SCIP_CALL( SCIPlpiGetNCols(sepadata->lpi, &ncols) );
1983  SCIP_CALL( SCIPlpiGetNRows(sepadata->lpi, &nrows) );
1984  ncorner = poweroftwo[ecaggr->nvars];
1985 
1986  assert(ncorner <= ncols);
1987  assert(ecaggr->nvars + 1 <= nrows);
1988  assert(nrows <= ncols);
1989 
1990  /* allocate necessary memory */
1991  SCIP_CALL( SCIPallocBufferArray(scip, &fvals, ncols) );
1992  SCIP_CALL( SCIPallocBufferArray(scip, &inds, ncols) );
1993  SCIP_CALL( SCIPallocBufferArray(scip, &lb, ncols) );
1994  SCIP_CALL( SCIPallocBufferArray(scip, &ub, ncols) );
1995  SCIP_CALL( SCIPallocBufferArray(scip, &side, ncols) );
1996 
1997  /*
1998  * 1. compute f(v_i) for each corner v_i of [l,u]
1999  * 2. set up the described LP for the transformed space
2000  */
2001  for( i = 0; i < ncols; ++i )
2002  {
2003  fvals[i] = i < ncorner ? evalCorner(ecaggr, i) : 0.0;
2004  inds[i] = i;
2005 
2006  /* update bounds; fix variables to zero which are currently not in the LP */
2007  lb[i] = 0.0;
2008  ub[i] = i < ncorner ? 1.0 : 0.0;
2009  SCIPdebugMessage("bounds of LP col %d = [%e, %e]; obj = %e\n", i, lb[i], ub[i], fvals[i]);
2010  }
2011 
2012  /* update lhs and rhs */
2013  perturbation = 0.001;
2014  for( i = 0; i < nrows; ++i )
2015  {
2016  /* note that the last row corresponds to sum_{j} \lambda_j = 1 */
2017  if( i < ecaggr->nvars )
2018  {
2019  SCIP_VAR* x;
2020 
2021  x = ecaggr->vars[i];
2022  assert(x != NULL);
2023 
2024  side[i] = transformValue(scip, SCIPvarGetLbLocal(x), SCIPvarGetUbLocal(x), SCIPgetSolVal(scip, sol, x));
2025 
2026  /* perturb point to enforce an LP solution with ecaggr->nvars + 1 nonzero */
2027  side[i] += side[i] > perturbation ? -perturbation : perturbation;
2028  perturbation /= 1.2;
2029  }
2030  else
2031  {
2032  side[i] = (i == nrows - 1) ? 1.0 : 0.0;
2033  }
2034 
2035  SCIPdebugMessage("LP row %d in [%e, %e]\n", i, side[i], side[i]);
2036  }
2037 
2038  /* update LP */
2039  SCIP_CALL( SCIPlpiChgObj(sepadata->lpi, ncols, inds, fvals) );
2040  SCIP_CALL( SCIPlpiChgBounds(sepadata->lpi, ncols, inds, lb, ub) );
2041  SCIP_CALL( SCIPlpiChgSides(sepadata->lpi, nrows, inds, side, side) );
2042 
2043  /* free memory used to build the LP */
2044  SCIPfreeBufferArray(scip, &side);
2045  SCIPfreeBufferArray(scip, &ub);
2046  SCIPfreeBufferArray(scip, &lb);
2047  SCIPfreeBufferArray(scip, &inds);
2048 
2049  /*
2050  * 3. solve the LP and store the resulting facet for the transformed space
2051  */
2052  if( USEDUALSIMPLEX ) /*lint !e774 !e506*/
2053  {
2054  SCIP_CALL( SCIPlpiSolveDual(sepadata->lpi) );
2055  }
2056  else
2057  {
2058  SCIP_CALL( SCIPlpiSolvePrimal(sepadata->lpi) );
2059  }
2060 
2061  /* the dual solution corresponds to the coefficients of the facet in the transformed problem; note that it might be
2062  * the case that the dual solution has more components than the facet array
2063  */
2064  if( ecaggr->nvars + 1 == ncols )
2065  {
2066  SCIP_CALL( SCIPlpiGetSol(sepadata->lpi, NULL, NULL, facet, NULL, NULL) );
2067  }
2068  else
2069  {
2070  SCIP_Real* dualsol;
2071 
2072  SCIP_CALL( SCIPallocBufferArray(scip, &dualsol, nrows) );
2073 
2074  /* get the dual solution */
2075  SCIP_CALL( SCIPlpiGetSol(sepadata->lpi, NULL, NULL, dualsol, NULL, NULL) );
2076 
2077  for( i = 0; i < ecaggr->nvars; ++i )
2078  facet[i] = dualsol[i];
2079 
2080  /* constant part of the facet is the last component of the dual solution */
2081  facet[ecaggr->nvars] = dualsol[nrows - 1];
2082 
2083  SCIPfreeBufferArray(scip, &dualsol);
2084  }
2085 
2086 #ifdef SCIP_DEBUG
2087  SCIPdebugMessage("facet for the transformed problem: ");
2088  for( i = 0; i < ecaggr->nvars; ++i )
2089  {
2090  SCIPdebugPrintf("%3.4e * %s + ", facet[i], SCIPvarGetName(ecaggr->vars[i]));
2091  }
2092  SCIPdebugPrintf("%3.4e\n", facet[ecaggr->nvars]);
2093 #endif
2094 
2095  /*
2096  * 4. transform the facet to original space
2097  * we now have the linear underestimator L(x) = beta^T x + beta_0, which needs to be transform to the original space
2098  * the underestimator in the original space, G(x) = alpha^T x + alpha_0, is given by G(x) = L(T(x)), where T(.) is
2099  * the transformation applied in step 2; therefore,
2100  * alpha_i = beta_i/(ub_i - lb_i)
2101  * alpha_0 = beta_0 - sum_i lb_i * beta_i/(ub_i - lb_i)
2102  */
2103 
2104  SCIPdebugMessage("facet in orig. space: ");
2105  *facetval = 0.0;
2106 
2107  for( i = 0; i < ecaggr->nvars; ++i )
2108  {
2109  SCIP_Real varlb;
2110  SCIP_Real varub;
2111 
2112  varlb = SCIPvarGetLbLocal(ecaggr->vars[i]);
2113  varub = SCIPvarGetUbLocal(ecaggr->vars[i]);
2114  assert(!SCIPisEQ(scip, varlb, varub));
2115 
2116  /* substract (\beta_i * lb_i) / (ub_i - lb_i) from current alpha_0 */
2117  facet[ecaggr->nvars] -= (facet[i] * varlb) / (varub - varlb);
2118 
2119  /* set \alpha_i := \beta_i / (ub_i - lb_i) */
2120  facet[i] = facet[i] / (varub - varlb);
2121  *facetval += facet[i] * SCIPgetSolVal(scip, sol, ecaggr->vars[i]);
2122 
2123  SCIPdebugPrintf("%3.4e * %s + ", facet[i], SCIPvarGetName(ecaggr->vars[i]));
2124  }
2125 
2126  /* add constant part to the facet value */
2127  *facetval += facet[ecaggr->nvars];
2128  SCIPdebugPrintf("%3.4e\n", facet[ecaggr->nvars]);
2129 
2130  /*
2131  * 5. adjust and check facet with the algorithm of Rikun et al.
2132  */
2133 
2134  if( checkRikun(scip, ecaggr, fvals, facet) )
2135  {
2136  SCIPdebugMessage("facet pass the check of Rikun et al.\n");
2137  *success = TRUE;
2138  }
2139 
2140  /* free allocated memory */
2141  SCIPfreeBufferArray(scip, &fvals);
2142 
2143  return SCIP_OKAY;
2144 }
2145 
2146 /*
2147  * miscellaneous methods
2148  */
2149 
2150 /** method to add a facet of the convex envelope of an edge-concave aggregation to a given cut */
2151 static
2153  SCIP* scip, /**< SCIP data structure */
2154  SCIP_SOL* sol, /**< current solution (might be NULL) */
2155  SCIP_ROW* cut, /**< current cut (modifiable) */
2156  SCIP_Real* facet, /**< coefficient of the facet (dimension nvars + 1) */
2157  SCIP_VAR** vars, /**< variables of the facet */
2158  int nvars, /**< number of variables in the facet */
2159  SCIP_Real* cutconstant, /**< pointer to update the constant part of the facet */
2160  SCIP_Real* cutactivity, /**< pointer to update the activity of the cut */
2161  SCIP_Bool* success /**< pointer to store if everything went fine */
2162  )
2163 {
2164  int i;
2165 
2166  assert(cut != NULL);
2167  assert(facet != NULL);
2168  assert(vars != NULL);
2169  assert(nvars > 0);
2170  assert(cutconstant != NULL);
2171  assert(cutactivity != NULL);
2172  assert(success != NULL);
2173 
2174  *success = TRUE;
2175 
2176  for( i = 0; i < nvars; ++i )
2177  {
2178  if( SCIPisInfinity(scip, REALABS(facet[i])) )
2179  {
2180  *success = FALSE;
2181  return SCIP_OKAY;
2182  }
2183 
2184  if( !SCIPisZero(scip, facet[i]) )
2185  {
2186  /* add only a constant if the variable has been fixed */
2187  if( SCIPvarGetLbLocal(vars[i]) == SCIPvarGetUbLocal(vars[i]) ) /*lint !e777*/
2188  {
2189  assert(SCIPisFeasEQ(scip, SCIPvarGetLbLocal(vars[i]), SCIPgetSolVal(scip, sol, vars[i])));
2190  *cutconstant += facet[i] * SCIPgetSolVal(scip, sol, vars[i]);
2191  *cutactivity += facet[i] * SCIPgetSolVal(scip, sol, vars[i]);
2192  }
2193  else
2194  {
2195  *cutactivity += facet[i] * SCIPgetSolVal(scip, sol, vars[i]);
2196  SCIP_CALL( SCIPaddVarToRow(scip, cut, vars[i], facet[i]) );
2197  }
2198  }
2199  }
2200 
2201  /* add constant part of the facet */
2202  *cutconstant += facet[nvars];
2203  *cutactivity += facet[nvars];
2204 
2205  return SCIP_OKAY;
2206 }
2207 
2208 /** method to add an linear term to a given cut */
2209 static
2211  SCIP* scip, /**< SCIP data structure */
2212  SCIP_SOL* sol, /**< current solution (might be NULL) */
2213  SCIP_ROW* cut, /**< current cut (modifiable) */
2214  SCIP_VAR* x, /**< linear variable */
2215  SCIP_Real coeff, /**< coefficient */
2216  SCIP_Real* cutconstant, /**< pointer to update the constant part of the facet */
2217  SCIP_Real* cutactivity, /**< pointer to update the activity of the cut */
2218  SCIP_Bool* success /**< pointer to store if everything went fine */
2219  )
2220 {
2221  SCIP_Real activity;
2222 
2223  assert(cut != NULL);
2224  assert(x != NULL);
2225  assert(!SCIPisZero(scip, coeff));
2226  assert(!SCIPisInfinity(scip, coeff));
2227  assert(cutconstant != NULL);
2228  assert(cutactivity != NULL);
2229  assert(success != NULL);
2230 
2231  *success = TRUE;
2232  activity = SCIPgetSolVal(scip, sol, x) * coeff;
2233 
2234  /* do not add a term if the activity is -infinity */
2235  if( SCIPisInfinity(scip, -1.0 * REALABS(activity)) )
2236  {
2237  *success = FALSE;
2238  return SCIP_OKAY;
2239  }
2240 
2241  /* add activity to the constant part if the variable has been fixed */
2242  if( SCIPvarGetLbLocal(x) == SCIPvarGetUbLocal(x) ) /*lint !e777*/
2243  {
2244  assert(SCIPisFeasEQ(scip, SCIPvarGetLbLocal(x), SCIPgetSolVal(scip, sol, x)));
2245  *cutconstant += activity;
2246  SCIPdebugMessage("add to cut: %e\n", activity);
2247  }
2248  else
2249  {
2250  SCIP_CALL( SCIPaddVarToRow(scip, cut, x, coeff) );
2251  SCIPdebugMessage("add to cut: %e * %s\n", coeff, SCIPvarGetName(x));
2252  }
2253 
2254  *cutactivity += activity;
2255 
2256  return SCIP_OKAY;
2257 }
2258 
2259 /** method to add an underestimate of a bilinear term to a given cut */
2260 static
2262  SCIP* scip, /**< SCIP data structure */
2263  SCIP_SOL* sol, /**< current solution (might be NULL) */
2264  SCIP_ROW* cut, /**< current cut (modifiable) */
2265  SCIP_VAR* x, /**< first bilinear variable */
2266  SCIP_VAR* y, /**< seconds bilinear variable */
2267  SCIP_Real coeff, /**< coefficient */
2268  SCIP_Real* cutconstant, /**< pointer to update the constant part of the facet */
2269  SCIP_Real* cutactivity, /**< pointer to update the activity of the cut */
2270  SCIP_Bool* success /**< pointer to store if everything went fine */
2271  )
2272 {
2273  SCIP_Real activity;
2274 
2275  assert(cut != NULL);
2276  assert(x != NULL);
2277  assert(y != NULL);
2278  assert(!SCIPisZero(scip, coeff));
2279  assert(cutconstant != NULL);
2280  assert(cutactivity != NULL);
2281  assert(success != NULL);
2282 
2283  *success = TRUE;
2284  activity = coeff * SCIPgetSolVal(scip, sol, x) * SCIPgetSolVal(scip, sol, y);
2285 
2286  if( SCIPisInfinity(scip, REALABS(coeff)) )
2287  {
2288  *success = FALSE;
2289  return SCIP_OKAY;
2290  }
2291 
2292  /* do not add a term if the activity is -infinity */
2293  if( SCIPisInfinity(scip, -1.0 * REALABS(activity)) )
2294  {
2295  *success = FALSE;
2296  return SCIP_OKAY;
2297  }
2298 
2299  /* quadratic case */
2300  if( x == y )
2301  {
2302  SCIP_Real refpoint;
2303  SCIP_Real lincoef;
2304  SCIP_Real linconst;
2305 
2306  lincoef = 0.0;
2307  linconst = 0.0;
2308  refpoint = SCIPgetSolVal(scip, sol, x);
2309 
2310  /* adjust the reference point */
2311  refpoint = SCIPisLT(scip, refpoint, SCIPvarGetLbLocal(x)) ? SCIPvarGetLbLocal(x) : refpoint;
2312  refpoint = SCIPisGT(scip, refpoint, SCIPvarGetUbLocal(x)) ? SCIPvarGetUbLocal(x) : refpoint;
2313  assert(SCIPisLE(scip, refpoint, SCIPvarGetUbLocal(x)) && SCIPisGE(scip, refpoint, SCIPvarGetLbLocal(x)));
2314 
2315  if( SCIPisPositive(scip, coeff) )
2316  SCIPaddSquareLinearization(scip, coeff, refpoint, SCIPvarIsIntegral(x), &lincoef, &linconst, success);
2317  else
2318  SCIPaddSquareSecant(scip, coeff, SCIPvarGetLbLocal(x), SCIPvarGetUbLocal(x), refpoint, &lincoef, &linconst, success);
2319 
2320  *cutactivity += lincoef * refpoint + linconst;
2321  *cutconstant += linconst;
2322 
2323  /* add underestimate to cut */
2324  SCIP_CALL( SCIPaddVarToRow(scip, cut, x, lincoef) );
2325 
2326  SCIPdebugMessage("add to cut: %e * %s + %e\n", lincoef, SCIPvarGetName(x), linconst);
2327  }
2328  /* bilinear case */
2329  else
2330  {
2331  SCIP_Real refpointx;
2332  SCIP_Real refpointy;
2333  SCIP_Real lincoefx;
2334  SCIP_Real lincoefy;
2335  SCIP_Real linconst;
2336 
2337  lincoefx = 0.0;
2338  lincoefy = 0.0;
2339  linconst = 0.0;
2340  refpointx = SCIPgetSolVal(scip, sol, x);
2341  refpointy = SCIPgetSolVal(scip, sol, y);
2342 
2343  /* adjust the reference points */
2344  refpointx = SCIPisLT(scip, refpointx, SCIPvarGetLbLocal(x)) ? SCIPvarGetLbLocal(x) : refpointx;
2345  refpointx = SCIPisGT(scip, refpointx, SCIPvarGetUbLocal(x)) ? SCIPvarGetUbLocal(x) : refpointx;
2346  refpointy = SCIPisLT(scip, refpointy, SCIPvarGetLbLocal(y)) ? SCIPvarGetLbLocal(y) : refpointy;
2347  refpointy = SCIPisGT(scip, refpointy, SCIPvarGetUbLocal(y)) ? SCIPvarGetUbLocal(y) : refpointy;
2348  assert(SCIPisLE(scip, refpointx, SCIPvarGetUbLocal(x)) && SCIPisGE(scip, refpointx, SCIPvarGetLbLocal(x)));
2349  assert(SCIPisLE(scip, refpointy, SCIPvarGetUbLocal(y)) && SCIPisGE(scip, refpointy, SCIPvarGetLbLocal(y)));
2350 
2352  SCIPvarGetUbLocal(y), refpointy, FALSE, &lincoefx, &lincoefy, &linconst, success);
2353 
2354  *cutactivity += lincoefx * refpointx + lincoefy * refpointy + linconst;
2355  *cutconstant += linconst;
2356 
2357  /* add underestimate to cut */
2358  SCIP_CALL( SCIPaddVarToRow(scip, cut, x, lincoefx) );
2359  SCIP_CALL( SCIPaddVarToRow(scip, cut, y, lincoefy) );
2360 
2361  SCIPdebugMessage("add to cut: %e * %s + %e * %s + %e\n", lincoefx, SCIPvarGetName(x), lincoefy,
2362  SCIPvarGetName(y), linconst);
2363  }
2364 
2365  return SCIP_OKAY;
2366 }
2367 
2368 /** method to compute and and a cut for a nonlinear row aggregation and a given solution; we compute for each edge
2369  * concave aggregation one facet; the remaining bilinear terms will be underestimated with McCormick, secants or
2370  * linearizations; constant and linear terms will be added to the cut directly
2371  */
2372 static
2374  SCIP* scip, /**< SCIP data structure */
2375  SCIP_SEPA* sepa, /**< separator */
2376  SCIP_SEPADATA* sepadata, /**< separator data */
2377  SCIP_NLROWAGGR* nlrowaggr, /**< nonlinear row aggregation */
2378  SCIP_SOL* sol, /**< current solution (might be NULL) */
2379  SCIP_Bool* separated, /**< pointer to store if we could separate the current solution */
2380  SCIP_Bool* cutoff /**< pointer to store if the current node gets cut off */
2381  )
2382 {
2383  SCIP_ROW* cut;
2384  SCIP_Real* bestfacet;
2385  SCIP_Real bestfacetval;
2386  SCIP_Real cutconstant;
2387  SCIP_Real cutactivity;
2388  int bestfacetsize;
2389  char cutname[SCIP_MAXSTRLEN];
2390  SCIP_Bool found;
2391  SCIP_Bool islocalcut;
2392  int i;
2393 
2394  assert(separated != NULL);
2395  assert(cutoff != NULL);
2396  assert(nlrowaggr->necaggr > 0);
2397  assert(nlrowaggr->nlrow != NULL);
2398  assert(SCIPnlrowIsInNLP(nlrowaggr->nlrow));
2399 
2400  *separated = FALSE;
2401  *cutoff = FALSE;
2402  islocalcut = SCIPgetDepth(scip) != 0;
2403 
2404  /* create the cut */
2405  (void) SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "ec");
2406  SCIP_CALL( SCIPcreateEmptyRowSepa(scip, &cut, sepa, cutname, -SCIPinfinity(scip), SCIPinfinity(scip), islocalcut, FALSE,
2407  sepadata->dynamiccuts) );
2408  SCIP_CALL( SCIPcacheRowExtensions(scip, cut) );
2409 
2410  /* track rhs and activity of the cut */
2411  cutconstant = nlrowaggr->constant;
2412  cutactivity = 0.0;
2413 
2414  /* allocate necessary memory */
2415  bestfacetsize = sepadata->maxaggrsize + 1;
2416  SCIP_CALL( SCIPallocBufferArray(scip, &bestfacet, bestfacetsize) );
2417 
2418 #ifdef SCIP_DEBUG
2419  SCIP_CALL( SCIPnlrowPrint(nlrowaggr->nlrow, SCIPgetMessagehdlr(scip), NULL) );
2420 
2421  SCIPdebugMessage("current solution:\n");
2422  for( i = 0; i < SCIPgetNVars(scip); ++i )
2423  {
2424  SCIP_VAR* var = SCIPgetVars(scip)[i];
2425  SCIPdebugMessage(" %s = [%e, %e] solval = %e\n", SCIPvarGetName(var), SCIPvarGetLbLocal(var),
2426  SCIPvarGetUbLocal(var), SCIPgetSolVal(scip, sol, var));
2427  }
2428 #endif
2429 
2430  /* compute a facet for each edge-concave aggregation */
2431  for( i = 0; i < nlrowaggr->necaggr; ++i )
2432  {
2433  SCIP_ECAGGR* ecaggr;
2434  SCIP_Bool success;
2435 
2436  ecaggr = nlrowaggr->ecaggr[i];
2437  assert(ecaggr != NULL);
2438 
2439  /* compute a facet of the convex envelope */
2440  SCIP_CALL( SCIPcomputeConvexEnvelopeFacet(scip, sepadata, sol, ecaggr, bestfacet, &bestfacetval, &found) );
2441 
2442  SCIPdebugMessage("found facet for edge-concave aggregation %d/%d ? %s\n", i, nlrowaggr->necaggr,
2443  found ? "yes" : "no");
2444 
2445 #ifdef SCIP_DEBUG
2446  if( found )
2447  printFacet(scip, ecaggr->vars, ecaggr->nvars, bestfacet, bestfacetval);
2448 #endif
2449 
2450  /* do not add any cut because we did not found a facet for at least one edge-concave aggregation */
2451  if( !found ) /*lint !e774*/
2452  goto TERMINATE;
2453 
2454  /* add facet to the cut and update the rhs and activity of the cut */
2455  SCIP_CALL( addFacetToCut(scip, sol, cut, bestfacet, ecaggr->vars, ecaggr->nvars, &cutconstant, &cutactivity,
2456  &success) );
2457 
2458  if( !success )
2459  goto TERMINATE;
2460  }
2461 
2462  /* compute an underestimate for each bilinear term which is not in any edge-concave aggregation */
2463  for( i = 0; i < nlrowaggr->nremterms; ++i )
2464  {
2465  SCIP_VAR* x;
2466  SCIP_VAR* y;
2467  SCIP_Bool success;
2468 
2469  x = nlrowaggr->remtermvars1[i];
2470  y = nlrowaggr->remtermvars2[i];
2471  assert(x != NULL);
2472  assert(y != NULL);
2473 
2474  SCIP_CALL( addBilinearTermToCut(scip, sol, cut, x, y, nlrowaggr->remtermcoefs[i], &cutconstant, &cutactivity,
2475  &success) );
2476 
2477  if( !success )
2478  goto TERMINATE;
2479  }
2480 
2481  /* add all linear terms to the cut */
2482  for( i = 0; i < nlrowaggr->nlinvars; ++i )
2483  {
2484  SCIP_VAR* x;
2485  SCIP_Real coef;
2486  SCIP_Bool success;
2487 
2488  x = nlrowaggr->linvars[i];
2489  assert(x != NULL);
2490 
2491  coef = nlrowaggr->lincoefs[i];
2492 
2493  SCIP_CALL( addLinearTermToCut(scip, sol, cut, x, coef, &cutconstant, &cutactivity, &success) );
2494 
2495  if( !success )
2496  goto TERMINATE;
2497  }
2498 
2499  SCIPdebugMessage("cut activity = %e rhs(nlrow) = %e\n", cutactivity, nlrowaggr->rhs);
2500 
2501  /* set rhs of the cut (substract the constant part of the cut) */
2502  SCIP_CALL( SCIPchgRowRhs(scip, cut, nlrowaggr->rhs - cutconstant) );
2503  SCIP_CALL( SCIPflushRowExtensions(scip, cut) );
2504 
2505  /* check activity of the row; this assert can fail because of numerics */
2506  /* assert(SCIPisFeasEQ(scip, cutactivity - cutconstant, SCIPgetRowSolActivity(scip, cut, sol)) ); */
2507 
2508 #ifdef SCIP_DEBUG
2509  SCIP_CALL( SCIPprintRow(scip, cut, NULL) );
2510 #endif
2511 
2512  SCIPdebugMessage("EC cut <%s>: act=%f eff=%f rank=%d range=%e\n",
2513  SCIProwGetName(cut), SCIPgetRowSolActivity(scip, cut, sol), SCIPgetCutEfficacy(scip, sol, cut),
2514  SCIProwGetRank(cut), SCIPgetRowMaxCoef(scip, cut) / SCIPgetRowMinCoef(scip, cut) );
2515 
2516  /* try to add the cut has a finite rhs, is efficacious, and does not exceed the maximum cut range */
2517  if( !SCIPisInfinity(scip, nlrowaggr->rhs - cutconstant) && SCIPisCutEfficacious(scip, sol, cut)
2518  && SCIPgetRowMaxCoef(scip, cut) / SCIPgetRowMinCoef(scip, cut) < sepadata->cutmaxrange )
2519  {
2520  /* add the cut if it is separating the given solution by at least minviolation */
2521  if( SCIPisGE(scip, cutactivity - nlrowaggr->rhs, sepadata->minviolation) )
2522  {
2523  SCIP_CALL( SCIPaddCut(scip, sol, cut, FALSE, cutoff) );
2524  *separated = TRUE;
2525  SCIPdebugMessage("added separating cut\n");
2526  }
2527 
2528  if( !(*cutoff) && !islocalcut )
2529  {
2530  SCIP_CALL( SCIPaddPoolCut(scip, cut) );
2531  SCIPdebugMessage("added cut to cut pool\n");
2532  }
2533  }
2534 
2535 TERMINATE:
2536  /* free allocated memory */
2537  SCIPfreeBufferArray(scip, &bestfacet);
2538 
2539  /* release the row */
2540  SCIP_CALL( SCIPreleaseRow(scip, &cut) );
2541 
2542  return SCIP_OKAY;
2543 }
2544 
2545 /** returns whether it is possible to compute a cut for a given nonlinear row aggregation */
2546 static
2548  SCIP* scip, /**< SCIP data structure */
2549  SCIP_SOL* sol, /**< current solution (might be NULL) */
2550  SCIP_NLROWAGGR* nlrowaggr /**< nonlinear row aggregation */
2551  )
2552 {
2553  int i;
2554 
2555  assert(scip != NULL);
2556  assert(nlrowaggr != NULL);
2557 
2558  if( !SCIPnlrowIsInNLP(nlrowaggr->nlrow) )
2559  {
2560  SCIPdebugMessage("nlrow is not in NLP anymore\n");
2561  return FALSE;
2562  }
2563 
2564  for( i = 0; i < nlrowaggr->nquadvars; ++i )
2565  {
2566  SCIP_VAR* var = nlrowaggr->quadvars[i];
2567  assert(var != NULL);
2568 
2569  /* check whether the variable has infinite bounds */
2571  || SCIPisInfinity(scip, REALABS(SCIPgetSolVal(scip, sol, var))) )
2572  {
2573  SCIPdebugMessage("nlrow aggregation contains unbounded variables\n");
2574  return FALSE;
2575  }
2576 
2577  /* check whether the variable has been fixed and is in one edge-concave aggregation */
2578  if( nlrowaggr->quadvar2aggr[i] >= 0 && SCIPisFeasEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) )
2579  {
2580  SCIPdebugMessage("nlrow aggregation contains fixed variables in an e.c. aggregation\n");
2581  return FALSE;
2582  }
2583  }
2584 
2585  return TRUE;
2586 }
2587 
2588 /** searches and tries to add edge-concave cuts */
2589 static
2591  SCIP* scip, /**< SCIP data structure */
2592  SCIP_SEPA* sepa, /**< separator */
2593  SCIP_SEPADATA* sepadata, /**< separator data */
2594  SCIP_SOL* sol, /**< current solution */
2595  SCIP_RESULT* result /**< pointer to store the result of the separation call */
2596  )
2597 {
2598  int nmaxcuts;
2599  int ncuts;
2600  int i;
2601 
2602  assert(*result == SCIP_DIDNOTRUN);
2603 
2604  SCIPdebugMessage("separate cuts...\n");
2605 
2606  /* skip if there are no nonlinear row aggregations */
2607  if( sepadata->nnlrowaggrs == 0 )
2608  {
2609  SCIPdebugMessage("no aggregations exists -> skip call\n");
2610  return SCIP_OKAY;
2611  }
2612 
2613  /* get the maximal number of cuts allowed in a separation round */
2614  nmaxcuts = SCIPgetDepth(scip) == 0 ? sepadata->maxsepacutsroot : sepadata->maxsepacuts;
2615  ncuts = 0;
2616 
2617  /* try to compute cuts for each nonlinear row independently */
2618  for( i = 0; i < sepadata->nnlrowaggrs && ncuts < nmaxcuts; ++i )
2619  {
2620  SCIP_NLROWAGGR* nlrowaggr;
2621  SCIP_Bool separated;
2622  SCIP_Bool cutoff;
2623 
2624  nlrowaggr = sepadata->nlrowaggrs[i];
2625  assert(nlrowaggr != NULL);
2626 
2627  /* skip nonlinear aggregations for which it is obviously not possible to compute a cut */
2628  if( !isPossibleToComputeCut(scip, sol, nlrowaggr) )
2629  return SCIP_OKAY;
2630 
2631  *result = (*result == SCIP_DIDNOTRUN) ? SCIP_DIDNOTFIND : *result;
2632 
2633  SCIPdebugMessage("try to compute a cut for nonlinear row aggregation %d\n", i);
2634 
2635  /* compute and add cut */
2636  SCIP_CALL( computeCut(scip, sepa, sepadata, nlrowaggr, sol, &separated, &cutoff) );
2637  SCIPdebugMessage("found a cut: %s cutoff: %s\n", separated ? "yes" : "no", cutoff ? "yes" : "no");
2638 
2639  /* stop if the current node gets cut off */
2640  if( cutoff )
2641  {
2642  assert(separated);
2643  *result = SCIP_CUTOFF;
2644  return SCIP_OKAY;
2645  }
2646 
2647  /* do not compute more cuts if we already separated the given solution */
2648  if( separated )
2649  {
2650  assert(!cutoff);
2651  *result = SCIP_SEPARATED;
2652  ++ncuts;
2653  }
2654  }
2655 
2656  return SCIP_OKAY;
2657 }
2658 
2659 /*
2660  * Callback methods of separator
2661  */
2662 
2663 /** copy method for separator plugins (called when SCIP copies plugins) */
2664 static
2665 SCIP_DECL_SEPACOPY(sepaCopyEccuts)
2666 { /*lint --e{715}*/
2667  assert(scip != NULL);
2668  assert(sepa != NULL);
2669  assert(strcmp(SCIPsepaGetName(sepa), SEPA_NAME) == 0);
2670 
2671  /* call inclusion method of constraint handler */
2673 
2674  return SCIP_OKAY;
2675 }
2676 
2677 /** destructor of separator to free user data (called when SCIP is exiting) */
2678 static
2679 SCIP_DECL_SEPAFREE(sepaFreeEccuts)
2680 { /*lint --e{715}*/
2681  SCIP_SEPADATA* sepadata;
2682 
2683  sepadata = SCIPsepaGetData(sepa);
2684  assert(sepadata != NULL);
2685 
2686  SCIP_CALL( sepadataFree(scip, &sepadata) );
2687  SCIPsepaSetData(sepa, NULL);
2688 
2689  return SCIP_OKAY;
2690 }
2691 
2692 /** solving process deinitialization method of separator (called before branch and bound process data is freed) */
2693 static
2694 SCIP_DECL_SEPAEXITSOL(sepaExitsolEccuts)
2695 { /*lint --e{715}*/
2696  SCIP_SEPADATA* sepadata;
2697 
2698  sepadata = SCIPsepaGetData(sepa);
2699  assert(sepadata != NULL);
2700 
2701  /* print statistics */
2702 #ifdef SCIP_STATISTIC
2703  SCIPstatisticMessage("rhs-AGGR %d\n", sepadata->nrhsnlrowaggrs);
2704  SCIPstatisticMessage("lhs-AGGR %d\n", sepadata->nlhsnlrowaggrs);
2705  SCIPstatisticMessage("aggr. search time = %f\n", sepadata->aggrsearchtime);
2706 #endif
2707 
2708  /* free nonlinear row aggregations */
2709  SCIP_CALL( sepadataFreeNlrows(scip, sepadata) );
2710 
2711  /* mark that we should search again for nonlinear row aggregations */
2712  sepadata->searchedforaggr = FALSE;
2713 
2714  SCIPdebugMessage("exitsol\n");
2715 
2716  return SCIP_OKAY;
2717 }
2718 
2719 /** LP solution separation method of separator */
2720 static
2721 SCIP_DECL_SEPAEXECLP(sepaExeclpEccuts)
2722 { /*lint --e{715}*/
2723  SCIP_SEPADATA* sepadata;
2724  int depth;
2725  int ncalls;
2726 
2727  sepadata = SCIPsepaGetData(sepa);
2728  assert(sepadata != NULL);
2729 
2730  *result = SCIP_DIDNOTRUN;
2731 
2732  /* check min- and maximal aggregation size */
2733  if( sepadata->maxaggrsize < sepadata->minaggrsize )
2734  return SCIP_PARAMETERWRONGVAL;
2735 
2736  /* only call separator, if we are not close to terminating */
2737  if( SCIPisStopped(scip) )
2738  return SCIP_OKAY;
2739 
2740  /* skip if the LP is not constructed yet */
2741  if( !SCIPisNLPConstructed(scip) )
2742  {
2743  SCIPdebugMessage("Skip since NLP is not constructed yet.\n");
2744  return SCIP_OKAY;
2745  }
2746 
2747  depth = SCIPgetDepth(scip);
2748 
2749  /* only call separator up to a maximum depth */
2750  if ( sepadata->maxdepth >= 0 && depth > sepadata->maxdepth )
2751  return SCIP_OKAY;
2752 
2753  /* only call separator a given number of times at each node */
2754  ncalls = SCIPsepaGetNCallsAtNode(sepa);
2755  if ( (depth == 0 && sepadata->maxroundsroot >= 0 && ncalls >= sepadata->maxroundsroot)
2756  || (depth > 0 && sepadata->maxrounds >= 0 && ncalls >= sepadata->maxrounds) )
2757  return SCIP_OKAY;
2758 
2759  /* search for nonlinear row aggregations */
2760  if( !sepadata->searchedforaggr )
2761  {
2762  int i;
2763 
2764  SCIPstatistic( sepadata->aggrsearchtime -= SCIPgetTotalTime(scip) );
2765 
2766  SCIPdebugMessage("search for nonlinear row aggregations\n");
2767  for( i = 0; i < SCIPgetNNLPNlRows(scip); ++i )
2768  {
2769  SCIP_NLROW* nlrow = SCIPgetNLPNlRows(scip)[i];
2770  SCIP_CALL( findAndStoreEcAggregations(scip, sepadata, nlrow, NULL) );
2771  }
2772  sepadata->searchedforaggr = TRUE;
2773 
2774  SCIPstatistic( sepadata->aggrsearchtime += SCIPgetTotalTime(scip) );
2775  }
2776 
2777  /* search for edge-concave cuts */
2778  SCIP_CALL( separateCuts(scip, sepa, sepadata, NULL, result) );
2779 
2780  return SCIP_OKAY;
2781 }
2782 
2783 /*
2784  * separator specific interface methods
2785  */
2786 
2787 /** creates the edge concave separator and includes it in SCIP */
2789  SCIP* scip /**< SCIP data structure */
2790  )
2791 {
2792  SCIP_SEPADATA* sepadata;
2793  SCIP_SEPA* sepa;
2794 
2795  /* create eccuts separator data */
2796  SCIP_CALL( sepadataCreate(scip, &sepadata) );
2797 
2798  /* include separator */
2800  SEPA_USESSUBSCIP, SEPA_DELAY, sepaExeclpEccuts, NULL, sepadata) );
2801 
2802  assert(sepa != NULL);
2803 
2804  /* set non fundamental callbacks via setter functions */
2805  SCIP_CALL( SCIPsetSepaCopy(scip, sepa, sepaCopyEccuts) );
2806  SCIP_CALL( SCIPsetSepaFree(scip, sepa, sepaFreeEccuts) );
2807  SCIP_CALL( SCIPsetSepaExitsol(scip, sepa, sepaExitsolEccuts) );
2808 
2809  /* add eccuts separator parameters */
2811  "separating/" SEPA_NAME "/dynamiccuts",
2812  "should generated cuts be removed from the LP if they are no longer tight?",
2813  &sepadata->dynamiccuts, FALSE, DEFAULT_DYNAMICCUTS, NULL, NULL) );
2814 
2815  SCIP_CALL( SCIPaddIntParam(scip,
2816  "separating/" SEPA_NAME "/maxrounds",
2817  "maximal number of eccuts separation rounds per node (-1: unlimited)",
2818  &sepadata->maxrounds, FALSE, DEFAULT_MAXROUNDS, -1, INT_MAX, NULL, NULL) );
2819 
2820  SCIP_CALL( SCIPaddIntParam(scip,
2821  "separating/" SEPA_NAME "/maxroundsroot",
2822  "maximal number of eccuts separation rounds in the root node (-1: unlimited)",
2823  &sepadata->maxroundsroot, FALSE, DEFAULT_MAXROUNDSROOT, -1, INT_MAX, NULL, NULL) );
2824 
2825  SCIP_CALL( SCIPaddIntParam(scip,
2826  "separating/" SEPA_NAME "/maxdepth",
2827  "maximal depth at which the separator is applied (-1: unlimited)",
2828  &sepadata->maxdepth, FALSE, DEFAULT_MAXDEPTH, -1, INT_MAX, NULL, NULL) );
2829 
2830  SCIP_CALL( SCIPaddIntParam(scip,
2831  "separating/" SEPA_NAME "/maxsepacuts",
2832  "maximal number of edge-concave cuts separated per separation round",
2833  &sepadata->maxsepacuts, FALSE, DEFAULT_MAXSEPACUTS, 0, INT_MAX, NULL, NULL) );
2834 
2835  SCIP_CALL( SCIPaddIntParam(scip,
2836  "separating/" SEPA_NAME "/maxsepacutsroot",
2837  "maximal number of edge-concave cuts separated per separation round in the root node",
2838  &sepadata->maxsepacutsroot, FALSE, DEFAULT_MAXSEPACUTSROOT, 0, INT_MAX, NULL, NULL) );
2839 
2840  SCIP_CALL( SCIPaddRealParam(scip, "separating/" SEPA_NAME "/cutmaxrange",
2841  "maximal coef. range of a cut (max coef. divided by min coef.) in order to be added to LP relaxation",
2842  &sepadata->cutmaxrange, FALSE, DEFAULT_CUTMAXRANGE, 0.0, SCIPinfinity(scip), NULL, NULL) );
2843 
2844  SCIP_CALL( SCIPaddRealParam(scip, "separating/" SEPA_NAME "/minviolation",
2845  "minimal violation of an edge-concave cut to be separated",
2846  &sepadata->minviolation, FALSE, DEFAULT_MINVIOLATION, 0.0, 0.5, NULL, NULL) );
2847 
2848  SCIP_CALL( SCIPaddIntParam(scip,
2849  "separating/" SEPA_NAME "/minaggrsize",
2850  "search for edge-concave aggregations of at least this size",
2851  &sepadata->minaggrsize, TRUE, DEFAULT_MINAGGRSIZE, 3, 5, NULL, NULL) );
2852 
2853  SCIP_CALL( SCIPaddIntParam(scip,
2854  "separating/" SEPA_NAME "/maxaggrsize",
2855  "search for edge-concave aggregations of at most this size",
2856  &sepadata->maxaggrsize, TRUE, DEFAULT_MAXAGGRSIZE, 3, 5, NULL, NULL) );
2857 
2858  SCIP_CALL( SCIPaddIntParam(scip,
2859  "separating/" SEPA_NAME "/maxbilinterms",
2860  "maximum number of bilinear terms allowed to be in a quadratic constraint",
2861  &sepadata->maxbilinterms, TRUE, DEFAULT_MAXBILINTERMS, 0, INT_MAX, NULL, NULL) );
2862 
2863  SCIP_CALL( SCIPaddIntParam(scip,
2864  "separating/" SEPA_NAME "/maxstallrounds",
2865  "maximum number of unsuccessful rounds in the edge-concave aggregation search",
2866  &sepadata->maxstallrounds, TRUE, DEFAULT_MAXSTALLROUNDS, 0, INT_MAX, NULL, NULL) );
2867 
2868  return SCIP_OKAY;
2869 }
enum SCIP_Result SCIP_RESULT
Definition: type_result.h:51
SCIP_Bool SCIPisEQ(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
Definition: scip.c:41572
#define USEDUALSIMPLEX
Definition: sepa_eccuts.c:68
SCIP_RETCODE SCIPcreateConsBasicLinear(SCIP *scip, SCIP_CONS **cons, const char *name, int nvars, SCIP_VAR **vars, SCIP_Real *vals, SCIP_Real lhs, SCIP_Real rhs)
SCIP_Real * remtermcoefs
Definition: sepa_eccuts.c:113
static SCIP_RETCODE separateCuts(SCIP *scip, SCIP_SEPA *sepa, SCIP_SEPADATA *sepadata, SCIP_SOL *sol, SCIP_RESULT *result)
Definition: sepa_eccuts.c:2592
SCIP_Bool SCIPisZero(SCIP *scip, SCIP_Real val)
Definition: scip.c:41685
int * quadvar2aggr
Definition: sepa_eccuts.c:107
SCIP_Bool rhsaggr
Definition: sepa_eccuts.c:96
int SCIPgetNVars(SCIP *scip)
Definition: scip.c:10698
#define SCIPallocMemory(scip, ptr)
Definition: scip.h:20526
enum TCLIQUE_Status TCLIQUE_STATUS
Definition: tclique.h:57
SCIP_RETCODE SCIPnlrowPrint(SCIP_NLROW *nlrow, SCIP_MESSAGEHDLR *messagehdlr, FILE *file)
Definition: nlp.c:2279
const char * SCIPvarGetName(SCIP_VAR *var)
Definition: var.c:16443
void tcliqueFree(TCLIQUE_GRAPH **tcliquegraph)
SCIP_Real SCIPgetRowSolActivity(SCIP *scip, SCIP_ROW *row, SCIP_SOL *sol)
Definition: scip.c:28285
static SCIP_RETCODE createMIP(SCIP *scip, SCIP *subscip, SCIP_SEPADATA *sepadata, SCIP_NLROW *nlrow, SCIP_Bool rhsaggr, SCIP_VAR **forwardarcs, SCIP_VAR **backwardarcs, SCIP_Real *nodeweights, int *nedges)
Definition: sepa_eccuts.c:769
SCIP_RETCODE SCIPlpiSolveDual(SCIP_LPI *lpi)
Definition: lpi_clp.cpp:1705
static SCIP_RETCODE addFacetToCut(SCIP *scip, SCIP_SOL *sol, SCIP_ROW *cut, SCIP_Real *facet, SCIP_VAR **vars, int nvars, SCIP_Real *cutconstant, SCIP_Real *cutactivity, SCIP_Bool *success)
Definition: sepa_eccuts.c:2154
SCIP_STATUS SCIPgetStatus(SCIP *scip)
Definition: scip.c:908
int SCIPnlrowGetNQuadElems(SCIP_NLROW *nlrow)
Definition: nlp.c:3312
SCIP_Bool SCIPisInfinity(SCIP *scip, SCIP_Real val)
Definition: scip.c:41648
TCLIQUE_Bool tcliqueCreate(TCLIQUE_GRAPH **tcliquegraph)
int nremterms
Definition: sepa_eccuts.c:114
struct TCLIQUE_Graph TCLIQUE_GRAPH
Definition: tclique.h:38
SCIP_Real SCIPgetRowMinCoef(SCIP *scip, SCIP_ROW *row)
Definition: scip.c:28045
SCIP_RETCODE SCIPaddVar(SCIP *scip, SCIP_VAR *var)
Definition: scip.c:10378
SCIP_Real rhs
Definition: sepa_eccuts.c:116
TCLIQUE_Bool tcliqueAddNode(TCLIQUE_GRAPH *tcliquegraph, int node, TCLIQUE_WEIGHT weight)
#define SEPA_DELAY
Definition: sepa_eccuts.c:41
#define SEPA_DESC
Definition: sepa_eccuts.c:36
SCIP_VAR ** remtermvars1
Definition: sepa_eccuts.c:111
SCIP_VAR ** SCIPgetVars(SCIP *scip)
Definition: scip.c:10653
#define SCIP_MAXSTRLEN
Definition: def.h:201
static SCIP_DECL_SEPACOPY(sepaCopyEccuts)
Definition: sepa_eccuts.c:2667
#define NULL
Definition: lpi_spx.cpp:130
SCIP_Real SCIPnlrowGetConstant(SCIP_NLROW *nlrow)
Definition: nlp.c:3225
SCIP_Real SCIPvarGetLbLocal(SCIP_VAR *var)
Definition: var.c:17113
#define CLIQUE_MINWEIGHT
Definition: sepa_eccuts.c:46
SCIP_Bool SCIPisStopped(SCIP *scip)
Definition: scip.c:1125
static SCIP_DECL_SEPAEXITSOL(sepaExitsolEccuts)
Definition: sepa_eccuts.c:2696
static SCIP_RETCODE sepadataFreeNlrows(SCIP *scip, SCIP_SEPADATA *sepadata)
Definition: sepa_eccuts.c:650
#define SEPA_PRIORITY
Definition: sepa_eccuts.c:37
SCIP_Real * termcoefs
Definition: sepa_eccuts.c:85
#define CLIQUE_MAXNTREENODES
Definition: sepa_eccuts.c:47
SCIP_Real SCIPvarGetUbGlobal(SCIP_VAR *var)
Definition: var.c:17067
SCIP_Bool SCIPisCutEfficacious(SCIP *scip, SCIP_SOL *sol, SCIP_ROW *cut)
Definition: scip.c:30859
static SCIP_RETCODE searchEcAggrWithCliques(SCIP *scip, TCLIQUE_GRAPH *graph, SCIP_SEPADATA *sepadata, SCIP_NLROW *nlrow, int *quadvar2aggr, int nfoundsofar, SCIP_Bool rhsaggr, SCIP_Bool *foundaggr, SCIP_Bool *foundclique)
Definition: sepa_eccuts.c:1167
#define SCIPfreeMemoryArray(scip, ptr)
Definition: scip.h:20544
SCIP_RETCODE SCIPlpiAddCols(SCIP_LPI *lpi, int ncols, const SCIP_Real *obj, const SCIP_Real *lb, const SCIP_Real *ub, char **colnames, int nnonz, const int *beg, const int *ind, const SCIP_Real *val)
Definition: lpi_clp.cpp:638
static SCIP_RETCODE SCIPcomputeConvexEnvelopeFacet(SCIP *scip, SCIP_SEPADATA *sepadata, SCIP_SOL *sol, SCIP_ECAGGR *ecaggr, SCIP_Real *facet, SCIP_Real *facetval, SCIP_Bool *success)
Definition: sepa_eccuts.c:1946
static SCIP_RETCODE searchEcAggrWithMIP(SCIP *subscip, SCIP_Real timelimit, int nedges, SCIP_Bool *aggrleft, SCIP_Bool *found)
Definition: sepa_eccuts.c:1016
void SCIPaddSquareLinearization(SCIP *scip, SCIP_Real sqrcoef, SCIP_Real refpoint, SCIP_Bool isint, SCIP_Real *lincoef, SCIP_Real *linconstant, SCIP_Bool *success)
Definition: scip.c:30454
#define SCIPallocMemoryArray(scip, ptr, num)
Definition: scip.h:20528
SCIP_RETCODE SCIPcreateVarBasic(SCIP *scip, SCIP_VAR **var, const char *name, SCIP_Real lb, SCIP_Real ub, SCIP_Real obj, SCIP_VARTYPE vartype)
Definition: scip.c:15817
#define FALSE
Definition: def.h:56
SCIP_Real SCIPgetSolvingTime(SCIP *scip)
Definition: scip.c:41009
SCIP_RETCODE SCIPhashmapCreate(SCIP_HASHMAP **hashmap, BMS_BLKMEM *blkmem, int mapsize)
Definition: misc.c:2057
SCIP_RETCODE SCIPsetLongintParam(SCIP *scip, const char *name, SCIP_Longint value)
Definition: scip.c:4046
SCIP_NLROW * nlrow
Definition: sepa_eccuts.c:95
#define CLIQUE_MAXFIRSTNODEWEIGHT
Definition: sepa_eccuts.c:43
SCIP_ECAGGR ** ecaggr
Definition: sepa_eccuts.c:99
int SCIPsnprintf(char *t, int len, const char *s,...)
Definition: misc.c:8174
#define TRUE
Definition: def.h:55
enum SCIP_Retcode SCIP_RETCODE
Definition: type_retcode.h:53
SCIP_RETCODE SCIPlpiFree(SCIP_LPI **lpi)
Definition: lpi_clp.cpp:538
#define SCIPstatisticMessage
Definition: pub_message.h:104
#define SCIP_CALL(x)
Definition: def.h:266
void SCIPaddBilinMcCormick(SCIP *scip, SCIP_Real bilincoef, SCIP_Real lbx, SCIP_Real ubx, SCIP_Real refpointx, SCIP_Real lby, SCIP_Real uby, SCIP_Real refpointy, SCIP_Bool overestimate, SCIP_Real *lincoefx, SCIP_Real *lincoefy, SCIP_Real *linconstant, SCIP_Bool *success)
Definition: scip.c:30619
SCIP_RETCODE SCIPsetRealParam(SCIP *scip, const char *name, SCIP_Real value)
Definition: scip.c:4109
static SCIP_RETCODE sepadataFree(SCIP *scip, SCIP_SEPADATA **sepadata)
Definition: sepa_eccuts.c:679
SCIP_VAR ** remtermvars2
Definition: sepa_eccuts.c:112
static SCIP_RETCODE createTcliqueGraph(SCIP *scip, SCIP_NLROW *nlrow, TCLIQUE_GRAPH **graph, SCIP_Real *nodeweights)
Definition: sepa_eccuts.c:1082
edge concave cut separator
SCIP_RETCODE SCIPfreeTransform(SCIP *scip)
Definition: scip.c:15373
SCIP_VAR ** quadvars
Definition: sepa_eccuts.c:106
#define SCIPdebugMessage
Definition: pub_message.h:77
SCIP_Real SCIPgetSolVal(SCIP *scip, SCIP_SOL *sol, SCIP_VAR *var)
Definition: scip.c:34983
#define BMSclearMemoryArray(ptr, num)
Definition: memory.h:85
tclique user interface
SCIP_RETCODE SCIPchgRowRhs(SCIP *scip, SCIP_ROW *row, SCIP_Real rhs)
Definition: scip.c:27783
SCIP_RETCODE SCIPaddCut(SCIP *scip, SCIP_SOL *sol, SCIP_ROW *cut, SCIP_Bool forcecut, SCIP_Bool *infeasible)
Definition: scip.c:30967
SCIP_RETCODE SCIPsetSepaFree(SCIP *scip, SCIP_SEPA *sepa, SCIP_DECL_SEPAFREE((*sepafree)))
Definition: scip.c:6718
SCIP_SEPADATA * SCIPsepaGetData(SCIP_SEPA *sepa)
Definition: sepa.c:544
SCIP_RETCODE SCIPreleaseCons(SCIP *scip, SCIP_CONS **cons)
Definition: scip.c:24949
const char * SCIPsepaGetName(SCIP_SEPA *sepa)
Definition: sepa.c:633
SCIP_RETCODE SCIPlpiGetNCols(SCIP_LPI *lpi, int *ncols)
Definition: lpi_clp.cpp:1273
SCIP_Real SCIPgetRowMaxCoef(SCIP *scip, SCIP_ROW *row)
Definition: scip.c:28063
static SCIP_RETCODE ecaggrCreateEmpty(SCIP *scip, SCIP_ECAGGR **ecaggr, int nquadvars, int nquadterms)
Definition: sepa_eccuts.c:161
int SCIPgetNSols(SCIP *scip)
Definition: scip.c:35668
SCIP_RETCODE SCIPaddBoolParam(SCIP *scip, const char *name, const char *desc, SCIP_Bool *valueptr, SCIP_Bool isadvanced, SCIP_Bool defaultvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
Definition: scip.c:3547
#define DEFAULT_MAXSTALLROUNDS
Definition: sepa_eccuts.c:63
SCIP_Bool SCIPhashmapExists(SCIP_HASHMAP *hashmap, void *origin)
Definition: misc.c:2159
SCIP_RETCODE SCIPaddIntParam(SCIP *scip, const char *name, const char *desc, int *valueptr, SCIP_Bool isadvanced, int defaultvalue, int minvalue, int maxvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
Definition: scip.c:3573
SCIP_RETCODE SCIPcreateProbBasic(SCIP *scip, const char *name)
Definition: scip.c:9077
SCIP_Bool SCIPvarIsIntegral(SCIP_VAR *var)
Definition: var.c:16634
static SCIP_RETCODE ecaggrFree(SCIP *scip, SCIP_ECAGGR **ecaggr)
Definition: sepa_eccuts.c:189
int SCIPgetNNLPNlRows(SCIP *scip)
Definition: scip.c:28636
int * termvars1
Definition: sepa_eccuts.c:86
static SCIP_Bool isPossibleToComputeCut(SCIP *scip, SCIP_SOL *sol, SCIP_NLROWAGGR *nlrowaggr)
Definition: sepa_eccuts.c:2549
#define SEPA_NAME
Definition: sepa_eccuts.c:35
SCIP_Real coef
Definition: type_expr.h:102
#define SCIPfreeMemory(scip, ptr)
Definition: scip.h:20542
SCIP_Bool SCIPisLT(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
Definition: scip.c:41585
SCIP_RETCODE SCIPlpiGetNRows(SCIP_LPI *lpi, int *nrows)
Definition: lpi_clp.cpp:1255
int nterms
Definition: sepa_eccuts.c:88
SCIP_Bool SCIPisNegative(SCIP *scip, SCIP_Real val)
Definition: scip.c:41709
TCLIQUE_Bool tcliqueAddEdge(TCLIQUE_GRAPH *tcliquegraph, int node1, int node2)
#define SCIPerrorMessage
Definition: pub_message.h:45
#define SCIPdebugPrintf
Definition: pub_message.h:80
SCIP_VAR ** vars
Definition: sepa_eccuts.c:82
void tcliqueChangeWeight(TCLIQUE_GRAPH *tcliquegraph, int node, TCLIQUE_WEIGHT weight)
int SCIProwGetRank(SCIP_ROW *row)
Definition: lp.c:19004
int SCIPcalcHashtableSize(int minsize)
Definition: misc.c:1157
SCIP_Bool SCIPisNLPConstructed(SCIP *scip)
Definition: scip.c:28407
#define SEPA_FREQ
Definition: sepa_eccuts.c:38
SCIP_Bool SCIPisLE(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
Definition: scip.c:41598
SCIP_RETCODE SCIPaddPoolCut(SCIP *scip, SCIP_ROW *row)
Definition: scip.c:31062
BMS_BLKMEM * SCIPblkmem(SCIP *scip)
Definition: scip.c:41353
SCIP_Bool SCIPisFeasEQ(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
Definition: scip.c:41907
static SCIP_Real phi(SCIP *scip, SCIP_Real val, SCIP_Real lb, SCIP_Real ub)
Definition: sepa_eccuts.c:746
static SCIP_RETCODE sepadataAddNlrowaggr(SCIP *scip, SCIP_SEPADATA *sepadata, SCIP_NLROWAGGR *nlrowaggr)
Definition: sepa_eccuts.c:705
static SCIP_RETCODE storeAggrFromMIP(SCIP *subscip, SCIP_NLROW *nlrow, SCIP_VAR **forwardarcs, SCIP_VAR **backwardarcs, int *quadvar2aggr, int nfoundsofar)
Definition: sepa_eccuts.c:970
internal methods for NLP management
void SCIPhashmapFree(SCIP_HASHMAP **hashmap)
Definition: misc.c:2075
SCIP_RETCODE SCIPlpiChgSides(SCIP_LPI *lpi, int nrows, const int *ind, const SCIP_Real *lhs, const SCIP_Real *rhs)
Definition: lpi_clp.cpp:1007
SCIP_Real constant
Definition: sepa_eccuts.c:117
static SCIP_RETCODE addBilinearTermToCut(SCIP *scip, SCIP_SOL *sol, SCIP_ROW *cut, SCIP_VAR *x, SCIP_VAR *y, SCIP_Real coeff, SCIP_Real *cutconstant, SCIP_Real *cutactivity, SCIP_Bool *success)
Definition: sepa_eccuts.c:2263
SCIP_Bool SCIPnlrowIsInNLP(SCIP_NLROW *nlrow)
Definition: nlp.c:3403
void tcliqueMaxClique(TCLIQUE_GETNNODES((*getnnodes)), TCLIQUE_GETWEIGHTS((*getweights)), TCLIQUE_ISEDGE((*isedge)), TCLIQUE_SELECTADJNODES((*selectadjnodes)), TCLIQUE_GRAPH *tcliquegraph, TCLIQUE_NEWSOL((*newsol)), TCLIQUE_DATA *tcliquedata, int *maxcliquenodes, int *nmaxcliquenodes, TCLIQUE_WEIGHT *maxcliqueweight, TCLIQUE_WEIGHT maxfirstnodeweight, TCLIQUE_WEIGHT minweight, int maxntreenodes, int backtrackfreq, int maxnzeroextensions, int fixednode, int *ntreenodes, TCLIQUE_STATUS *status)
void SCIPaddSquareSecant(SCIP *scip, SCIP_Real sqrcoef, SCIP_Real lb, SCIP_Real ub, SCIP_Real refpoint, SCIP_Real *lincoef, SCIP_Real *linconstant, SCIP_Bool *success)
Definition: scip.c:30522
#define DEFAULT_MAXAGGRSIZE
Definition: sepa_eccuts.c:61
SCIP_Real SCIPinfinity(SCIP *scip)
Definition: scip.c:41637
#define DEFAULT_CUTMAXRANGE
Definition: sepa_eccuts.c:56
SCIP_RETCODE SCIPfree(SCIP **scip)
Definition: scip.c:766
const char * SCIProwGetName(SCIP_ROW *row)
Definition: lp.c:18974
SCIP_RETCODE SCIPcreateEmptyRowSepa(SCIP *scip, SCIP_ROW **row, SCIP_SEPA *sepa, const char *name, SCIP_Real lhs, SCIP_Real rhs, SCIP_Bool local, SCIP_Bool modifiable, SCIP_Bool removable)
Definition: scip.c:27629
int nlinvars
Definition: sepa_eccuts.c:104
SCIP_Real * lincoefs
Definition: sepa_eccuts.c:103
#define DEFAULT_MAXDEPTH
Definition: sepa_eccuts.c:53
SCIP_RETCODE SCIPreleaseVar(SCIP *scip, SCIP_VAR **var)
Definition: scip.c:16884
SCIP_RETCODE SCIPlpiGetSol(SCIP_LPI *lpi, SCIP_Real *objval, SCIP_Real *primsol, SCIP_Real *dualsol, SCIP_Real *activity, SCIP_Real *redcost)
Definition: lpi_clp.cpp:2652
#define DEFAULT_MINVIOLATION
Definition: sepa_eccuts.c:59
#define DEFAULT_MAXSEPACUTS
Definition: sepa_eccuts.c:54
SCIP_Bool SCIPisGT(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
Definition: scip.c:41611
SCIP_Real SCIPvarGetUbLocal(SCIP_VAR *var)
Definition: var.c:17123
SCIP_Real SCIPnlrowGetRhs(SCIP_NLROW *nlrow)
Definition: nlp.c:3373
SCIP_NLROW ** SCIPgetNLPNlRows(SCIP *scip)
Definition: scip.c:28614
void SCIPsepaSetData(SCIP_SEPA *sepa, SCIP_SEPADATA *sepadata)
Definition: sepa.c:554
SCIP_RETCODE SCIPchgVarUb(SCIP *scip, SCIP_VAR *var, SCIP_Real newbound)
Definition: scip.c:19828
int SCIPnlrowGetNLinearVars(SCIP_NLROW *nlrow)
Definition: nlp.c:3235
static SCIP_RETCODE nlrowaggrAddRemBilinTerm(SCIP *scip, SCIP_NLROWAGGR *nlrowaggr, SCIP_VAR *x, SCIP_VAR *y, SCIP_Real coef)
Definition: sepa_eccuts.c:356
int SCIPvarGetIndex(SCIP_VAR *var)
Definition: var.c:16740
#define DEFAULT_MAXSEPACUTSROOT
Definition: sepa_eccuts.c:55
#define SCIP_Bool
Definition: def.h:53
SCIP_RETCODE SCIPincludeDefaultPlugins(SCIP *scip)
SCIP_VAR ** SCIPnlrowGetLinearVars(SCIP_NLROW *nlrow)
Definition: nlp.c:3245
SCIP_RETCODE SCIPsetIntParam(SCIP *scip, const char *name, int value)
Definition: scip.c:4001
static SCIP_RETCODE nlrowaggrStoreQuadraticVars(SCIP *scip, SCIP_NLROWAGGR *nlrowaggr, SCIP_VAR **quadvars, int nquadvars)
Definition: sepa_eccuts.c:335
SCIP_MESSAGEHDLR * SCIPgetMessagehdlr(SCIP *scip)
Definition: scip.c:1216
int nquadvars
Definition: struct_nlp.h:79
SCIP_RETCODE SCIPlpiChgObj(SCIP_LPI *lpi, int ncols, int *ind, SCIP_Real *obj)
Definition: lpi_clp.cpp:1078
SCIP_RETCODE SCIPsetSepaCopy(SCIP *scip, SCIP_SEPA *sepa, SCIP_DECL_SEPACOPY((*sepacopy)))
Definition: scip.c:6702
SCIP_RETCODE SCIPsetSepaExitsol(SCIP *scip, SCIP_SEPA *sepa, SCIP_DECL_SEPAEXITSOL((*sepaexitsol)))
Definition: scip.c:6782
SCIP_VAR ** linvars
Definition: sepa_eccuts.c:102
static SCIP_RETCODE searchEcAggr(SCIP *scip, SCIP_SEPADATA *sepadata, SCIP_NLROW *nlrow, SCIP_SOL *sol, SCIP_Bool rhsaggr, int *quadvar2aggr, int *nfound)
Definition: sepa_eccuts.c:1297
SCIP_Real * SCIPnlrowGetLinearCoefs(SCIP_NLROW *nlrow)
Definition: nlp.c:3255
SCIP_RETCODE SCIPcacheRowExtensions(SCIP *scip, SCIP_ROW *row)
Definition: scip.c:27811
#define MAX(x, y)
Definition: tclique_def.h:75
static SCIP_Real transformValue(SCIP *scip, SCIP_Real lb, SCIP_Real ub, SCIP_Real val)
Definition: sepa_eccuts.c:1901
int SCIPnlrowGetNQuadVars(SCIP_NLROW *nlrow)
Definition: nlp.c:3265
#define SUBSCIP_NODELIMIT
Definition: sepa_eccuts.c:65
SCIP_Real SCIPgetTotalTime(SCIP *scip)
Definition: scip.c:40982
#define DEFAULT_MAXBILINTERMS
Definition: sepa_eccuts.c:62
int nquadvars
Definition: sepa_eccuts.c:109
SCIP_RETCODE SCIPincludeSepaEccuts(SCIP *scip)
Definition: sepa_eccuts.c:2790
SCIP_RETCODE SCIPlpiSolvePrimal(SCIP_LPI *lpi)
Definition: lpi_clp.cpp:1632
#define SCIPreallocMemoryArray(scip, ptr, newnum)
Definition: scip.h:20534
#define BMScopyMemoryArray(ptr, source, num)
Definition: memory.h:89
static SCIP_RETCODE nlrowaggrCreate(SCIP *scip, SCIP_NLROW *nlrow, SCIP_NLROWAGGR **nlrowaggr, int *quadvar2aggr, int nfound, SCIP_Bool rhsaggr)
Definition: sepa_eccuts.c:379
int nvars
Definition: sepa_eccuts.c:83
SCIP_Real SCIPnlrowGetLhs(SCIP_NLROW *nlrow)
Definition: nlp.c:3363
static SCIP_RETCODE isCandidate(SCIP *scip, SCIP_SEPADATA *sepadata, SCIP_NLROW *nlrow, SCIP_Bool *rhscandidate, SCIP_Bool *lhscandidate)
Definition: sepa_eccuts.c:1483
#define SCIPstatistic(x)
Definition: pub_message.h:101
SCIP_RETCODE SCIPlpiAddRows(SCIP_LPI *lpi, int nrows, const SCIP_Real *lhs, const SCIP_Real *rhs, char **rownames, int nnonz, const int *beg, const int *ind, const SCIP_Real *val)
Definition: lpi_clp.cpp:782
static SCIP_RETCODE nlrowaggrStoreLinearTerms(SCIP *scip, SCIP_NLROWAGGR *nlrowaggr, SCIP_VAR **linvars, SCIP_Real *lincoefs, int nlinvars)
Definition: sepa_eccuts.c:295
int SCIPgetDepth(SCIP *scip)
Definition: scip.c:38140
SCIP_Bool SCIPisGE(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
Definition: scip.c:41624
SCIP_RETCODE SCIPaddCoefLinear(SCIP *scip, SCIP_CONS *cons, SCIP_VAR *var, SCIP_Real val)
SCIP_RETCODE SCIPaddCons(SCIP *scip, SCIP_CONS *cons)
Definition: scip.c:11477
static SCIP_RETCODE nlrowaggrFree(SCIP *scip, SCIP_NLROWAGGR **nlrowaggr)
Definition: sepa_eccuts.c:539
SCIP_RETCODE SCIPsolve(SCIP *scip)
Definition: scip.c:14503
SCIP_RETCODE SCIPlpiChgBounds(SCIP_LPI *lpi, int ncols, const int *ind, const SCIP_Real *lb, const SCIP_Real *ub)
Definition: lpi_clp.cpp:941
Constraint handler for "xor" constraints, .
SCIP_RETCODE SCIPsetHeuristics(SCIP *scip, SCIP_PARAMSETTING paramsetting, SCIP_Bool quiet)
Definition: scip.c:4405
#define SCIPallocBufferArray(scip, ptr, num)
Definition: scip.h:20585
SCIP_Real SCIPvarGetLbGlobal(SCIP_VAR *var)
Definition: var.c:17057
SCIP_RETCODE SCIPcreate(SCIP **scip)
Definition: scip.c:692
#define CLIQUE_BACKTRACKFREQ
Definition: sepa_eccuts.c:48
static SCIP_RETCODE addLinearTermToCut(SCIP *scip, SCIP_SOL *sol, SCIP_ROW *cut, SCIP_VAR *x, SCIP_Real coeff, SCIP_Real *cutconstant, SCIP_Real *cutactivity, SCIP_Bool *success)
Definition: sepa_eccuts.c:2212
static SCIP_RETCODE createLP(SCIP *scip, SCIP_SEPADATA *sepadata)
Definition: sepa_eccuts.c:1751
SCIP_RETCODE SCIPflushRowExtensions(SCIP *scip, SCIP_ROW *row)
Definition: scip.c:27834
SCIP_RETCODE SCIPsetObjsense(SCIP *scip, SCIP_OBJSENSE objsense)
Definition: scip.c:10014
SCIP_VAR ** SCIPnlrowGetQuadVars(SCIP_NLROW *nlrow)
Definition: nlp.c:3275
static SCIP_RETCODE findAndStoreEcAggregations(SCIP *scip, SCIP_SEPADATA *sepadata, SCIP_NLROW *nlrow, SCIP_SOL *sol)
Definition: sepa_eccuts.c:1580
#define REALABS(x)
Definition: def.h:151
#define DEFAULT_MAXROUNDSROOT
Definition: sepa_eccuts.c:52
#define SCIPfreeMemoryArrayNull(scip, ptr)
Definition: scip.h:20545
SCIP_RETCODE SCIPlpiCreate(SCIP_LPI **lpi, SCIP_MESSAGEHDLR *messagehdlr, const char *name, SCIP_OBJSEN objsen)
Definition: lpi_clp.cpp:469
SCIP_EXPRTREE * SCIPnlrowGetExprtree(SCIP_NLROW *nlrow)
Definition: nlp.c:3353
#define DEFAULT_DYNAMICCUTS
Definition: sepa_eccuts.c:50
#define SCIP_Real
Definition: def.h:127
#define MIN(x, y)
Definition: memory.c:67
static SCIP_DECL_SEPAEXECLP(sepaExeclpEccuts)
Definition: sepa_eccuts.c:2723
SCIP_RETCODE SCIPprintRow(SCIP *scip, SCIP_ROW *row, FILE *file)
Definition: scip.c:28334
static SCIP_DECL_SEPAFREE(sepaFreeEccuts)
Definition: sepa_eccuts.c:2681
SCIP_RETCODE SCIPreleaseRow(SCIP *scip, SCIP_ROW **row)
Definition: scip.c:27738
int SCIPsepaGetNCallsAtNode(SCIP_SEPA *sepa)
Definition: sepa.c:760
static SCIP_RETCODE sepadataCreate(SCIP *scip, SCIP_SEPADATA **sepadata)
Definition: sepa_eccuts.c:621
static SCIP_Real evalCorner(SCIP_ECAGGR *ecaggr, int k)
Definition: sepa_eccuts.c:1863
TCLIQUE_Bool tcliqueFlush(TCLIQUE_GRAPH *tcliquegraph)
#define ADJUSTFACETTOL
Definition: sepa_eccuts.c:67
#define SCIPfreeBufferArray(scip, ptr)
Definition: scip.h:20597
static SCIP_RETCODE computeCut(SCIP *scip, SCIP_SEPA *sepa, SCIP_SEPADATA *sepadata, SCIP_NLROWAGGR *nlrowaggr, SCIP_SOL *sol, SCIP_Bool *separated, SCIP_Bool *cutoff)
Definition: sepa_eccuts.c:2375
SCIP_RETCODE SCIPgetRealParam(SCIP *scip, const char *name, SCIP_Real *value)
Definition: scip.c:3797
#define SCIPdebug(x)
Definition: pub_message.h:74
#define DEFAULT_MINAGGRSIZE
Definition: sepa_eccuts.c:60
SCIP_RETCODE SCIPcreateConsBasicXor(SCIP *scip, SCIP_CONS **cons, const char *name, SCIP_Bool rhs, int nvars, SCIP_VAR **vars)
Definition: cons_xor.c:5482
SCIP_RETCODE SCIPhashmapInsert(SCIP_HASHMAP *hashmap, void *origin, void *image)
Definition: misc.c:2094
SCIP_RETCODE SCIPincludeSepaBasic(SCIP *scip, SCIP_SEPA **sepa, const char *name, const char *desc, int priority, int freq, SCIP_Real maxbounddist, SCIP_Bool usessubscip, SCIP_Bool delay, SCIP_DECL_SEPAEXECLP((*sepaexeclp)), SCIP_DECL_SEPAEXECSOL((*sepaexecsol)), SCIP_SEPADATA *sepadata)
Definition: scip.c:6660
SCIP_Bool SCIPisPositive(SCIP *scip, SCIP_Real val)
Definition: scip.c:41697
static SCIP_RETCODE ecaggrAddBilinTerm(SCIP *scip, SCIP_ECAGGR *ecaggr, SCIP_VAR *x, SCIP_VAR *y, SCIP_Real coef)
Definition: sepa_eccuts.c:221
SCIP_SOL * SCIPgetBestSol(SCIP *scip)
Definition: scip.c:35767
SCIP_RETCODE SCIPaddRealParam(SCIP *scip, const char *name, const char *desc, SCIP_Real *valueptr, SCIP_Bool isadvanced, SCIP_Real defaultvalue, SCIP_Real minvalue, SCIP_Real maxvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
Definition: scip.c:3629
SCIP_RETCODE SCIPaddVarToRow(SCIP *scip, SCIP_ROW *row, SCIP_VAR *var, SCIP_Real val)
Definition: scip.c:27864
SCIP_Real SCIPgetCutEfficacy(SCIP *scip, SCIP_SOL *sol, SCIP_ROW *cut)
Definition: scip.c:30836
static const int poweroftwo[]
Definition: sepa_eccuts.c:71
#define SEPA_USESSUBSCIP
Definition: sepa_eccuts.c:40
#define DEFAULT_MAXROUNDS
Definition: sepa_eccuts.c:51
#define SEPA_MAXBOUNDDIST
Definition: sepa_eccuts.c:39
int * termvars2
Definition: sepa_eccuts.c:87
default SCIP plugins
static SCIP_RETCODE ecaggrAddQuadvar(SCIP_ECAGGR *ecaggr, SCIP_VAR *x)
Definition: sepa_eccuts.c:210
static SCIP_RETCODE updateMIP(SCIP *subscip, SCIP_NLROW *nlrow, SCIP_VAR **forwardarcs, SCIP_VAR **backwardarcs, int *quadvar2aggr, int *nedges)
Definition: sepa_eccuts.c:926
struct SCIP_SepaData SCIP_SEPADATA
Definition: type_sepa.h:38
SCIP_QUADELEM * SCIPnlrowGetQuadElems(SCIP_NLROW *nlrow)
Definition: nlp.c:3322
SCIP_RETCODE SCIPprintSol(SCIP *scip, SCIP_SOL *sol, FILE *file, SCIP_Bool printzeros)
Definition: scip.c:35397
static SCIP_Bool checkRikun(SCIP *scip, SCIP_ECAGGR *ecaggr, SCIP_Real *fvals, SCIP_Real *facet)
Definition: sepa_eccuts.c:1679