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