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