SCIP Doxygen Documentation
Loading...
Searching...
No Matches
nlhdlr_soc.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 nlhdlr_soc.c
26 * @ingroup DEFPLUGINS_NLHDLR
27 * @brief nonlinear handler for second order cone constraints
28
29 * @author Benjamin Mueller
30 * @author Felipe Serrano
31 * @author Fabian Wegscheider
32 *
33 * This is a nonlinear handler for second order cone constraints of the form
34 *
35 * \f[\sqrt{\sum_{i=1}^{n} (v_i^T x + \beta_i)^2} \leq v_{n+1}^T x + \beta_{n+1}.\f]
36 *
37 * Note that \f$v_i\f$, for \f$i \leq n\f$, could be 0, thus allowing a positive constant term inside the root.
38 *
39 * @todo test if it makes sense to only disaggregate when nterms > some parameter
40 *
41 */
42
43#include "scip/nlhdlr_soc.h"
44#include "scip/cons_nonlinear.h"
45#include "scip/expr_pow.h"
46#include "scip/expr_sum.h"
47#include "scip/expr_var.h"
48#include "scip/debug.h"
49#include "scip/pub_nlhdlr.h"
50#include "scip/lapack_calls.h"
51
52
53/* fundamental nonlinear handler properties */
54#define NLHDLR_NAME "soc"
55#define NLHDLR_DESC "nonlinear handler for second-order cone structures"
56#define NLHDLR_DETECTPRIORITY 100 /**< priority of the nonlinear handler for detection */
57#define NLHDLR_ENFOPRIORITY 100 /**< priority of the nonlinear handler for enforcement */
58#define DEFAULT_MINCUTEFFICACY 1e-5 /**< default value for parameter mincutefficacy */
59#define DEFAULT_COMPEIGENVALUES TRUE /**< default value for parameter compeigenvalues */
60
61/*
62 * Data structures
63 */
64
65/** nonlinear handler expression data. The data is structured in the following way:
66 *
67 * A 'term' is one of the arguments of the quadratic terms, i.e. \f$v_i^T x + beta_i\f$.
68 * The last term is always the one on the right-hand side. This means that nterms is
69 * equal to n+1 in the above description.
70 *
71 * - vars contains a list of all expressions which are treated as variables (no duplicates)
72 * - offsets contains the constants beta_i of each term
73 * - transcoefs contains the non-zero values of the transformation vectors v_i of each term
74 * - transcoefsidx contains for each entry of transcoefs the position of the respective variable in vars
75 * - termbegins contains the index at which the transcoefs of each term start, with a sentinel value
76 * - nterms is the total number of terms appearing on both sides
77 * - nvars is the total number of unique variables appearing (length of vars)
78 *
79 * Note that the numbers of nonzeroes in v_i is termbegins[i+1] - termbegins[i] and that
80 * the total number of entries in transcoefs and transcoefsidx is termbegins[nterms]
81 *
82 * The disaggregation is implicitly stored in the variables disvars and disrow. An SOC as
83 * described above is replaced by n smaller SOCs
84 *
85 * (v_i^T x + beta_i)^2 <= disvar_i * (v_{n+1}^T x + beta_{n+1})
86 *
87 * and the row sum_i disvar_i <= v_{n+1}^T x + beta_{n+1}.
88 *
89 * The disaggregation only happens if we have more than 3 terms.
90 *
91 * Example: The constraint sqrt(5 + (3x - 4y + 2)^2 + y^2 + 7z^2) <= 5x - y - 1
92 * results in the following nlhdlrexprdata:
93 *
94 * vars = {x, y, z}
95 * offsets = {2, 0, 0, sqrt(5), -1}
96 * transcoefs = {3, -4, 1, sqrt(7), 5, -1}
97 * transcoefsidx = {0, 1, 1, 2, 0, 1}
98 * termbegins = {0, 2, 3, 4, 4, 6}
99 * nvars = 3
100 * nterms = 5
101 *
102 * @note: due to the current implementation, the constant term is the second to last term, except when the SOC was a rotated
103 * SOC, e.g., 1 + x^2 - y*z, i.e., when detected by detectSocQuadraticSimple. In that case, the constant is third to
104 * last term.
105 */
106struct SCIP_NlhdlrExprData
107{
108 SCIP_EXPR** vars; /**< expressions which (aux)variables appear on both sides (x) */
109 SCIP_Real* offsets; /**< offsets of both sides (beta_i) */
110 SCIP_Real* transcoefs; /**< non-zeros of linear transformation vectors (v_i) */
111 int* transcoefsidx; /**< mapping of transformation coefficients to variable indices in vars */
112 int* termbegins; /**< starting indices of transcoefs for each term */
113 int nvars; /**< total number of variables appearing */
114 int nterms; /**< number of summands in the SQRT +1 for RHS (n+1) */
115
116 /* variables for cone disaggregation */
117 SCIP_VAR** disvars; /**< disaggregation variables for each term in lhs */
118 SCIP_ROW* disrow; /**< disaggregation row */
119
120 /* separation data */
121 SCIP_Real* varvals; /**< current values for vars */
122 SCIP_Real* disvarvals; /**< current values for disvars */
123};
124
125struct SCIP_NlhdlrData
126{
127 SCIP_Real mincutefficacy; /**< minimum efficacy a cut need to be added */
128 SCIP_Bool compeigenvalues; /**< whether Eigenvalue computations should be done to detect complex cases */
129};
130
131/*
132 * Local methods
133 */
134
135#ifdef SCIP_DEBUG
136/** prints the nlhdlr expression data */
137static
138void printNlhdlrExprData(
139 SCIP* scip, /**< SCIP data structure */
140 SCIP_NLHDLREXPRDATA* nlhdlrexprdata /**< pointer to store nonlinear handler expression data */
141 )
142{
143 int nterms;
144 int i;
145 int j;
146
147 nterms = nlhdlrexprdata->nterms;
148
149 SCIPinfoMessage(scip, NULL, "SQRT( ");
150
151 for( i = 0; i < nterms - 1; ++i )
152 {
153 int startidx;
154
155 startidx = nlhdlrexprdata->termbegins[i];
156
157 if( startidx == nlhdlrexprdata->termbegins[i + 1] )
158 {
159 /* v_i is 0 */
160 assert(nlhdlrexprdata->offsets[i] != 0.0);
161
162 SCIPinfoMessage(scip, NULL, "%g", SQR(nlhdlrexprdata->offsets[i]));
163 }
164 else
165 {
166 /* v_i is not 0 */
168
169 for( j = startidx; j < nlhdlrexprdata->termbegins[i + 1]; ++j )
170 {
171 if( nlhdlrexprdata->transcoefs[j] != 1.0 )
172 SCIPinfoMessage(scip, NULL, " %+g*", nlhdlrexprdata->transcoefs[j]);
173 else
174 SCIPinfoMessage(scip, NULL, " +");
175 if( SCIPgetExprAuxVarNonlinear(nlhdlrexprdata->vars[nlhdlrexprdata->transcoefsidx[j]]) != NULL )
176 {
177 SCIPinfoMessage(scip, NULL, "%s", SCIPvarGetName(SCIPgetExprAuxVarNonlinear(nlhdlrexprdata->vars[nlhdlrexprdata->transcoefsidx[j]])));
178 SCIPinfoMessage(scip, NULL, "(%p)", (void*)nlhdlrexprdata->vars[nlhdlrexprdata->transcoefsidx[j]]);
179 }
180 else
181 SCIPinfoMessage(scip, NULL, "%p", (void*)nlhdlrexprdata->vars[nlhdlrexprdata->transcoefsidx[j]]);
182 }
183 if( nlhdlrexprdata->offsets[i] != 0.0 )
184 SCIPinfoMessage(scip, NULL, " %+g", nlhdlrexprdata->offsets[i]);
185
186 SCIPinfoMessage(scip, NULL, ")^2");
187 }
188
189 if( i < nterms - 2 )
190 SCIPinfoMessage(scip, NULL, " + ");
191 }
192
193 SCIPinfoMessage(scip, NULL, " ) <=");
194
195 for( j = nlhdlrexprdata->termbegins[nterms-1]; j < nlhdlrexprdata->termbegins[nterms]; ++j )
196 {
197 if( nlhdlrexprdata->transcoefs[j] != 1.0 )
198 SCIPinfoMessage(scip, NULL, " %+g*", nlhdlrexprdata->transcoefs[j]);
199 else
200 SCIPinfoMessage(scip, NULL, " +");
201 if( SCIPgetExprAuxVarNonlinear(nlhdlrexprdata->vars[nlhdlrexprdata->transcoefsidx[j]]) != NULL )
202 SCIPinfoMessage(scip, NULL, "%s", SCIPvarGetName(SCIPgetExprAuxVarNonlinear(nlhdlrexprdata->vars[nlhdlrexprdata->transcoefsidx[j]])));
203 else
204 SCIPinfoMessage(scip, NULL, "%p", (void*)nlhdlrexprdata->vars[nlhdlrexprdata->transcoefsidx[j]]);
205 }
206 if( nlhdlrexprdata->offsets[nterms-1] != 0.0 )
207 SCIPinfoMessage(scip, NULL, " %+g", nlhdlrexprdata->offsets[nterms-1]);
208
209 SCIPinfoMessage(scip, NULL, "\n");
210}
211#endif
212
213/** helper method to create variables for the cone disaggregation */
214static
216 SCIP* scip, /**< SCIP data structure */
217 SCIP_EXPR* expr, /**< expression */
218 SCIP_NLHDLREXPRDATA* nlhdlrexprdata /**< nonlinear handler expression data */
219 )
220{
221 char name[SCIP_MAXSTRLEN];
222 int ndisvars;
223 int i;
224
225 assert(nlhdlrexprdata != NULL);
226
227 ndisvars = nlhdlrexprdata->nterms - 1;
228
229 /* allocate memory */
230 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &nlhdlrexprdata->disvars, ndisvars) );
231 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &nlhdlrexprdata->disvarvals, ndisvars) );
232
233 /* create disaggregation variables representing the epigraph of (v_i^T x + beta_i)^2 / (v_{n+1}^T x + beta_{n+1}) */
234 for( i = 0; i < ndisvars; ++i )
235 {
236 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "conedis_%p_%d", (void*) expr, i);
237 SCIP_CALL( SCIPcreateVarBasic(scip, &nlhdlrexprdata->disvars[i], name, 0.0, SCIPinfinity(scip), 0.0,
239 SCIPvarMarkRelaxationOnly(nlhdlrexprdata->disvars[i]);
240
241 SCIP_CALL( SCIPaddVar(scip, nlhdlrexprdata->disvars[i]) );
242 SCIP_CALL( SCIPaddVarLocksType(scip, nlhdlrexprdata->disvars[i], SCIP_LOCKTYPE_MODEL, 1, 1) );
243 }
244
245 return SCIP_OKAY;
246}
247
248/** helper method to free variables for the cone disaggregation */
249static
251 SCIP* scip, /**< SCIP data structure */
252 SCIP_NLHDLREXPRDATA* nlhdlrexprdata /**< nonlinear handler expression data */
253 )
254{
255 int ndisvars;
256 int i;
257
258 assert(nlhdlrexprdata != NULL);
259
260 if( nlhdlrexprdata->disvars == NULL )
261 return SCIP_OKAY;
262
263 ndisvars = nlhdlrexprdata->nterms - 1;
264
265 /* release variables */
266 for( i = 0; i < ndisvars; ++i )
267 {
268 SCIP_CALL( SCIPaddVarLocksType(scip, nlhdlrexprdata->disvars[i], SCIP_LOCKTYPE_MODEL, -1, -1) );
269 SCIP_CALL( SCIPreleaseVar(scip, &nlhdlrexprdata->disvars[i]) );
270 }
271
272 /* free memory */
273 SCIPfreeBlockMemoryArray(scip, &nlhdlrexprdata->disvars, ndisvars);
274 SCIPfreeBlockMemoryArrayNull(scip, &nlhdlrexprdata->disvarvals, ndisvars);
275
276 return SCIP_OKAY;
277}
278
279/** helper method to create the disaggregation row \f$\text{disvars}_i \leq v_{n+1}^T x + \beta_{n+1}\f$ */
280static
282 SCIP* scip, /**< SCIP data structure */
283 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
284 SCIP_EXPR* expr, /**< expression */
285 SCIP_NLHDLREXPRDATA* nlhdlrexprdata /**< nonlinear handler expression data */
286 )
287{
288 SCIP_Real beta;
289 char name[SCIP_MAXSTRLEN];
290 int ndisvars;
291 int nterms;
292 int i;
293
294 assert(scip != NULL);
295 assert(expr != NULL);
296 assert(nlhdlrexprdata != NULL);
297 assert(nlhdlrexprdata->disrow == NULL);
298
299 nterms = nlhdlrexprdata->nterms;
300 beta = nlhdlrexprdata->offsets[nterms - 1];
301
302 ndisvars = nterms - 1;
303
304 /* create row 0 <= beta_{n+1} */
305 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "conedis_%p_row", (void*) expr);
306 SCIP_CALL( SCIPcreateEmptyRowConshdlr(scip, &nlhdlrexprdata->disrow, conshdlr, name,
307 -SCIPinfinity(scip), beta, FALSE, FALSE, TRUE) );
308
309 /* add disvars to row */
310 for( i = 0; i < ndisvars; ++i )
311 {
312 SCIP_CALL( SCIPaddVarToRow(scip, nlhdlrexprdata->disrow, nlhdlrexprdata->disvars[i], 1.0) );
313 }
314
315 /* add rhs vars to row */
316 for( i = nlhdlrexprdata->termbegins[nterms - 1]; i < nlhdlrexprdata->termbegins[nterms]; ++i )
317 {
318 SCIP_VAR* var;
319 SCIP_Real coef;
320
321 var = SCIPgetExprAuxVarNonlinear(nlhdlrexprdata->vars[nlhdlrexprdata->transcoefsidx[i]]);
322 assert(var != NULL);
323
324 coef = -nlhdlrexprdata->transcoefs[i];
325
326 SCIP_CALL( SCIPaddVarToRow(scip, nlhdlrexprdata->disrow, var, coef) );
327 }
328
329 return SCIP_OKAY;
330}
331
332/** helper method to create nonlinear handler expression data */
333static
335 SCIP* scip, /**< SCIP data structure */
336 SCIP_EXPR** vars, /**< expressions which variables appear on both sides (\f$x\f$) */
337 SCIP_Real* offsets, /**< offsets of bot sides (\f$beta_i\f$) */
338 SCIP_Real* transcoefs, /**< non-zeroes of linear transformation vectors (\f$v_i\f$) */
339 int* transcoefsidx, /**< mapping of transformation coefficients to variable indices in vars */
340 int* termbegins, /**< starting indices of transcoefs for each term */
341 int nvars, /**< total number of variables appearing */
342 int nterms, /**< number of summands in the SQRT, +1 for RHS */
343 SCIP_NLHDLREXPRDATA** nlhdlrexprdata /**< pointer to store nonlinear handler expression data */
344 )
345{
346 int ntranscoefs;
347
348 assert(vars != NULL);
349 assert(offsets != NULL);
350 assert(transcoefs != NULL);
351 assert(transcoefsidx != NULL);
352 assert(termbegins != NULL);
353 assert(nlhdlrexprdata != NULL);
354
355 ntranscoefs = termbegins[nterms];
356
357 SCIP_CALL( SCIPallocBlockMemory(scip, nlhdlrexprdata) );
358 SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*nlhdlrexprdata)->vars, vars, nvars) );
359 SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*nlhdlrexprdata)->offsets, offsets, nterms) );
360 SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*nlhdlrexprdata)->transcoefs, transcoefs, ntranscoefs) );
361 SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*nlhdlrexprdata)->transcoefsidx, transcoefsidx, ntranscoefs) );
362 SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*nlhdlrexprdata)->termbegins, termbegins, nterms + 1) );
363 (*nlhdlrexprdata)->nvars = nvars;
364 (*nlhdlrexprdata)->nterms = nterms;
365
366 (*nlhdlrexprdata)->disrow = NULL;
367 (*nlhdlrexprdata)->disvars = NULL;
368
369 (*nlhdlrexprdata)->varvals = NULL;
370 (*nlhdlrexprdata)->disvarvals = NULL;
371
372#ifdef SCIP_DEBUG
373 SCIPdebugMsg(scip, "created nlhdlr data for the following soc expression:\n");
374 printNlhdlrExprData(scip, *nlhdlrexprdata);
375 /* SCIPdebugMsg(scip, "x is %p\n", (void *)vars[0]); */
376#endif
377
378 return SCIP_OKAY;
379}
380
381/** helper method to free nonlinear handler expression data */
382static
384 SCIP* scip, /**< SCIP data structure */
385 SCIP_NLHDLREXPRDATA** nlhdlrexprdata /**< pointer to free nonlinear handler expression data */
386 )
387{
388 int ntranscoefs;
389
390 assert(nlhdlrexprdata != NULL);
391 assert(*nlhdlrexprdata != NULL);
392
393 /* free variables and row for cone disaggregation */
394 SCIP_CALL( freeDisaggrVars(scip, *nlhdlrexprdata) );
395
396 ntranscoefs = (*nlhdlrexprdata)->termbegins[(*nlhdlrexprdata)->nterms];
397
398 SCIPfreeBlockMemoryArrayNull(scip, &(*nlhdlrexprdata)->varvals, (*nlhdlrexprdata)->nvars);
399 SCIPfreeBlockMemoryArray(scip, &(*nlhdlrexprdata)->termbegins, (*nlhdlrexprdata)->nterms + 1);
400 SCIPfreeBlockMemoryArray(scip, &(*nlhdlrexprdata)->transcoefsidx, ntranscoefs);
401 SCIPfreeBlockMemoryArray(scip, &(*nlhdlrexprdata)->transcoefs, ntranscoefs);
402 SCIPfreeBlockMemoryArray(scip, &(*nlhdlrexprdata)->offsets, (*nlhdlrexprdata)->nterms);
403 SCIPfreeBlockMemoryArray(scip, &(*nlhdlrexprdata)->vars, (*nlhdlrexprdata)->nvars);
404 SCIPfreeBlockMemory(scip, nlhdlrexprdata);
405
406 return SCIP_OKAY;
407}
408
409/** set varvalrs in nlhdlrexprdata to values from given SCIP solution */
410static
412 SCIP* scip, /**< SCIP data structure */
413 SCIP_NLHDLREXPRDATA* nlhdlrexprdata, /**< nonlinear handler expression data */
414 SCIP_SOL* sol, /**< SCIP solution */
415 SCIP_Bool roundtinyfrac /**< whether values close to integers should be rounded */
416 )
417{
418 int i;
419
420 assert(nlhdlrexprdata != NULL);
421 assert(nlhdlrexprdata->varvals != NULL);
422
423 /* update varvals */
424 for( i = 0; i < nlhdlrexprdata->nvars; ++i )
425 {
426 nlhdlrexprdata->varvals[i] = SCIPgetSolVal(scip, sol, SCIPgetExprAuxVarNonlinear(nlhdlrexprdata->vars[i]));
427 if( roundtinyfrac && SCIPisIntegral(scip, nlhdlrexprdata->varvals[i]) )
428 nlhdlrexprdata->varvals[i] = SCIPround(scip, nlhdlrexprdata->varvals[i]);
429 }
430
431 /* update disvarvals (in unittests, this may be NULL even though nterms > 1 */
432 if( nlhdlrexprdata->disvarvals != NULL )
433 for( i = 0; i < nlhdlrexprdata->nterms - 1; ++i )
434 {
435 nlhdlrexprdata->disvarvals[i] = SCIPgetSolVal(scip, sol, nlhdlrexprdata->disvars[i]);
436 if( roundtinyfrac && SCIPisIntegral(scip, nlhdlrexprdata->disvarvals[i]) )
437 nlhdlrexprdata->disvarvals[i] = SCIPround(scip, nlhdlrexprdata->disvarvals[i]);
438 }
439}
440
441/** evaluate a single term of the form \f$v_i^T x + \beta_i\f$ */
442static
444 SCIP* scip, /**< SCIP data structure */
445 SCIP_NLHDLREXPRDATA* nlhdlrexprdata, /**< nonlinear handler expression data */
446 int k /**< term to be evaluated */
447 )
448{
450 int i;
451
452 assert(scip != NULL);
453 assert(nlhdlrexprdata != NULL);
454 assert(0 <= k && k < nlhdlrexprdata->nterms);
455
456 result = nlhdlrexprdata->offsets[k];
457
458 for( i = nlhdlrexprdata->termbegins[k]; i < nlhdlrexprdata->termbegins[k + 1]; ++i )
459 result += nlhdlrexprdata->transcoefs[i] * nlhdlrexprdata->varvals[nlhdlrexprdata->transcoefsidx[i]];
460
461 return result;
462}
463
464/** computes gradient cut for a 2D or 3D SOC
465 *
466 * A 3D SOC looks like
467 * \f[
468 * \sqrt{ (v_1^T x + \beta_1)^2 + (v_2^T x + \beta_2)^2 } \leq v_3^T x + \beta_3
469 * \f]
470 *
471 * Let \f$f(x)\f$ be the left-hand-side. The partial derivatives of \f$f\f$ are given by
472 * \f[
473 * \frac{\delta f}{\delta x_j} = \frac{(v_1)_j(v_1^T x + \beta_1) + (v_2)_j (v_2^T x + \beta_2)}{f(x)}
474 * \f]
475 *
476 * and the gradient cut is then \f$f(x^*) + \nabla f(x^*)(x - x^*) \leq v_3^T x + \beta_3\f$.
477 *
478 * If \f$\beta_1 = \beta_2 = 0\f$, then the constant on the left-hand-side of the cut becomes zero:
479 * \f[
480 * f(x^*) - (\frac{(v_1)_j v_1^T x^* + (v_2)_j v_2^T x^*}{f(x^*)})_j^T x^*
481 * = f(x^*) - \frac{1}{f(x^*)} \sum_j ((v_1)_j x_j^* v_1^T x^* + (v_2)_j x_j^* v_2^T x^*)
482 * = f(x^*) - \frac{1}{f(x^*)} ((v_1^T x^*)^2 + (v_2^T x^*)^2)
483 * = f(x^*) - \frac{1}{f(x^*)} f(x^*)^2 = 0
484 * \f]
485 *
486 * A 2D SOC is
487 * \f[
488 * |v_1^T x + \beta_1| \leq v_2^T x + \beta_2
489 * \f]
490 * but we build the cut using the same procedure as for 3D.
491 */
492static
494 SCIP* scip, /**< SCIP data structure */
495 SCIP_ROWPREP** rowprep, /**< buffer to store rowprep with cut data */
496 SCIP_EXPR* expr, /**< expression */
497 SCIP_CONS* cons, /**< the constraint that expr is part of */
498 SCIP_NLHDLREXPRDATA* nlhdlrexprdata, /**< nonlinear handler expression data */
499 SCIP_Real mincutviolation, /**< minimal required cut violation */
500 SCIP_Real rhsval /**< value of last term at sol */
501 )
502{
503 SCIP_Real* transcoefs;
504 SCIP_Real cutcoef;
505 SCIP_Real fvalue;
506 SCIP_Real valterms[2] = {0.0, 0.0}; /* for lint */
507 SCIP_Real cutrhs;
508 SCIP_EXPR** vars;
509 SCIP_VAR* cutvar;
510 SCIP_Bool offsetzero;
511 int* transcoefsidx;
512 int* termbegins;
513 int nterms;
514 int i;
515 int j;
516
517 assert(rowprep != NULL);
518 assert(expr != NULL);
519 assert(cons != NULL);
520 assert(nlhdlrexprdata != NULL);
521
522 vars = nlhdlrexprdata->vars;
523 transcoefs = nlhdlrexprdata->transcoefs;
524 transcoefsidx = nlhdlrexprdata->transcoefsidx;
525 termbegins = nlhdlrexprdata->termbegins;
526 nterms = nlhdlrexprdata->nterms;
527
528 *rowprep = NULL;
529
530 /* evaluate lhs terms and compute f(x*), check whether both beta_1 and beta_2 are zero */
531 fvalue = 0.0;
532 offsetzero = TRUE;
533 for( i = 0; i < nterms - 1; ++i )
534 {
535 valterms[i] = evalSingleTerm(scip, nlhdlrexprdata, i);
536 fvalue += SQR( valterms[i] );
537 if( nlhdlrexprdata->offsets[i] != 0.0 )
538 offsetzero = FALSE;
539 }
540 fvalue = sqrt(fvalue);
541
542 /* don't generate cut if we are not violated @todo: remove this once core detects better when a nlhdlr's cons is
543 * violated
544 */
545 if( fvalue - rhsval <= mincutviolation )
546 {
547 SCIPdebugMsg(scip, "do not generate cut: rhsval %g, fvalue %g violation is %g\n", rhsval, fvalue, fvalue - rhsval);
548 return SCIP_OKAY;
549 }
550
551 /* if f(x*) = 0 then we are at top of cone, where we cannot generate cut */
552 if( SCIPisZero(scip, fvalue) )
553 {
554 SCIPdebugMsg(scip, "do not generate cut for lhs=%g, cannot linearize at top of cone\n", fvalue);
555 return SCIP_OKAY;
556 }
557
558 /* create cut */
560 SCIP_CALL( SCIPensureRowprepSize(scip, *rowprep, termbegins[nterms]) );
561
562 /* cut is f(x*) + \nabla f(x*)^T (x - x*) \leq v_n^T x + \beta_n, i.e.,
563 * \nabla f(x*)^T x - v_n^T x \leq \beta_n + \nabla f(x*)^T x* - f(x*)
564 * thus cutrhs is \beta_n - f(x*) + \nabla f(x*)^T x*
565 * if offsetzero, then we make sure that cutrhs is exactly \beta_n
566 */
567 cutrhs = nlhdlrexprdata->offsets[nterms - 1];
568 if( !offsetzero )
569 cutrhs -= fvalue;
570
571 /* add cut coefficients from lhs terms and compute cut's rhs */
572 for( j = 0; j < nterms - 1; ++j )
573 {
574 for( i = termbegins[j]; i < termbegins[j + 1]; ++i )
575 {
576 cutvar = SCIPgetExprAuxVarNonlinear(vars[transcoefsidx[i]]);
577
578 /* cutcoef is (the first part of) the partial derivative w.r.t cutvar */
579 cutcoef = transcoefs[i] * valterms[j] / fvalue;
580
581 SCIP_CALL( SCIPaddRowprepTerm(scip, *rowprep, cutvar, cutcoef) );
582
583 if( !offsetzero )
584 cutrhs += cutcoef * nlhdlrexprdata->varvals[transcoefsidx[i]];
585 }
586 }
587
588 /* add terms for v_n */
589 for( i = termbegins[nterms - 1]; i < termbegins[nterms]; ++i )
590 {
591 cutvar = SCIPgetExprAuxVarNonlinear(vars[transcoefsidx[i]]);
592 SCIP_CALL( SCIPaddRowprepTerm(scip, *rowprep, cutvar, -transcoefs[i]) );
593 }
594
595 /* add side */
596 SCIProwprepAddSide(*rowprep, cutrhs);
597
598 /* set name */
599 (void) SCIPsnprintf(SCIProwprepGetName(*rowprep), SCIP_MAXSTRLEN, "soc%d_%p_%" SCIP_LONGINT_FORMAT, nterms, (void*) expr, SCIPgetNLPs(scip));
600
601 return SCIP_OKAY;
602}
603
604/** helper method to compute and add a gradient cut for the k-th cone disaggregation
605 *
606 * After the SOC constraint \f$\sqrt{\sum_{i = 0}^{n-1} (v_i^T x + \beta_i)^2} \leq v_n^T x + \beta_n\f$
607 * has been disaggregated into the row \f$\sum_{i = 0}^{n-1} y_i \leq v_n^T x + \beta_n\f$ and the smaller SOC constraints
608 * \f[
609 * (v_i^T x + \beta_i)^2 \leq (v_n^T x + \beta_n) y_i \text{ for } i \in \{0, \ldots, n -1\},
610 * \f]
611 * we want to separate one of the small rotated cones.
612 * We first transform it into standard form:
613 * \f[
614 * \sqrt{4(v_i^T x + \beta_i)^2 + (v_n^T x + \beta_n - y_i)^2} - v_n^T x - \beta_n - y_i \leq 0.
615 * \f]
616 * Let \f$f(x,y)\f$ be the left-hand-side. We now compute the gradient by
617 * \f{align*}{
618 * \frac{\delta f}{\delta x_j} &= \frac{(v_i)_j(4v_i^T x + 4\beta_i) + (v_n)_j(v_n^T x + \beta_n - y_i)}{\sqrt{4(v_i^T x + \beta_i)^2 + (v_n^T x + \beta_n - y_i)^2}} - (v_n)_j \\
619 * \frac{\delta f}{\delta y_i} &= \frac{y_i - v_n^T x -\beta_n}{\sqrt{4(v_i^T x + \beta_i)^2 + (v_n^T x + \beta_n - y_i)^2}} - 1
620 * \f}
621 * and the gradient cut is then \f$f(x^*, y^*) + \nabla f(x^*,y^*)((x,y) - (x^*, y^*)) \leq 0\f$.
622 *
623 * As in \ref generateCutSolSOC(), the cut constant is zero if \f$\beta_i = \beta_n = 0\f$.
624 */
625static
627 SCIP* scip, /**< SCIP data structure */
628 SCIP_ROWPREP** rowprep, /**< buffer to store rowprep with cut data */
629 SCIP_EXPR* expr, /**< expression */
630 SCIP_CONS* cons, /**< the constraint that expr is part of */
631 SCIP_NLHDLREXPRDATA* nlhdlrexprdata, /**< nonlinear handler expression data */
632 int disaggidx, /**< index of disaggregation to separate */
633 SCIP_Real mincutviolation, /**< minimal required cut violation */
634 SCIP_Real rhsval /**< value of the rhs term */
635 )
636{
637 SCIP_EXPR** vars;
638 SCIP_VAR** disvars;
639 SCIP_Real* transcoefs;
640 int* transcoefsidx;
641 int* termbegins;
642 SCIP_VAR* cutvar;
643 SCIP_Real cutcoef;
644 SCIP_Real fvalue;
645 SCIP_Real disvarval;
646 SCIP_Real lhsval;
647 SCIP_Real constant;
648 SCIP_Real denominator;
649 SCIP_Bool offsetzero;
650 int ncutvars;
651 int nterms;
652 int i;
653
654 assert(rowprep != NULL);
655 assert(expr != NULL);
656 assert(cons != NULL);
657 assert(nlhdlrexprdata != NULL);
658 assert(disaggidx < nlhdlrexprdata->nterms-1);
659
660 vars = nlhdlrexprdata->vars;
661 disvars = nlhdlrexprdata->disvars;
662 transcoefs = nlhdlrexprdata->transcoefs;
663 transcoefsidx = nlhdlrexprdata->transcoefsidx;
664 termbegins = nlhdlrexprdata->termbegins;
665 nterms = nlhdlrexprdata->nterms;
666
667 /* nterms is equal to n in the description and disaggidx is in {0, ..., n - 1} */
668
669 *rowprep = NULL;
670
671 disvarval = nlhdlrexprdata->disvarvals[disaggidx];
672
673 lhsval = evalSingleTerm(scip, nlhdlrexprdata, disaggidx);
674
675 denominator = sqrt(4.0 * SQR(lhsval) + SQR(rhsval - disvarval));
676
677 /* compute value of function to be separated (f(x*,y*)) */
678 fvalue = denominator - rhsval - disvarval;
679
680 /* if the disagg soc is not violated don't compute cut */
681 if( fvalue <= mincutviolation )
682 {
683 SCIPdebugMsg(scip, "skip cut on disaggregation index %d as violation=%g below minviolation %g\n", disaggidx,
684 fvalue, mincutviolation);
685 return SCIP_OKAY;
686 }
687
688 /* if the denominator is 0 -> the constraint can't be violated, and the gradient is infinite */
689 if( SCIPisZero(scip, denominator) )
690 {
691 SCIPdebugMsg(scip, "skip cut on disaggregation index %d as we are on top of cone (denom=%g)\n", disaggidx, denominator);
692 return SCIP_OKAY;
693 }
694
695 /* compute upper bound on the number of variables in cut: vars in rhs + vars in term + disagg var */
696 ncutvars = (termbegins[nterms] - termbegins[nterms-1]) + (termbegins[disaggidx + 1] - termbegins[disaggidx]) + 1;
697
698 /* create cut */
700 SCIP_CALL( SCIPensureRowprepSize(scip, *rowprep, ncutvars) );
701
702 /* check whether offsets (beta) are zero, so we can know cut constant will be zero */
703 offsetzero = nlhdlrexprdata->offsets[disaggidx] == 0.0 && nlhdlrexprdata->offsets[nterms-1] == 0.0;
704
705 /* constant will be grad_f(x*,y*)^T (x*, y*) */
706 constant = 0.0;
707
708 /* a variable could appear on the lhs and rhs, but we add the coefficients separately */
709
710 /* add terms for v_disaggidx */
711 for( i = termbegins[disaggidx]; i < termbegins[disaggidx + 1]; ++i )
712 {
713 cutvar = SCIPgetExprAuxVarNonlinear(vars[transcoefsidx[i]]);
714 assert(cutvar != NULL);
715
716 /* cutcoef is (the first part of) the partial derivative w.r.t cutvar */
717 cutcoef = 4.0 * lhsval * transcoefs[i] / denominator;
718
719 SCIP_CALL( SCIPaddRowprepTerm(scip, *rowprep, cutvar, cutcoef) );
720
721 if( !offsetzero )
722 constant += cutcoef * nlhdlrexprdata->varvals[transcoefsidx[i]];
723 }
724
725 /* add terms for v_n */
726 for( i = termbegins[nterms - 1]; i < termbegins[nterms]; ++i )
727 {
728 cutvar = SCIPgetExprAuxVarNonlinear(vars[transcoefsidx[i]]);
729 assert(cutvar != NULL);
730
731 /* cutcoef is the (second part of) the partial derivative w.r.t cutvar */
732 cutcoef = (rhsval - disvarval) * transcoefs[i] / denominator - transcoefs[i];
733
734 SCIP_CALL( SCIPaddRowprepTerm(scip, *rowprep, cutvar, cutcoef) );
735
736 if( !offsetzero )
737 constant += cutcoef * nlhdlrexprdata->varvals[transcoefsidx[i]];
738 }
739
740 /* add term for disvar: cutcoef is the the partial derivative w.r.t. the disaggregation variable */
741 cutcoef = (disvarval - rhsval) / denominator - 1.0;
742 cutvar = disvars[disaggidx];
743
744 SCIP_CALL( SCIPaddRowprepTerm(scip, *rowprep, cutvar, cutcoef) );
745
746 if( !offsetzero )
747 {
748 constant += cutcoef * nlhdlrexprdata->disvarvals[disaggidx];
749
750 /* add side */
751 SCIProwprepAddSide(*rowprep, constant - fvalue);
752 }
753
754 /* set name */
755 (void) SCIPsnprintf(SCIProwprepGetName(*rowprep), SCIP_MAXSTRLEN, "soc_%p_%d_%" SCIP_LONGINT_FORMAT, (void*) expr, disaggidx, SCIPgetNLPs(scip));
756
757 return SCIP_OKAY;
758}
759
760/** given a rowprep, does a number of cleanup and checks and, if successful, generate a cut to be added to the sepastorage */
761static
763 SCIP* scip, /**< SCIP data structure */
764 SCIP_NLHDLRDATA* nlhdlrdata, /**< nonlinear handler data */
765 SCIP_ROWPREP* rowprep, /**< rowprep from which to generate row and add as cut */
766 SCIP_SOL* sol, /**< solution to be separated */
767 SCIP_CONS* cons, /**< constraint for which cut is generated, or NULL */
768 SCIP_Bool allowweakcuts, /**< whether weak cuts are allowed */
769 SCIP_RESULT* result /**< result pointer to update (set to SCIP_CUTOFF or SCIP_SEPARATED if cut is added) */
770 )
771{
772 SCIP_ROW* cut;
773 SCIP_Real cutefficacy;
774 SCIP_Bool success;
775
776 assert(scip != NULL);
777 assert(nlhdlrdata != NULL);
778 assert(rowprep != NULL);
779 assert(result != NULL);
780
781 SCIP_CALL( SCIPcleanupRowprep2(scip, rowprep, sol, SCIPgetHugeValue(scip), &success) );
782
783 if( !success )
784 {
785 SCIPdebugMsg(scip, "rowprep cleanup failed, skip cut\n");
786 return SCIP_OKAY;
787 }
788
790 {
791 SCIPdebugMsg(scip, "rowprep violation %g below LP feastol %g, skip cut\n",
793 return SCIP_OKAY;
794 }
795
796 SCIP_CALL( SCIPgetRowprepRowCons(scip, &cut, rowprep, cons) );
797
798 cutefficacy = SCIPgetCutEfficacy(scip, sol, cut);
799
800 SCIPdebugMsg(scip, "generated row for SOC, efficacy=%g, minefficacy=%g, allowweakcuts=%u\n",
801 cutefficacy, nlhdlrdata->mincutefficacy, allowweakcuts);
802
803 /* check whether cut is applicable */
804 if( SCIPisCutApplicable(scip, cut) && (allowweakcuts || cutefficacy >= nlhdlrdata->mincutefficacy) )
805 {
806 SCIP_Bool infeasible;
807
808 SCIP_CALL( SCIPaddRow(scip, cut, FALSE, &infeasible) );
809
810#ifdef SCIP_CONSNONLINEAR_ROWNOTREMOVABLE
811 /* mark row as not removable from LP for current node, if in enforcement (==addbranchscores) (this can prevent some cycling) */
812 if( addbranchscores )
814#endif
815
816 if( infeasible )
818 else
820 }
821
822 /* release row */
823 SCIP_CALL( SCIPreleaseRow(scip, &cut) );
824
825 return SCIP_OKAY;
826}
827
828/** given a rowprep, does a number of cleanup and checks and, if successful, generate a cut to be added to the cutpool */
829static
831 SCIP* scip, /**< SCIP data structure */
832 SCIP_NLHDLRDATA* nlhdlrdata, /**< nonlinear handler data */
833 SCIP_ROWPREP* rowprep, /**< rowprep from which to generate row and add as cut */
834 SCIP_SOL* sol, /**< solution to be separated */
835 SCIP_CONS* cons /**< constraint for which cut is generated, or NULL */
836 )
837{
838 SCIP_ROW* cut;
839 SCIP_Bool success;
840
841 assert(scip != NULL);
842 assert(nlhdlrdata != NULL);
843 assert(rowprep != NULL);
844
845 assert(!SCIProwprepIsLocal(rowprep));
846
847 SCIP_CALL( SCIPcleanupRowprep2(scip, rowprep, sol, SCIPgetHugeValue(scip), &success) );
848 /* if failed or cut is only locally valid now, then skip */
849 if( !success || SCIProwprepIsLocal(rowprep) )
850 return SCIP_OKAY;
851
852 /* if row after cleanup is just a boundchange, then skip */
853 if( SCIProwprepGetNVars(rowprep) <= 1 )
854 return SCIP_OKAY;
855
856 /* generate row and add to cutpool */
857 SCIP_CALL( SCIPgetRowprepRowCons(scip, &cut, rowprep, cons) );
858
860
861 SCIP_CALL( SCIPreleaseRow(scip, &cut) );
862
863 return SCIP_OKAY;
864}
865
866/** checks if an expression is quadratic and collects all occurring expressions
867 *
868 * @pre `expr2idx` and `occurringexprs` need to be initialized with capacity 2 * nchildren
869 *
870 * @note We assume that a linear term always appears before its corresponding
871 * quadratic term in quadexpr; this should be ensured by canonicalize
872 */
873static
875 SCIP* scip, /**< SCIP data structure */
876 SCIP_EXPR* quadexpr, /**< candidate for a quadratic expression */
877 SCIP_HASHMAP* expr2idx, /**< hashmap to store expressions */
878 SCIP_EXPR** occurringexprs, /**< array to store expressions */
879 int* nexprs, /**< buffer to store number of expressions */
880 SCIP_Bool* success /**< buffer to store whether the check was successful */
881 )
882{
883 SCIP_EXPR** children;
884 int nchildren;
885 int i;
886
887 assert(scip != NULL);
888 assert(quadexpr != NULL);
889 assert(expr2idx != NULL);
890 assert(occurringexprs != NULL);
891 assert(nexprs != NULL);
892 assert(success != NULL);
893
894 *nexprs = 0;
895 *success = FALSE;
896 children = SCIPexprGetChildren(quadexpr);
897 nchildren = SCIPexprGetNChildren(quadexpr);
898
899 /* iterate in reverse order to ensure that quadratic terms are found before linear terms */
900 for( i = nchildren - 1; i >= 0; --i )
901 {
902 SCIP_EXPR* child;
903
904 child = children[i];
905 if( SCIPisExprPower(scip, child) )
906 {
907 SCIP_EXPR* childarg;
908
909 if( SCIPgetExponentExprPow(child) != 2.0 )
910 return SCIP_OKAY;
911
912 childarg = SCIPexprGetChildren(child)[0];
913
914 if( !SCIPhashmapExists(expr2idx, (void*) childarg) )
915 {
916 SCIP_CALL( SCIPhashmapInsertInt(expr2idx, (void*) childarg, *nexprs) );
917
918 /* store the expression so we know it later */
919 assert(*nexprs < 2 * nchildren);
920 occurringexprs[*nexprs] = childarg;
921
922 ++(*nexprs);
923 }
924 }
925 else if( SCIPisExprVar(scip, child) && SCIPvarIsBinary(SCIPgetVarExprVar(child)) )
926 {
927 if( !SCIPhashmapExists(expr2idx, (void*) child) )
928 {
929 SCIP_CALL( SCIPhashmapInsertInt(expr2idx, (void*) child, *nexprs) );
930
931 /* store the expression so we know it later */
932 assert(*nexprs < 2 * nchildren);
933 occurringexprs[*nexprs] = child;
934
935 ++(*nexprs);
936 }
937 }
938 else if( SCIPisExprProduct(scip, child) )
939 {
940 SCIP_EXPR* childarg1;
941 SCIP_EXPR* childarg2;
942
943 if( SCIPexprGetNChildren(child) != 2 )
944 return SCIP_OKAY;
945
946 childarg1 = SCIPexprGetChildren(child)[0];
947 childarg2 = SCIPexprGetChildren(child)[1];
948
949 if( !SCIPhashmapExists(expr2idx, (void*) childarg1) )
950 {
951 SCIP_CALL( SCIPhashmapInsertInt(expr2idx, (void*) childarg1, *nexprs) );
952
953 /* store the expression so we know it later */
954 assert(*nexprs < 2 * nchildren);
955 occurringexprs[*nexprs] = childarg1;
956
957 ++(*nexprs);
958 }
959
960 if( !SCIPhashmapExists(expr2idx, (void*) childarg2) )
961 {
962 SCIP_CALL( SCIPhashmapInsertInt(expr2idx, (void*) childarg2, *nexprs) );
963
964 /* store the expression so we know it later */
965 assert(*nexprs < 2 * nchildren);
966 occurringexprs[*nexprs] = childarg2;
967
968 ++(*nexprs);
969 }
970 }
971 else
972 {
973 /* if there is a linear term without corresponding quadratic term, it is not a SOC */
974 if( !SCIPhashmapExists(expr2idx, (void*) child) )
975 return SCIP_OKAY;
976 }
977 }
978
979 *success = TRUE;
980
981 return SCIP_OKAY;
982}
983
984/* builds the constraint defining matrix and vector of a quadratic expression
985 *
986 * @pre `quadmatrix` and `linvector` need to be initialized with size `nexprs`^2 and `nexprs`, resp.
987 */
988static
990 SCIP* scip, /**< SCIP data structure */
991 SCIP_EXPR* quadexpr, /**< the quadratic expression */
992 SCIP_HASHMAP* expr2idx, /**< hashmap mapping the occurring expressions to their index */
993 int nexprs, /**< number of occurring expressions */
994 SCIP_Real* quadmatrix, /**< pointer to store (the lower-left triangle of) the quadratic matrix */
995 SCIP_Real* linvector /**< pointer to store the linear vector */
996 )
997{
998 SCIP_EXPR** children;
999 SCIP_Real* childcoefs;
1000 int nchildren;
1001 int i;
1002
1003 assert(scip != NULL);
1004 assert(quadexpr != NULL);
1005 assert(expr2idx != NULL);
1006 assert(quadmatrix != NULL);
1007 assert(linvector != NULL);
1008
1009 children = SCIPexprGetChildren(quadexpr);
1010 nchildren = SCIPexprGetNChildren(quadexpr);
1011 childcoefs = SCIPgetCoefsExprSum(quadexpr);
1012
1013 /* iterate over children to build the constraint defining matrix and vector */
1014 for( i = 0; i < nchildren; ++i )
1015 {
1016 int varpos;
1017
1018 if( SCIPisExprPower(scip, children[i]) )
1019 {
1020 assert(SCIPgetExponentExprPow(children[i]) == 2.0);
1021 assert(SCIPhashmapExists(expr2idx, (void*) SCIPexprGetChildren(children[i])[0]));
1022
1023 varpos = SCIPhashmapGetImageInt(expr2idx, (void*) SCIPexprGetChildren(children[i])[0]);
1024 assert(0 <= varpos && varpos < nexprs);
1025
1026 quadmatrix[varpos * nexprs + varpos] = childcoefs[i];
1027 }
1028 else if( SCIPisExprVar(scip, children[i]) && SCIPvarIsBinary(SCIPgetVarExprVar(children[i])) )
1029 {
1030 assert(SCIPhashmapExists(expr2idx, (void*) children[i]));
1031
1032 varpos = SCIPhashmapGetImageInt(expr2idx, (void*) children[i]);
1033 assert(0 <= varpos && varpos < nexprs);
1034
1035 quadmatrix[varpos * nexprs + varpos] = childcoefs[i];
1036 }
1037 else if( SCIPisExprProduct(scip, children[i]) )
1038 {
1039 int varpos2;
1040
1041 assert(SCIPexprGetNChildren(children[i]) == 2);
1042 assert(SCIPhashmapExists(expr2idx, (void*) SCIPexprGetChildren(children[i])[0]));
1043 assert(SCIPhashmapExists(expr2idx, (void*) SCIPexprGetChildren(children[i])[1]));
1044
1045 varpos = SCIPhashmapGetImageInt(expr2idx, (void*) SCIPexprGetChildren(children[i])[0]);
1046 assert(0 <= varpos && varpos < nexprs);
1047
1048 varpos2 = SCIPhashmapGetImageInt(expr2idx, (void*) SCIPexprGetChildren(children[i])[1]);
1049 assert(0 <= varpos2 && varpos2 < nexprs);
1050 assert(varpos != varpos2);
1051
1052 /* Lapack uses only the lower left triangle of the symmetric matrix */
1053 quadmatrix[MIN(varpos, varpos2) * nexprs + MAX(varpos, varpos2)] = childcoefs[i] / 2.0;
1054 }
1055 else
1056 {
1057 varpos = SCIPhashmapGetImageInt(expr2idx, (void*) children[i]);
1058 assert(0 <= varpos && varpos < nexprs);
1059
1060 linvector[varpos] = childcoefs[i];
1061 }
1062 }
1063}
1064
1065/** tries to fill the nlhdlrexprdata for a potential quadratic SOC expression
1066 *
1067 * We say "try" because the expression might still turn out not to be a SOC at this point.
1068 */
1069static
1071 SCIP* scip, /**< SCIP data structure */
1072 SCIP_EXPR** occurringexprs, /**< array of all occurring expressions (nvars many) */
1073 SCIP_Real* eigvecmatrix, /**< array containing the Eigenvectors */
1074 SCIP_Real* eigvals, /**< array containing the Eigenvalues */
1075 SCIP_Real* bp, /**< product of linear vector b * P (eigvecmatrix^t) */
1076 int nvars, /**< number of variables */
1077 int* termbegins, /**< pointer to store the termbegins */
1078 SCIP_Real* transcoefs, /**< pointer to store the transcoefs */
1079 int* transcoefsidx, /**< pointer to store the transcoefsidx */
1080 SCIP_Real* offsets, /**< pointer to store the offsets */
1081 SCIP_Real* lhsconstant, /**< pointer to store the lhsconstant */
1082 int* nterms, /**< pointer to store the total number of terms */
1083 SCIP_Bool* success /**< whether the expression is indeed a SOC */
1084 )
1085{
1086 SCIP_Real sqrteigval;
1087 int nextterm = 0;
1088 int nexttranscoef = 0;
1089 int specialtermidx;
1090 int i;
1091 int j;
1092
1093 assert(scip != NULL);
1094 assert(occurringexprs != NULL);
1095 assert(eigvecmatrix != NULL);
1096 assert(eigvals != NULL);
1097 assert(bp != NULL);
1098 assert(termbegins != NULL);
1099 assert(transcoefs != NULL);
1100 assert(transcoefsidx != NULL);
1101 assert(offsets != NULL);
1102 assert(lhsconstant != NULL);
1103 assert(success != NULL);
1104
1105 *success = FALSE;
1106 *nterms = 0;
1107
1108 /* we have lhsconstant + x^t A x + b x <= 0 and A has a single negative eigenvalue; try to build soc;
1109 * we now store all the v_i^T x + beta_i on the lhs, and compute the constant
1110 */
1111 specialtermidx = -1;
1112 for( i = 0; i < nvars; ++i )
1113 {
1114 if( SCIPisZero(scip, eigvals[i]) )
1115 continue;
1116
1117 if( eigvals[i] < 0.0 )
1118 {
1119 assert(specialtermidx == -1); /* there should only be one negative eigenvalue */
1120
1121 specialtermidx = i;
1122
1123 *lhsconstant -= bp[i] * bp[i] / (4.0 * eigvals[i]);
1124
1125 continue;
1126 }
1127
1128 assert(eigvals[i] > 0.0);
1129 sqrteigval = sqrt(eigvals[i]);
1130
1131 termbegins[nextterm] = nexttranscoef;
1132 offsets[nextterm] = bp[i] / (2.0 * sqrteigval);
1133 *lhsconstant -= bp[i] * bp[i] / (4.0 * eigvals[i]);
1134
1135 /* set transcoefs */
1136 for( j = 0; j < nvars; ++j )
1137 {
1138 if( !SCIPisZero(scip, eigvecmatrix[i * nvars + j]) )
1139 {
1140 transcoefs[nexttranscoef] = sqrteigval * eigvecmatrix[i * nvars + j];
1141 transcoefsidx[nexttranscoef] = j;
1142
1143 ++nexttranscoef;
1144 }
1145 }
1146 ++nextterm;
1147 }
1148 assert(specialtermidx > -1);
1149
1150 /* process constant; if constant is negative -> no soc */
1151 if( SCIPisNegative(scip, *lhsconstant) )
1152 return SCIP_OKAY;
1153
1154 /* we need lhsconstant to be >= 0 */
1155 if( *lhsconstant < 0.0 )
1156 *lhsconstant = 0.0;
1157
1158 /* store constant term */
1159 if( *lhsconstant > 0.0 )
1160 {
1161 termbegins[nextterm] = nexttranscoef;
1162 offsets[nextterm] = sqrt(*lhsconstant);
1163 ++nextterm;
1164 }
1165
1166 /* now process rhs */
1167 {
1168 SCIP_Real rhstermlb;
1169 SCIP_Real rhstermub;
1170 SCIP_Real signfactor;
1171
1172 assert(-eigvals[specialtermidx] > 0.0);
1173 sqrteigval = sqrt(-eigvals[specialtermidx]);
1174
1175 termbegins[nextterm] = nexttranscoef;
1176 offsets[nextterm] = -bp[specialtermidx] / (2.0 * sqrteigval);
1177
1178 /* the expression can only be an soc if the resulting rhs term does not change sign;
1179 * the rhs term is a linear combination of variables, so estimate its bounds
1180 */
1181 rhstermlb = offsets[nextterm];
1182 for( j = 0; j < nvars; ++j )
1183 {
1184 SCIP_INTERVAL activity;
1185 SCIP_Real aux;
1186
1187 if( SCIPisZero(scip, eigvecmatrix[specialtermidx * nvars + j]) )
1188 continue;
1189
1190 SCIP_CALL( SCIPevalExprActivity(scip, occurringexprs[j]) );
1191 activity = SCIPexprGetActivity(occurringexprs[j]);
1192
1193 if( eigvecmatrix[specialtermidx * nvars + j] > 0.0 )
1194 {
1195 aux = activity.inf;
1196 assert(!SCIPisInfinity(scip, aux));
1197 }
1198 else
1199 {
1200 aux = activity.sup;
1201 assert(!SCIPisInfinity(scip, -aux));
1202 }
1203
1204 if( SCIPisInfinity(scip, aux) || SCIPisInfinity(scip, -aux) )
1205 {
1206 rhstermlb = -SCIPinfinity(scip);
1207 break;
1208 }
1209 else
1210 rhstermlb += sqrteigval * eigvecmatrix[specialtermidx * nvars + j] * aux;
1211 }
1212
1213 rhstermub = offsets[nextterm];
1214 for( j = 0; j < nvars; ++j )
1215 {
1216 SCIP_INTERVAL activity;
1217 SCIP_Real aux;
1218
1219 if( SCIPisZero(scip, eigvecmatrix[specialtermidx * nvars + j]) )
1220 continue;
1221
1222 SCIP_CALL( SCIPevalExprActivity(scip, occurringexprs[j]) );
1223 activity = SCIPexprGetActivity(occurringexprs[j]);
1224
1225 if( eigvecmatrix[specialtermidx * nvars + j] > 0.0 )
1226 {
1227 aux = activity.sup;
1228 assert(!SCIPisInfinity(scip, -aux));
1229 }
1230 else
1231 {
1232 aux = activity.inf;
1233 assert(!SCIPisInfinity(scip, aux));
1234 }
1235
1236 if( SCIPisInfinity(scip, aux) || SCIPisInfinity(scip, -aux) )
1237 {
1238 rhstermub = SCIPinfinity(scip);
1239 break;
1240 }
1241 else
1242 rhstermub += sqrteigval * eigvecmatrix[specialtermidx * nvars + j] * aux;
1243 }
1244
1245 /* since we are just interested in obtaining an interval that contains the real bounds
1246 * and is tight enough so that we can identify that the rhsvar does not change sign,
1247 * we swap the bounds in case of numerical troubles
1248 */
1249 if( rhstermub < rhstermlb )
1250 {
1251 assert(SCIPisEQ(scip, rhstermub, rhstermlb));
1252 SCIPswapReals(&rhstermub, &rhstermlb);
1253 }
1254
1255 /* if rhs changes sign -> not a SOC */
1256 if( SCIPisLT(scip, rhstermlb, 0.0) && SCIPisGT(scip, rhstermub, 0.0) )
1257 return SCIP_OKAY;
1258
1259 signfactor = SCIPisLE(scip, rhstermub, 0.0) ? -1.0 : 1.0;
1260
1261 offsets[nextterm] *= signfactor;
1262
1263 /* set transcoefs for rhs term */
1264 for( j = 0; j < nvars; ++j )
1265 {
1266 if( SCIPisZero(scip, eigvecmatrix[specialtermidx * nvars + j]) )
1267 continue;
1268
1269 transcoefs[nexttranscoef] = signfactor * sqrteigval * eigvecmatrix[specialtermidx * nvars + j];
1270 transcoefsidx[nexttranscoef] = j;
1271
1272 ++nexttranscoef;
1273 }
1274
1275 /* if rhs is a constant this method shouldn't have been called */
1276 assert(nexttranscoef > termbegins[nextterm]);
1277
1278 /* finish processing term */
1279 ++nextterm;
1280 }
1281
1282 *nterms = nextterm;
1283
1284 /* sentinel value */
1285 termbegins[nextterm] = nexttranscoef;
1286
1287 *success = TRUE;
1288
1289 return SCIP_OKAY;
1290}
1291
1292/** detects if expr &le; auxvar is of the form sqrt(sum_i coef_i (expr_i + shift_i)^2 + const) &le; auxvar
1293 *
1294 * @note if a user inputs the above expression with `const` = -epsilon, then `const` is going to be set to 0.
1295 */
1296static
1298 SCIP* scip, /**< SCIP data structure */
1299 SCIP_EXPR* expr, /**< expression */
1300 SCIP_NLHDLREXPRDATA** nlhdlrexprdata, /**< pointer to store nonlinear handler expression data */
1301 SCIP_Bool* success /**< pointer to store whether SOC structure has been detected */
1302 )
1303{
1304 SCIP_EXPR** children;
1305 SCIP_EXPR* child;
1306 SCIP_EXPR** vars;
1307 SCIP_HASHMAP* expr2idx;
1308 SCIP_HASHSET* linexprs;
1309 SCIP_Real* childcoefs;
1310 SCIP_Real* offsets;
1311 SCIP_Real* transcoefs;
1312 SCIP_Real constant;
1313 SCIP_Bool issoc;
1314 int* transcoefsidx;
1315 int* termbegins;
1316 int nchildren;
1317 int nterms;
1318 int nvars;
1319 int nextentry;
1320 int i;
1321
1322 assert(expr != NULL);
1323 assert(success != NULL);
1324
1325 *success = FALSE;
1326 issoc = TRUE;
1327
1328 /* relation is not "<=" -> skip */
1329 if( SCIPgetExprNLocksPosNonlinear(expr) == 0 )
1330 return SCIP_OKAY;
1331
1332 /* expression is a leaf (variable or constant) */
1333 if( SCIPexprGetNChildren(expr) == 0 )
1334 return SCIP_OKAY;
1335
1336 assert(SCIPexprGetNChildren(expr) > 0);
1337
1338 child = SCIPexprGetChildren(expr)[0];
1339 assert(child != NULL);
1340
1341 /* check whether expression is a sqrt and has a sum as child with at least 2 children and a non-negative constant */
1342 if( ! SCIPisExprPower(scip, expr)
1343 || SCIPgetExponentExprPow(expr) != 0.5
1344 || !SCIPisExprSum(scip, child)
1345 || SCIPexprGetNChildren(child) < 2
1346 || SCIPgetConstantExprSum(child) < 0.0)
1347 {
1348 return SCIP_OKAY;
1349 }
1350
1351 /* assert(SCIPvarGetLbLocal(auxvar) >= 0.0); */
1352
1353 /* get children of the sum */
1354 children = SCIPexprGetChildren(child);
1355 nchildren = SCIPexprGetNChildren(child);
1356 childcoefs = SCIPgetCoefsExprSum(child);
1357
1358 /* TODO: should we initialize the hashmap with size SCIPgetNVars() so that it never has to be resized? */
1359 SCIP_CALL( SCIPhashmapCreate(&expr2idx, SCIPblkmem(scip), nchildren) );
1360 SCIP_CALL( SCIPhashsetCreate(&linexprs, SCIPblkmem(scip), nchildren) );
1361
1362 /* we create coefs array here already, since we have to fill it in first loop in case of success
1363 * +1 for auxvar
1364 */
1365 SCIP_CALL( SCIPallocBufferArray(scip, &transcoefs, nchildren+1) );
1366
1367 nterms = 0;
1368
1369 /* check if all children are squares or linear terms with matching square term:
1370 * if the i-th child is (pow, expr, 2) we store the association <|expr -> i|> in expr2idx and if expr was in
1371 * linexprs, we remove it from there.
1372 * if the i-th child is expr' (different from (pow, expr, 2)) and expr' is not a key of expr2idx, we add it
1373 * to linexprs.
1374 * if at the end there is any expr in linexpr -> we do not have a separable quadratic function.
1375 */
1376 for( i = 0; i < nchildren; ++i )
1377 {
1378 /* handle quadratic expressions children */
1379 if( SCIPisExprPower(scip, children[i]) && SCIPgetExponentExprPow(children[i]) == 2.0 )
1380 {
1381 SCIP_EXPR* squarearg = SCIPexprGetChildren(children[i])[0];
1382
1383 if( !SCIPhashmapExists(expr2idx, (void*) squarearg) )
1384 {
1385 SCIP_CALL( SCIPhashmapInsertInt(expr2idx, (void *) squarearg, nterms) );
1386 }
1387
1388 if( childcoefs[i] < 0.0 )
1389 {
1390 issoc = FALSE;
1391 break;
1392 }
1393 transcoefs[nterms] = sqrt(childcoefs[i]);
1394
1395 SCIP_CALL( SCIPhashsetRemove(linexprs, (void*) squarearg) );
1396 ++nterms;
1397 }
1398 /* handle binary variable children */
1399 else if( SCIPisExprVar(scip, children[i]) && SCIPvarIsBinary(SCIPgetVarExprVar(children[i])) )
1400 {
1401 assert(!SCIPhashmapExists(expr2idx, (void*) children[i]));
1402 assert(!SCIPhashsetExists(linexprs, (void*) children[i]));
1403
1404 SCIP_CALL( SCIPhashmapInsertInt(expr2idx, (void *) children[i], nterms) );
1405
1406 if( childcoefs[i] < 0.0 )
1407 {
1408 issoc = FALSE;
1409 break;
1410 }
1411 transcoefs[nterms] = sqrt(childcoefs[i]);
1412
1413 ++nterms;
1414 }
1415 else
1416 {
1417 if( !SCIPhashmapExists(expr2idx, (void*) children[i]) )
1418 {
1419 SCIP_CALL( SCIPhashsetInsert(linexprs, SCIPblkmem(scip), (void*) children[i]) );
1420 }
1421 }
1422 }
1423
1424 /* there are linear terms without corresponding quadratic terms or it was detected not to be soc */
1425 if( SCIPhashsetGetNElements(linexprs) > 0 || ! issoc )
1426 {
1427 SCIPfreeBufferArray(scip, &transcoefs);
1428 SCIPhashsetFree(&linexprs, SCIPblkmem(scip) );
1429 SCIPhashmapFree(&expr2idx);
1430 return SCIP_OKAY;
1431 }
1432
1433 /* add one to terms counter for auxvar */
1434 ++nterms;
1435
1436 constant = SCIPgetConstantExprSum(child);
1437
1438 /* compute constant of possible soc expression to check its sign */
1439 for( i = 0; i < nchildren; ++i )
1440 {
1441 if( ! SCIPisExprPower(scip, children[i]) || SCIPgetExponentExprPow(children[i]) != 2.0 )
1442 {
1443 int auxvarpos;
1444
1445 assert(SCIPhashmapExists(expr2idx, (void*) children[i]) );
1446 auxvarpos = SCIPhashmapGetImageInt(expr2idx, (void*) children[i]);
1447
1448 constant -= SQR(0.5 * childcoefs[i] / transcoefs[auxvarpos]);
1449 }
1450 }
1451
1452 /* if the constant is negative -> no SOC */
1453 if( SCIPisNegative(scip, constant) )
1454 {
1455 SCIPfreeBufferArray(scip, &transcoefs);
1456 SCIPhashsetFree(&linexprs, SCIPblkmem(scip) );
1457 SCIPhashmapFree(&expr2idx);
1458 return SCIP_OKAY;
1459 }
1460 else if( SCIPisZero(scip, constant) )
1461 constant = 0.0;
1462 assert(constant >= 0.0);
1463
1464 /* at this point, we have found an SOC structure */
1465 *success = TRUE;
1466
1467 nvars = nterms;
1468
1469 /* add one to terms counter for constant term */
1470 if( constant > 0.0 )
1471 ++nterms;
1472
1473 /* allocate temporary memory to collect data */
1476 SCIP_CALL( SCIPallocBufferArray(scip, &transcoefsidx, nvars) );
1477 SCIP_CALL( SCIPallocBufferArray(scip, &termbegins, nterms + 1) );
1478
1479 /* fill in data for non constant terms of lhs; initialize their offsets */
1480 for( i = 0; i < nvars - 1; ++i )
1481 {
1482 transcoefsidx[i] = i;
1483 termbegins[i] = i;
1484 offsets[i] = 0.0;
1485 }
1486
1487 /* add constant term and rhs */
1488 vars[nvars - 1] = expr;
1489 if( constant > 0.0 )
1490 {
1491 /* constant term */
1492 termbegins[nterms - 2] = nterms - 2;
1493 offsets[nterms - 2] = sqrt(constant);
1494
1495 /* rhs */
1496 termbegins[nterms - 1] = nterms - 2;
1497 offsets[nterms - 1] = 0.0;
1498 transcoefsidx[nterms - 2] = nvars - 1;
1499 transcoefs[nterms - 2] = 1.0;
1500
1501 /* sentinel value */
1502 termbegins[nterms] = nterms - 1;
1503 }
1504 else
1505 {
1506 /* rhs */
1507 termbegins[nterms - 1] = nterms - 1;
1508 offsets[nterms - 1] = 0.0;
1509 transcoefsidx[nterms - 1] = nvars - 1;
1510 transcoefs[nterms - 1] = 1.0;
1511
1512 /* sentinel value */
1513 termbegins[nterms] = nterms;
1514 }
1515
1516 /* request required auxiliary variables and fill vars and offsets array */
1517 nextentry = 0;
1518 for( i = 0; i < nchildren; ++i )
1519 {
1520 if( SCIPisExprPower(scip, children[i]) && SCIPgetExponentExprPow(children[i]) == 2.0 )
1521 {
1522 SCIP_EXPR* squarearg;
1523
1524 squarearg = SCIPexprGetChildren(children[i])[0];
1525 assert(SCIPhashmapGetImageInt(expr2idx, (void*) squarearg) == nextentry);
1526
1528
1529 vars[nextentry] = squarearg;
1530 ++nextentry;
1531 }
1532 else if( SCIPisExprVar(scip, children[i]) && SCIPvarIsBinary(SCIPgetVarExprVar(children[i])) )
1533 {
1534 /* handle binary variable children: no need to request auxvar */
1535 assert(SCIPhashmapGetImageInt(expr2idx, (void*) children[i]) == nextentry);
1536 vars[nextentry] = children[i];
1537 ++nextentry;
1538 }
1539 else
1540 {
1541 int auxvarpos;
1542
1543 assert(SCIPhashmapExists(expr2idx, (void*) children[i]));
1544 auxvarpos = SCIPhashmapGetImageInt(expr2idx, (void*) children[i]);
1545
1547
1548 offsets[auxvarpos] = 0.5 * childcoefs[i] / transcoefs[auxvarpos];
1549 }
1550 }
1551 assert(nextentry == nvars - 1);
1552
1553#ifdef SCIP_DEBUG
1554 SCIPdebugMsg(scip, "found SOC structure for expression %p\n", (void*)expr);
1555 SCIPprintExpr(scip, expr, NULL);
1556 SCIPinfoMessage(scip, NULL, " <= auxvar\n");
1557#endif
1558
1559 /* create and store nonlinear handler expression data */
1560 SCIP_CALL( createNlhdlrExprData(scip, vars, offsets, transcoefs, transcoefsidx, termbegins,
1561 nvars, nterms, nlhdlrexprdata) );
1562 assert(*nlhdlrexprdata != NULL);
1563
1564 /* free memory */
1565 SCIPhashsetFree(&linexprs, SCIPblkmem(scip) );
1566 SCIPhashmapFree(&expr2idx);
1567 SCIPfreeBufferArray(scip, &termbegins);
1568 SCIPfreeBufferArray(scip, &transcoefsidx);
1569 SCIPfreeBufferArray(scip, &offsets);
1571 SCIPfreeBufferArray(scip, &transcoefs);
1572
1573 return SCIP_OKAY;
1574}
1575
1576/** helper method to detect c + sum_i coef_i expr_i^2 - coef_k expr_k^2 &le; 0
1577 * and c + sum_i coef_i expr_i^2 - coef_k expr_k expr_l &le; 0
1578 *
1579 * binary linear variables are interpreted as quadratic terms
1580 *
1581 * @todo: extend this function to detect c + sum_i coef_i (expr_i + const_i)^2 - ...
1582 * this would probably share a lot of code with detectSocNorm
1583 */
1584static
1586 SCIP* scip, /**< SCIP data structure */
1587 SCIP_EXPR* expr, /**< expression */
1588 SCIP_Real conslhs, /**< lhs of the constraint that the expression defines (or SCIP_INVALID) */
1589 SCIP_Real consrhs, /**< rhs of the constraint that the expression defines (or SCIP_INVALID) */
1590 SCIP_NLHDLREXPRDATA** nlhdlrexprdata, /**< pointer to store nonlinear handler expression data */
1591 SCIP_Bool* enforcebelow, /**< pointer to store whether we enforce <= (TRUE) or >= (FALSE); only valid when success is TRUE */
1592 SCIP_Bool* success /**< pointer to store whether SOC structure has been detected */
1593 )
1594{
1595 SCIP_EXPR** children;
1596 SCIP_EXPR** vars = NULL;
1597 SCIP_Real* childcoefs;
1598 SCIP_Real* offsets = NULL;
1599 SCIP_Real* transcoefs = NULL;
1600 int* transcoefsidx = NULL;
1601 int* termbegins = NULL;
1602 SCIP_Real constant;
1603 SCIP_Real lhsconstant;
1604 SCIP_Real lhs;
1605 SCIP_Real rhs;
1606 SCIP_Real rhssign;
1607 SCIP_INTERVAL expractivity;
1608 int ntranscoefs;
1609 int nposquadterms;
1610 int nnegquadterms;
1611 int nposbilinterms;
1612 int nnegbilinterms;
1613 int rhsidx;
1614 int lhsidx;
1615 int specialtermidx;
1616 int nchildren;
1617 int nnzinterms;
1618 int nterms;
1619 int nvars;
1620 int nextentry;
1621 int i;
1622 SCIP_Bool ishyperbolic;
1623
1624 assert(expr != NULL);
1625 assert(success != NULL);
1626
1627 *success = FALSE;
1628
1629 /* check whether expression is a sum */
1630 if( SCIPisExprSum(scip, expr) )
1631 {
1632 assert(SCIPexprGetNChildren(expr) >= 1);
1633
1634 /* get children of the sum */
1635 children = SCIPexprGetChildren(expr);
1636 nchildren = SCIPexprGetNChildren(expr);
1637 constant = SCIPgetConstantExprSum(expr);
1638
1639 /* we duplicate the child coefficients since we have to manipulate them */
1640 SCIP_CALL( SCIPduplicateBufferArray(scip, &childcoefs, SCIPgetCoefsExprSum(expr), nchildren) ); /*lint !e666*/
1641 }
1642 else if( SCIPisExprProduct(scip, expr) && SCIPexprGetNChildren(expr) == 2 && conslhs != SCIP_INVALID ) /*lint !e777*/
1643 {
1644 /* handle bilinear term as SOC, if we have a constraint like x*y >= constant
1645 * (if conslhs is SCIP_INVALID, then we have not a constraint, but a subexpression)
1646 */
1647 children = &expr;
1648 nchildren = 1;
1649 constant = 0.0;
1650
1651 SCIP_CALL( SCIPallocBufferArray(scip, &childcoefs, 1) );
1652 childcoefs[0] = 1.0;
1653 }
1654 else
1655 {
1656 return SCIP_OKAY;
1657 }
1658
1659 /* initialize data */
1660 lhsidx = -1;
1661 rhsidx = -1;
1662 nposquadterms = 0;
1663 nnegquadterms = 0;
1664 nposbilinterms = 0;
1665 nnegbilinterms = 0;
1666
1667 /* check if all children are quadratic or binary linear and count number of positive and negative terms */
1668 for( i = 0; i < nchildren; ++i )
1669 {
1670 if( SCIPisExprPower(scip, children[i]) && SCIPgetExponentExprPow(children[i]) == 2.0 )
1671 {
1672 if( childcoefs[i] > 0.0 )
1673 {
1674 ++nposquadterms;
1675 lhsidx = i;
1676 }
1677 else
1678 {
1679 ++nnegquadterms;
1680 rhsidx = i;
1681 }
1682 }
1683 else if( SCIPisExprVar(scip, children[i]) && SCIPvarIsBinary(SCIPgetVarExprVar(children[i])) )
1684 {
1685 if( childcoefs[i] > 0.0 )
1686 {
1687 ++nposquadterms;
1688 lhsidx = i;
1689 }
1690 else
1691 {
1692 ++nnegquadterms;
1693 rhsidx = i;
1694 }
1695 }
1696 else if( SCIPisExprProduct(scip, children[i]) && SCIPexprGetNChildren(children[i]) == 2 )
1697 {
1698 if( childcoefs[i] > 0.0 )
1699 {
1700 ++nposbilinterms;
1701 lhsidx = i;
1702 }
1703 else
1704 {
1705 ++nnegbilinterms;
1706 rhsidx = i;
1707 }
1708 }
1709 else
1710 {
1711 goto CLEANUP;
1712 }
1713
1714 /* more than one positive eigenvalue and more than one negative eigenvalue -> can't be convex */
1715 if( nposquadterms > 1 && nnegquadterms > 1 )
1716 goto CLEANUP;
1717
1718 /* more than one bilinear term -> can't be handled by this method */
1719 if( nposbilinterms + nnegbilinterms > 1 )
1720 goto CLEANUP;
1721
1722 /* one positive bilinear term and also at least one positive quadratic term -> not a simple SOC */
1723 if( nposbilinterms > 0 && nposquadterms > 0 )
1724 goto CLEANUP;
1725
1726 /* one negative bilinear term and also at least one negative quadratic term -> not a simple SOC */
1727 if( nnegbilinterms > 0 && nnegquadterms > 0 )
1728 goto CLEANUP;
1729 }
1730
1731 if( nposquadterms == nchildren || nnegquadterms == nchildren )
1732 goto CLEANUP;
1733
1734 assert(nposquadterms <= 1 || nnegquadterms <= 1);
1735 assert(nposbilinterms + nnegbilinterms <= 1);
1736 assert(nposbilinterms == 0 || nposquadterms == 0);
1737 assert(nnegbilinterms == 0 || nnegquadterms == 0);
1738
1739 /* if a bilinear term is involved, it is a hyperbolic expression */
1740 ishyperbolic = (nposbilinterms + nnegbilinterms > 0);
1741
1742 if( conslhs == SCIP_INVALID || consrhs == SCIP_INVALID ) /*lint !e777*/
1743 {
1745 expractivity = SCIPexprGetActivity(expr);
1746
1747 lhs = (conslhs == SCIP_INVALID ? expractivity.inf : conslhs); /*lint !e777*/
1748 rhs = (consrhs == SCIP_INVALID ? expractivity.sup : consrhs); /*lint !e777*/
1749 }
1750 else
1751 {
1752 lhs = conslhs;
1753 rhs = consrhs;
1754 }
1755
1756 /* detect case and store lhs/rhs information */
1757 if( (ishyperbolic && nnegbilinterms > 0) || (!ishyperbolic && nnegquadterms < 2) )
1758 {
1759 /* we have -x*y + z^2 ... -> we want to write z^2 ... <= x*y;
1760 * or we have -x^2 + y^2 ... -> we want to write y^2 ... <= x^2;
1761 * in any case, we need a finite rhs
1762 */
1763 assert(nnegbilinterms == 1 || nnegquadterms == 1);
1764 assert(rhsidx != -1);
1765
1766 /* if rhs is infinity, it can't be soc
1767 * TODO: if it can't be soc, then we should enforce the caller so that we do not try the more complex quadratic
1768 * method
1769 */
1770 if( SCIPisInfinity(scip, rhs) )
1771 goto CLEANUP;
1772
1773 specialtermidx = rhsidx;
1774 lhsconstant = constant - rhs;
1775 *enforcebelow = TRUE; /* enforce expr <= rhs */
1776 }
1777 else
1778 {
1779 /* we have x*y - z^2 ... -> we want to write x*y >= z^2 ...
1780 * or we have x^2 - y^2 - z^2 ... -> we want to write x^2 >= y^2 + z^2 ...
1781 * in any case, we need a finite lhs
1782 */
1783 assert(lhsidx != -1);
1784
1785 /* if lhs is infinity, it can't be soc */
1786 if( SCIPisInfinity(scip, -lhs) )
1787 goto CLEANUP;
1788
1789 specialtermidx = lhsidx;
1790 lhsconstant = lhs - constant;
1791
1792 /* negate all coefficients */
1793 for( i = 0; i < nchildren; ++i )
1794 childcoefs[i] = -childcoefs[i];
1795 *enforcebelow = FALSE; /* enforce lhs <= expr */
1796 }
1797 assert(childcoefs[specialtermidx] != 0.0);
1798
1799 if( ishyperbolic )
1800 {
1801 SCIP_INTERVAL yactivity;
1802 SCIP_INTERVAL zactivity;
1803
1804 assert(SCIPexprGetNChildren(children[specialtermidx]) == 2);
1805
1806 SCIP_CALL( SCIPevalExprActivity(scip, SCIPexprGetChildren(children[specialtermidx])[0]) );
1807 yactivity = SCIPexprGetActivity(SCIPexprGetChildren(children[specialtermidx])[0]);
1808
1809 SCIP_CALL( SCIPevalExprActivity(scip, SCIPexprGetChildren(children[specialtermidx])[1]) );
1810 zactivity = SCIPexprGetActivity(SCIPexprGetChildren(children[specialtermidx])[1]);
1811
1812 if( SCIPisNegative(scip, yactivity.inf + zactivity.inf) )
1813 {
1814 /* the sum of the expressions in the bilinear term changes sign -> no SOC */
1815 if( SCIPisPositive(scip, yactivity.sup + zactivity.sup) )
1816 goto CLEANUP;
1817
1818 rhssign = -1.0;
1819 }
1820 else
1821 rhssign = 1.0;
1822
1823 lhsconstant *= 4.0 / -childcoefs[specialtermidx];
1824 }
1825 else if( SCIPisExprVar(scip, children[specialtermidx]) )
1826 {
1827 /* children[specialtermidx] can be a variable, in which case we treat it as if it is squared */
1828 rhssign = 1.0;
1829 }
1830 else
1831 {
1832 SCIP_INTERVAL rhsactivity;
1833
1834 assert(SCIPexprGetNChildren(children[specialtermidx]) == 1);
1835 SCIP_CALL( SCIPevalExprActivity(scip, SCIPexprGetChildren(children[specialtermidx])[0]) );
1836 rhsactivity = SCIPexprGetActivity(SCIPexprGetChildren(children[specialtermidx])[0]);
1837
1838 if( rhsactivity.inf < 0.0 )
1839 {
1840 /* rhs variable changes sign -> no SOC */
1841 if( rhsactivity.sup > 0.0 )
1842 goto CLEANUP;
1843
1844 rhssign = -1.0;
1845 }
1846 else
1847 rhssign = 1.0;
1848 }
1849
1850 if( SCIPisNegative(scip, lhsconstant) )
1851 goto CLEANUP;
1852
1853 if( SCIPisZero(scip, lhsconstant) )
1854 lhsconstant = 0.0;
1855
1856 /*
1857 * we have found an SOC-representable expression. Now build the nlhdlrexprdata
1858 *
1859 * in the non-hyperbolic case, c + sum_i coef_i expr_i^2 - coef_k expr_k^2 <= 0 is transformed to
1860 * sqrt( c + sum_i coef_i expr_i^2 ) <= coef_k expr_k
1861 * so there are nchildren many vars, nchildren (+ 1 if c != 0) many terms, nchildren many coefficients in the vs
1862 * in SOC representation
1863 *
1864 * in the hyperbolic case, c + sum_i coef_i expr_i^2 - coef_k expr_k expr_l <= 0 is transformed to
1865 * sqrt( 4(c + sum_i coef_i expr_i^2) + (expr_k - expr_l)^2 ) <= expr_k + expr_l
1866 * so there are nchildren + 1many vars, nchildren + 1(+ 1 if c != 0) many terms, nchildren + 3 many coefficients in
1867 * the vs in SOC representation
1868 */
1869
1870 ntranscoefs = ishyperbolic ? nchildren + 3 : nchildren;
1871 nvars = ishyperbolic ? nchildren + 1 : nchildren;
1872 nterms = nvars;
1873
1874 /* constant term */
1875 if( lhsconstant > 0.0 )
1876 nterms++;
1877
1878 /* SOC was detected, allocate temporary memory for data to collect */
1881 SCIP_CALL( SCIPallocBufferArray(scip, &transcoefs, ntranscoefs) );
1882 SCIP_CALL( SCIPallocBufferArray(scip, &transcoefsidx, ntranscoefs) );
1883 SCIP_CALL( SCIPallocBufferArray(scip, &termbegins, nterms + 1) );
1884
1885 *success = TRUE;
1886 nextentry = 0;
1887
1888 /* collect all the v_i and beta_i */
1889 nnzinterms = 0;
1890 for( i = 0; i < nchildren; ++i )
1891 {
1892 /* variable and coef for rhs have to be set to the last entry */
1893 if( i == specialtermidx )
1894 continue;
1895
1896 /* extract (unique) variable appearing in term */
1897 if( SCIPisExprVar(scip, children[i]) )
1898 {
1899 vars[nextentry] = children[i];
1900
1902 }
1903 else
1904 {
1905 assert(SCIPisExprPower(scip, children[i]));
1906
1907 /* notify that we will require auxiliary variable */
1909 vars[nextentry] = SCIPexprGetChildren(children[i])[0];
1910 }
1911 assert(vars[nextentry] != NULL);
1912
1913 /* store v_i and beta_i */
1914 termbegins[nextentry] = nnzinterms;
1915 offsets[nextentry] = 0.0;
1916
1917 transcoefsidx[nnzinterms] = nextentry;
1918 if( ishyperbolic )
1919 {
1920 /* we eliminate the coefficient of the bilinear term to arrive at standard form */
1921 assert(4.0 * childcoefs[i] / -childcoefs[specialtermidx] > 0.0);
1922 transcoefs[nnzinterms] = sqrt(4.0 * childcoefs[i] / -childcoefs[specialtermidx]);
1923 }
1924 else
1925 {
1926 assert(childcoefs[i] > 0.0);
1927 transcoefs[nnzinterms] = sqrt(childcoefs[i]);
1928 }
1929
1930 /* finish adding nonzeros */
1931 ++nnzinterms;
1932
1933 /* finish processing term */
1934 ++nextentry;
1935 }
1936 assert(nextentry == nchildren - 1);
1937
1938 /* store term for constant (v_i = 0) */
1939 if( lhsconstant > 0.0 )
1940 {
1941 termbegins[nextentry] = nnzinterms;
1942 offsets[nextentry] = sqrt(lhsconstant);
1943
1944 /* finish processing term; this term has 0 nonzero thus we do not increase nnzinterms */
1945 ++nextentry;
1946 }
1947
1948 if( !ishyperbolic )
1949 {
1950 /* store rhs term */
1951 if( SCIPisExprVar(scip, children[specialtermidx]) )
1952 {
1953 /* this should be the "children[specialtermidx] can be a variable, in which case we treat it as if it is squared" case */
1954 SCIP_CALL( SCIPregisterExprUsageNonlinear(scip, children[specialtermidx], TRUE, FALSE, FALSE, FALSE) );
1955 vars[nvars - 1] = children[specialtermidx];
1956 }
1957 else
1958 {
1959 assert(SCIPisExprPower(scip, children[specialtermidx]));
1960 assert(SCIPexprGetChildren(children[specialtermidx]) != NULL);
1962 vars[nvars - 1] = SCIPexprGetChildren(children[specialtermidx])[0];
1963 }
1964
1965 assert(childcoefs[specialtermidx] < 0.0);
1966
1967 termbegins[nextentry] = nnzinterms;
1968 offsets[nextentry] = 0.0;
1969 transcoefs[nnzinterms] = rhssign * sqrt(-childcoefs[specialtermidx]);
1970 transcoefsidx[nnzinterms] = nvars - 1;
1971
1972 /* finish adding nonzeros */
1973 ++nnzinterms;
1974
1975 /* finish processing term */
1976 ++nextentry;
1977 }
1978 else
1979 {
1980 /* store last lhs term and rhs term coming from the bilinear term */
1982 vars[nvars - 2] = SCIPexprGetChildren(children[specialtermidx])[0];
1983
1985 vars[nvars - 1] = SCIPexprGetChildren(children[specialtermidx])[1];
1986
1987 /* at this point, vars[nvars - 2] = expr_k and vars[nvars - 1] = expr_l;
1988 * on the lhs we have the term (expr_k - expr_l)^2
1989 */
1990 termbegins[nextentry] = nnzinterms;
1991 offsets[nextentry] = 0.0;
1992
1993 /* expr_k */
1994 transcoefsidx[nnzinterms] = nvars - 2;
1995 transcoefs[nnzinterms] = 1.0;
1996 ++nnzinterms;
1997
1998 /* - expr_l */
1999 transcoefsidx[nnzinterms] = nvars - 1;
2000 transcoefs[nnzinterms] = -1.0;
2001 ++nnzinterms;
2002
2003 /* finish processing term */
2004 ++nextentry;
2005
2006 /* on rhs we have +/-(expr_k + expr_l) */
2007 termbegins[nextentry] = nnzinterms;
2008 offsets[nextentry] = 0.0;
2009
2010 /* rhssing * expr_k */
2011 transcoefsidx[nnzinterms] = nvars - 2;
2012 transcoefs[nnzinterms] = rhssign;
2013 ++nnzinterms;
2014
2015 /* rhssing * expr_l */
2016 transcoefsidx[nnzinterms] = nvars - 1;
2017 transcoefs[nnzinterms] = rhssign;
2018 ++nnzinterms;
2019
2020 /* finish processing term */
2021 ++nextentry;
2022 }
2023 assert(nextentry == nterms);
2024 assert(nnzinterms == ntranscoefs);
2025
2026 /* sentinel value */
2027 termbegins[nextentry] = nnzinterms;
2028
2029#ifdef SCIP_DEBUG
2030 SCIPdebugMsg(scip, "found SOC structure for expression %p\n %g <= ", (void*)expr, lhs);
2031 SCIPprintExpr(scip, expr, NULL);
2032 SCIPinfoMessage(scip, NULL, " <= %g\n", rhs);
2033#endif
2034
2035 /* create and store nonlinear handler expression data */
2036 SCIP_CALL( createNlhdlrExprData(scip, vars, offsets, transcoefs, transcoefsidx, termbegins, nvars, nterms,
2037 nlhdlrexprdata) );
2038 assert(*nlhdlrexprdata != NULL);
2039
2040CLEANUP:
2041 SCIPfreeBufferArrayNull(scip, &termbegins);
2042 SCIPfreeBufferArrayNull(scip, &transcoefsidx);
2043 SCIPfreeBufferArrayNull(scip, &transcoefs);
2044 SCIPfreeBufferArrayNull(scip, &offsets);
2046 SCIPfreeBufferArrayNull(scip, &childcoefs);
2047
2048 return SCIP_OKAY;
2049}
2050
2051/** detects complex quadratic expressions that can be represented as SOC constraints
2052 *
2053 * These are quadratic expressions with either exactly one positive or exactly one negative eigenvalue,
2054 * in addition to some extra conditions. One needs to write the quadratic as
2055 * sum eigval_i (eigvec_i . x)^2 + c &le; -eigval_k (eigvec_k . x)^2, where eigval_k is the negative eigenvalue,
2056 * and c must be positive and (eigvec_k . x) must not change sign.
2057 * This is described in more details in
2058 * Mahajan, Ashutosh & Munson, Todd, Exploiting Second-Order Cone Structure for Global Optimization, 2010.
2059 *
2060 * The eigen-decomposition is computed using Lapack.
2061 * Binary linear variables are interpreted as quadratic terms.
2062 *
2063 * @todo: In the case -b <= a + x^2 - y^2 <= b, it is possible to represent both sides by SOC. Currently, the
2064 * datastructure can only handle one SOC. If this should appear more often, it could be worth to extend it,
2065 * such that both sides can be handled (see e.g. instance chp_partload).
2066 * FS: this shouldn't be possible. For a <= b + x^2 - y^2 <= c to be SOC representable on both sides, we would need
2067 * that a - b >= 0 and b -c >= 0, but this implies that a >= c and assuming the constraint is not trivially infeasible,
2068 * a <= b. Thus, a = b = c and the constraint is x^2 == y^2.
2069 *
2070 * @todo: Since cons_nonlinear multiplies as many terms out as possible during presolving, some SOC-representable
2071 * structures cannot be detected, (see e.g. instances bearing or wager). There is currently no obvious way
2072 * to handle this.
2073 */
2074static
2076 SCIP* scip, /**< SCIP data structure */
2077 SCIP_EXPR* expr, /**< expression */
2078 SCIP_Real conslhs, /**< lhs of the constraint that the expression defines (or SCIP_INVALID) */
2079 SCIP_Real consrhs, /**< rhs of the constraint that the expression defines (or SCIP_INVALID) */
2080 SCIP_NLHDLREXPRDATA** nlhdlrexprdata, /**< pointer to store nonlinear handler expression data */
2081 SCIP_Bool* enforcebelow, /**< pointer to store whether we enforce <= (TRUE) or >= (FALSE); only
2082 * valid when success is TRUE */
2083 SCIP_Bool* success /**< pointer to store whether SOC structure has been detected */
2084 )
2085{
2086 SCIP_EXPR** occurringexprs;
2087 SCIP_HASHMAP* expr2idx;
2088 SCIP_Real* offsets;
2089 SCIP_Real* transcoefs;
2090 SCIP_Real* eigvecmatrix;
2091 SCIP_Real* eigvals;
2092 SCIP_Real* lincoefs;
2093 SCIP_Real* bp;
2094 int* transcoefsidx;
2095 int* termbegins;
2096 SCIP_Real constant;
2097 SCIP_Real lhsconstant;
2098 SCIP_Real lhs;
2099 SCIP_Real rhs;
2100 SCIP_INTERVAL expractivity;
2101 int nvars;
2102 int nterms;
2103 int nchildren;
2104 int npos;
2105 int nneg;
2106 int ntranscoefs;
2107 int i;
2108 int j;
2109 SCIP_Bool rhsissoc;
2110 SCIP_Bool lhsissoc;
2111 SCIP_Bool isquadratic;
2112
2113 assert(expr != NULL);
2114 assert(success != NULL);
2115
2116 *success = FALSE;
2117
2118 /* check whether expression is a sum with at least 2 children */
2119 if( ! SCIPisExprSum(scip, expr) || SCIPexprGetNChildren(expr) < 2 )
2120 {
2121 return SCIP_OKAY;
2122 }
2123
2124 /* we need Lapack to compute eigenvalues/vectors below */
2125 if( ! SCIPlapackIsAvailable() )
2126 return SCIP_OKAY;
2127
2128 /* get children of the sum */
2129 nchildren = SCIPexprGetNChildren(expr);
2130 constant = SCIPgetConstantExprSum(expr);
2131
2132 /* initialize data */
2133 offsets = NULL;
2134 transcoefs = NULL;
2135 transcoefsidx = NULL;
2136 termbegins = NULL;
2137 bp = NULL;
2138
2139 SCIP_CALL( SCIPhashmapCreate(&expr2idx, SCIPblkmem(scip), 2 * nchildren) );
2140 SCIP_CALL( SCIPallocBufferArray(scip, &occurringexprs, 2 * nchildren) );
2141
2142 /* check if the expression is quadratic and collect all occurring expressions */
2143 SCIP_CALL( checkAndCollectQuadratic(scip, expr, expr2idx, occurringexprs, &nvars, &isquadratic) );
2144
2145 if( !isquadratic )
2146 {
2147 SCIPfreeBufferArray(scip, &occurringexprs);
2148 SCIPhashmapFree(&expr2idx);
2149 return SCIP_OKAY;
2150 }
2151
2152 /* check that nvars*nvars doesn't get too large, see also SCIPcomputeExprQuadraticCurvature() */
2153 if( nvars > 23000 )
2154 {
2155 SCIPverbMessage(scip, SCIP_VERBLEVEL_FULL, NULL, "nlhdlr_soc - number of quadratic variables is too large (%d) to check the curvature\n", nvars);
2156 SCIPfreeBufferArray(scip, &occurringexprs);
2157 SCIPhashmapFree(&expr2idx);
2158 return SCIP_OKAY;
2159 }
2160
2161 assert(SCIPhashmapGetNElements(expr2idx) == nvars);
2162
2163 /* create datastructures for constaint defining matrix and vector */
2164 SCIP_CALL( SCIPallocClearBufferArray(scip, &eigvecmatrix, nvars * nvars) ); /*lint !e647*/
2166
2167 /* build constraint defining matrix (stored in eigvecmatrix) and vector (stored in lincoefs) */
2168 buildQuadExprMatrix(scip, expr, expr2idx, nvars, eigvecmatrix, lincoefs);
2169
2171
2172 /* compute eigenvalues and vectors, A = PDP^t
2173 * note: eigvecmatrix stores P^t, i.e., P^t_{i,j} = eigvecmatrix[i*nvars+j]
2174 */
2175 if( SCIPlapackComputeEigenvalues(SCIPbuffer(scip), TRUE, nvars, eigvecmatrix, eigvals) != SCIP_OKAY )
2176 {
2177 SCIPdebugMsg(scip, "Failed to compute eigenvalues and eigenvectors for expression:\n");
2178
2179#ifdef SCIP_DEBUG
2180 SCIPdismantleExpr(scip, NULL, expr);
2181#endif
2182
2183 goto CLEANUP;
2184 }
2185
2187
2188 nneg = 0;
2189 npos = 0;
2190 ntranscoefs = 0;
2191
2192 /* set small eigenvalues to 0 and compute b*P */
2193 for( i = 0; i < nvars; ++i )
2194 {
2195 for( j = 0; j < nvars; ++j )
2196 {
2197 bp[i] += lincoefs[j] * eigvecmatrix[i * nvars + j];
2198
2199 /* count the number of transcoefs to be used later */
2200 if( !SCIPisZero(scip, eigvals[i]) && !SCIPisZero(scip, eigvecmatrix[i * nvars + j]) )
2201 ++ntranscoefs;
2202 }
2203
2204 if( SCIPisZero(scip, eigvals[i]) )
2205 {
2206 /* if there is a purely linear variable, the constraint can't be written as a SOC */
2207 if( !SCIPisZero(scip, bp[i]) )
2208 goto CLEANUP;
2209
2210 bp[i] = 0.0;
2211 eigvals[i] = 0.0;
2212 }
2213 else if( eigvals[i] > 0.0 )
2214 npos++;
2215 else
2216 nneg++;
2217 }
2218
2219 /* a proper SOC constraint needs at least 2 variables */
2220 if( npos + nneg < 2 )
2221 goto CLEANUP;
2222
2223 /* determine whether rhs or lhs of cons is potentially SOC, if any */
2224 rhsissoc = (nneg == 1 && SCIPgetExprNLocksPosNonlinear(expr) > 0);
2225 lhsissoc = (npos == 1 && SCIPgetExprNLocksNegNonlinear(expr) > 0);
2226
2227 if( rhsissoc || lhsissoc )
2228 {
2229 if( conslhs == SCIP_INVALID || consrhs == SCIP_INVALID ) /*lint !e777*/
2230 {
2232 expractivity = SCIPexprGetActivity(expr);
2233 lhs = (conslhs == SCIP_INVALID ? expractivity.inf : conslhs); /*lint !e777*/
2234 rhs = (consrhs == SCIP_INVALID ? expractivity.sup : consrhs); /*lint !e777*/
2235 }
2236 else
2237 {
2238 lhs = conslhs;
2239 rhs = consrhs;
2240 }
2241 }
2242 else
2243 {
2244 /* if none of the sides is potentially SOC, stop */
2245 goto CLEANUP;
2246 }
2247
2248 /* @TODO: what do we do if both sides are possible? */
2249 if( !rhsissoc )
2250 {
2251 assert(lhsissoc);
2252
2253 /* lhs is potentially SOC, change signs */
2254 lhsconstant = lhs - constant; /*lint !e644*/
2255
2256 for( i = 0; i < nvars; ++i )
2257 {
2258 eigvals[i] = -eigvals[i];
2259 bp[i] = -bp[i];
2260 }
2261 *enforcebelow = FALSE; /* enforce lhs <= expr */
2262 }
2263 else
2264 {
2265 lhsconstant = constant - rhs; /*lint !e644*/
2266 *enforcebelow = TRUE; /* enforce expr <= rhs */
2267 }
2268
2269 /* initialize remaining datastructures for nonlinear handler */
2270 SCIP_CALL( SCIPallocBufferArray(scip, &offsets, npos + nneg + 1) );
2271 SCIP_CALL( SCIPallocBufferArray(scip, &transcoefs, ntranscoefs) );
2272 SCIP_CALL( SCIPallocBufferArray(scip, &transcoefsidx, ntranscoefs) );
2273 SCIP_CALL( SCIPallocBufferArray(scip, &termbegins, npos + nneg + 2) );
2274
2275 /* try to fill the nlhdlrexprdata (at this point, it can still fail) */
2276 SCIP_CALL( tryFillNlhdlrExprDataQuad(scip, occurringexprs, eigvecmatrix, eigvals, bp, nvars, termbegins, transcoefs,
2277 transcoefsidx, offsets, &lhsconstant, &nterms, success) );
2278
2279 if( !(*success) )
2280 goto CLEANUP;
2281
2282 assert(0 < nterms && nterms <= npos + nneg + 1);
2283 assert(ntranscoefs == termbegins[nterms]);
2284
2285 /*
2286 * at this point, the expression passed all checks and is SOC-representable
2287 */
2288
2289 /* register all requests for auxiliary variables */
2290 for( i = 0; i < nvars; ++i )
2291 {
2293 }
2294
2295#ifdef SCIP_DEBUG
2296 SCIPdebugMsg(scip, "found SOC structure for expression %p\n%f <= ", (void*)expr, lhs);
2297 SCIPprintExpr(scip, expr, NULL);
2298 SCIPinfoMessage(scip, NULL, "<= %f\n", rhs);
2299#endif
2300
2301 /* finally, create and store nonlinear handler expression data */
2302 SCIP_CALL( createNlhdlrExprData(scip, occurringexprs, offsets, transcoefs, transcoefsidx, termbegins, nvars, nterms,
2303 nlhdlrexprdata) );
2304 assert(*nlhdlrexprdata != NULL);
2305
2306CLEANUP:
2307 SCIPfreeBufferArrayNull(scip, &termbegins);
2308 SCIPfreeBufferArrayNull(scip, &transcoefsidx);
2309 SCIPfreeBufferArrayNull(scip, &transcoefs);
2310 SCIPfreeBufferArrayNull(scip, &offsets);
2312 SCIPfreeBufferArray(scip, &eigvals);
2313 SCIPfreeBufferArray(scip, &lincoefs);
2314 SCIPfreeBufferArray(scip, &eigvecmatrix);
2315 SCIPfreeBufferArray(scip, &occurringexprs);
2316 SCIPhashmapFree(&expr2idx);
2317
2318 return SCIP_OKAY;
2319}
2320
2321/** helper method to detect SOC structures
2322 *
2323 * The detection runs in 3 steps:
2324 * 1. check if expression is a norm of the form \f$\sqrt{\sum_i (\text{sqrcoef}_i\, \text{expr}_i^2 + \text{lincoef}_i\, \text{expr}_i) + c}\f$
2325 * which can be transformed to the form \f$\sqrt{\sum_i (\text{coef}_i \text{expr}_i + \text{const}_i)^2 + c^*}\f$ with \f$c^* \geq 0\f$.\n
2326 * -> this results in the SOC expr &le; auxvar(expr)
2327 *
2328 * TODO we should generalize and check for sqrt(positive-semidefinite-quadratic)
2329 *
2330 * 2. check if expression represents a quadratic function of one of the following forms (all coefs > 0)
2331 * 1. \f$(\sum_i \text{coef}_i \text{expr}_i^2) - \text{coef}_k \text{expr}_k^2 \leq \text{RHS}\f$ or
2332 * 2. \f$(\sum_i - \text{coef}_i \text{expr}_i^2) + \text{coef}_k \text{expr}_k^2 \geq \text{LHS}\f$ or
2333 * 3. \f$(\sum_i \text{coef}_i \text{expr}_i^2) - \text{coef}_k \text{expr}_k \text{expr}_l \leq \text{RHS}\f$ or
2334 * 4. \f$(\sum_i - \text{coef}_i \text{expr}_i^2) + \text{coef}_k \text{expr}_k \text{expr}_l \geq \text{LHS}\f$,
2335 *
2336 * where RHS &ge; 0 or LHS &le; 0, respectively. For LHS and RHS we use the constraint sides if it is a root expr
2337 * and the bounds of the auxiliary variable otherwise.
2338 * The last two cases are called hyperbolic or rotated second order cone.\n
2339 * -> this results in the SOC \f$\sqrt{(\sum_i \text{coef}_i \text{expr}_i^2) - \text{RHS}} \leq \sqrt{\text{coef}_k} \text{expr}_k\f$
2340 * or \f$\sqrt{4(\sum_i \text{coef}_i \text{expr}_i^2) - 4\text{RHS} + (\text{expr}_k - \text{expr}_l)^2)} \leq \text{expr}_k + \text{expr}_l\f$.
2341 * (analogously for the LHS cases)
2342 *
2343 * 3. check if expression represents a quadratic inequality of the form \f$f(x) = x^TAx + b^Tx + c \leq 0\f$ such that \f$f(x)\f$
2344 * has exactly one negative eigenvalue plus some extra conditions, see detectSocQuadraticComplex().
2345 *
2346 * Note that step 3 is only performed if parameter `compeigenvalues` is set to TRUE.
2347 */
2348static
2350 SCIP* scip, /**< SCIP data structure */
2351 SCIP_NLHDLRDATA* nlhdlrdata, /**< nonlinear handler data */
2352 SCIP_EXPR* expr, /**< expression */
2353 SCIP_Real conslhs, /**< lhs of the constraint that the expression defines (or SCIP_INVALID) */
2354 SCIP_Real consrhs, /**< rhs of the constraint that the expression defines (or SCIP_INVALID) */
2355 SCIP_NLHDLREXPRDATA** nlhdlrexprdata, /**< pointer to store nonlinear handler expression data */
2356 SCIP_Bool* enforcebelow, /**< pointer to store whether we enforce <= (TRUE) or >= (FALSE); only
2357 * valid when success is TRUE */
2358 SCIP_Bool* success /**< pointer to store whether SOC structure has been detected */
2359 )
2360{
2361 assert(expr != NULL);
2362 assert(nlhdlrdata != NULL);
2363 assert(nlhdlrexprdata != NULL);
2364 assert(success != NULL);
2365
2366 *success = FALSE;
2367
2368 /* check whether expression is given as norm as described in case 1 above: if we have a constraint
2369 * sqrt(sum x_i^2) <= constant, then it might be better not to handle this here; thus, we only call detectSocNorm
2370 * when the expr is _not_ the root of a constraint
2371 */
2372 if( conslhs == SCIP_INVALID && consrhs == SCIP_INVALID ) /*lint !e777*/
2373 {
2374 SCIP_CALL( detectSocNorm(scip, expr, nlhdlrexprdata, success) );
2375 *enforcebelow = *success;
2376 }
2377
2378 if( !(*success) )
2379 {
2380 /* check whether expression is a simple soc-respresentable quadratic expression as described in case 2 above */
2381 SCIP_CALL( detectSocQuadraticSimple(scip, expr, conslhs, consrhs, nlhdlrexprdata, enforcebelow, success) );
2382 }
2383
2384 if( !(*success) && nlhdlrdata->compeigenvalues )
2385 {
2386 /* check whether expression is a more complex soc-respresentable quadratic expression as described in case 3 */
2387 SCIP_CALL( detectSocQuadraticComplex(scip, expr, conslhs, consrhs, nlhdlrexprdata, enforcebelow, success) );
2388 }
2389
2390 return SCIP_OKAY;
2391}
2392
2393/*
2394 * Callback methods of nonlinear handler
2395 */
2396
2397/** nonlinear handler copy callback */
2398static
2400{ /*lint --e{715}*/
2401 assert(targetscip != NULL);
2402 assert(sourcenlhdlr != NULL);
2403
2405
2406 SCIP_CALL( SCIPincludeNlhdlrSoc(targetscip) );
2407
2408 return SCIP_OKAY;
2409}
2410
2411/** callback to free data of handler */
2412static
2413SCIP_DECL_NLHDLRFREEHDLRDATA(nlhdlrFreehdlrdataSoc)
2414{ /*lint --e{715}*/
2415 assert(nlhdlrdata != NULL);
2416
2417 SCIPfreeBlockMemory(scip, nlhdlrdata);
2418
2419 return SCIP_OKAY;
2420}
2421
2422/** callback to free expression specific data */
2423static
2424SCIP_DECL_NLHDLRFREEEXPRDATA(nlhdlrFreeExprDataSoc)
2425{ /*lint --e{715}*/
2426 assert(*nlhdlrexprdata != NULL);
2427
2428 SCIP_CALL( freeNlhdlrExprData(scip, nlhdlrexprdata) );
2429
2430 return SCIP_OKAY;
2431}
2432
2433/** callback to detect structure in expression tree */
2434static
2436{ /*lint --e{715}*/
2437 SCIP_Real conslhs;
2438 SCIP_Real consrhs;
2439 SCIP_Bool enforcebelow;
2440 SCIP_Bool success;
2441 SCIP_NLHDLRDATA* nlhdlrdata;
2442
2443 assert(expr != NULL);
2444
2445 /* don't try if no sepa is required
2446 * TODO implement some bound strengthening
2447 */
2449 return SCIP_OKAY;
2450
2451 assert(SCIPgetExprNAuxvarUsesNonlinear(expr) > 0); /* since some sepa is required, there should have been demand for it */
2452
2453 nlhdlrdata = SCIPnlhdlrGetData(nlhdlr);
2454 assert(nlhdlrdata != NULL);
2455
2456 conslhs = (cons == NULL ? SCIP_INVALID : SCIPgetLhsNonlinear(cons));
2457 consrhs = (cons == NULL ? SCIP_INVALID : SCIPgetRhsNonlinear(cons));
2458
2459 SCIP_CALL( detectSOC(scip, nlhdlrdata, expr, conslhs, consrhs, nlhdlrexprdata, &enforcebelow, &success) );
2460
2461 if( !success )
2462 return SCIP_OKAY;
2463
2464 /* inform what we can do */
2465 *participating = enforcebelow ? SCIP_NLHDLR_METHOD_SEPABELOW : SCIP_NLHDLR_METHOD_SEPAABOVE;
2466
2467 /*
2468 */
2469 if( SCIPisExprPower(scip, expr) && SCIPgetExponentExprPow(expr) == 0.5 )
2470 {
2471 /* if we have been successful on sqrt(...) <= auxvar, then we enforce */
2472 *enforcing |= *participating;
2473 }
2474 else if( cons != NULL )
2475 {
2476 /* expr is quadratic (product or sum) and we separate for expr <= ub(auxvar) or expr >= lb(auxvar) only
2477 * in that case, we enforce only if expr is the root of a constraint, since then replacing auxvar by up(auxvar) does not relax anything (auxvar <= ub(auxvar) is the only constraint on auxvar)
2478 * however, if the constraint has both lhs and rhs and has only one bilinear term (x*y=constant), then it seems that handling both sides by nlhdlr_bilinear can still be beneficial
2479 * (the latter means lhs or rhs disappear, or expr is not a product and not a sum with only 1 term)
2480 */
2482 *enforcing |= *participating;
2483 else if( !SCIPisExprProduct(scip, expr) && SCIPexprGetNChildren(expr) > 1 )
2484 *enforcing |= *participating;
2485 }
2486
2487 return SCIP_OKAY;
2488}
2489
2490
2491/** auxiliary evaluation callback of nonlinear handler
2492 * @todo: remember if we are in the original variables and avoid reevaluating
2493 */
2494static
2496{ /*lint --e{715}*/
2497 int i;
2498
2499 assert(nlhdlrexprdata != NULL);
2500 assert(nlhdlrexprdata->vars != NULL);
2501 assert(nlhdlrexprdata->transcoefs != NULL);
2502 assert(nlhdlrexprdata->transcoefsidx != NULL);
2503 assert(nlhdlrexprdata->nterms > 1);
2504
2505 /* if the original expression is a norm, evaluate w.r.t. the auxiliary variables */
2506 if( SCIPisExprPower(scip, expr) )
2507 {
2508 assert(SCIPgetExponentExprPow(expr) == 0.5);
2509
2510 updateVarVals(scip, nlhdlrexprdata, sol, FALSE);
2511
2512 /* compute sum_i coef_i expr_i^2 */
2513 *auxvalue = 0.0;
2514 for( i = 0; i < nlhdlrexprdata->nterms - 1; ++i )
2515 {
2516 SCIP_Real termval;
2517
2518 termval = evalSingleTerm(scip, nlhdlrexprdata, i);
2519 *auxvalue += SQR(termval);
2520 }
2521
2522 assert(*auxvalue >= 0.0);
2523
2524 /* compute sqrt(sum_i coef_i expr_i^2) */
2525 *auxvalue = sqrt(*auxvalue);
2526 }
2527 /* otherwise, evaluate the original quadratic expression w.r.t. the created auxvars of the children */
2528 else if( SCIPisExprSum(scip, expr) )
2529 {
2530 SCIP_EXPR** children;
2531 SCIP_Real* childcoefs;
2532 int nchildren;
2533
2534 assert(SCIPisExprSum(scip, expr));
2535
2536 children = SCIPexprGetChildren(expr);
2537 childcoefs = SCIPgetCoefsExprSum(expr);
2538 nchildren = SCIPexprGetNChildren(expr);
2539
2540 *auxvalue = SCIPgetConstantExprSum(expr);
2541
2542 for( i = 0; i < nchildren; ++i )
2543 {
2544 if( SCIPisExprPower(scip, children[i]) )
2545 {
2546 SCIP_VAR* argauxvar;
2547 SCIP_Real solval;
2548
2549 assert(SCIPgetExponentExprPow(children[i]) == 2.0);
2550
2551 argauxvar = SCIPgetExprAuxVarNonlinear(SCIPexprGetChildren(children[i])[0]);
2552 assert(argauxvar != NULL);
2553
2554 solval = SCIPgetSolVal(scip, sol, argauxvar);
2555 *auxvalue += childcoefs[i] * SQR( solval );
2556 }
2557 else if( SCIPisExprProduct(scip, children[i]) )
2558 {
2559 SCIP_VAR* argauxvar1;
2560 SCIP_VAR* argauxvar2;
2561
2562 assert(SCIPexprGetNChildren(children[i]) == 2);
2563
2564 argauxvar1 = SCIPgetExprAuxVarNonlinear(SCIPexprGetChildren(children[i])[0]);
2565 argauxvar2 = SCIPgetExprAuxVarNonlinear(SCIPexprGetChildren(children[i])[1]);
2566 assert(argauxvar1 != NULL);
2567 assert(argauxvar2 != NULL);
2568
2569 *auxvalue += childcoefs[i] * SCIPgetSolVal(scip, sol, argauxvar1) * SCIPgetSolVal(scip, sol, argauxvar2);
2570 }
2571 else
2572 {
2573 SCIP_VAR* argauxvar;
2574
2575 argauxvar = SCIPgetExprAuxVarNonlinear(children[i]);
2576 assert(argauxvar != NULL);
2577
2578 *auxvalue += childcoefs[i] * SCIPgetSolVal(scip, sol, argauxvar);
2579 }
2580 }
2581 }
2582 else
2583 {
2584 SCIP_VAR* argauxvar1;
2585 SCIP_VAR* argauxvar2;
2586
2588 assert(SCIPexprGetNChildren(expr) == 2);
2589
2590 argauxvar1 = SCIPgetExprAuxVarNonlinear(SCIPexprGetChildren(expr)[0]);
2591 argauxvar2 = SCIPgetExprAuxVarNonlinear(SCIPexprGetChildren(expr)[1]);
2592 assert(argauxvar1 != NULL);
2593 assert(argauxvar2 != NULL);
2594
2595 *auxvalue = SCIPgetSolVal(scip, sol, argauxvar1) * SCIPgetSolVal(scip, sol, argauxvar2);
2596 }
2597
2598 return SCIP_OKAY;
2599}
2600
2601
2602/** separation deinitialization method of a nonlinear handler (called during CONSINITLP) */
2603static
2605{ /*lint --e{715}*/
2606 SCIP_ROWPREP* rowprep;
2607
2608 assert(conshdlr != NULL);
2609 assert(expr != NULL);
2610 assert(nlhdlrexprdata != NULL);
2611
2612 /* already needed for debug solution */
2613 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &nlhdlrexprdata->varvals, nlhdlrexprdata->nvars) );
2614
2615 /* if we have 3 or more terms in lhs create variable and row for disaggregation */
2616 if( nlhdlrexprdata->nterms > 3 )
2617 {
2618 /* create variables for cone disaggregation */
2619 SCIP_CALL( createDisaggrVars(scip, expr, nlhdlrexprdata) );
2620
2621#ifdef WITH_DEBUG_SOLUTION
2622 if( SCIPdebugIsMainscip(scip) )
2623 {
2624 SCIP_Real lhsval;
2625 SCIP_Real rhsval;
2626 SCIP_Real disvarval;
2627 int ndisvars;
2628 int nterms;
2629 int i;
2630
2631 for( i = 0; i < nlhdlrexprdata->nvars; ++i )
2632 {
2633 SCIP_CALL( SCIPdebugGetSolVal(scip, SCIPgetExprAuxVarNonlinear(nlhdlrexprdata->vars[i]), &nlhdlrexprdata->varvals[i]) );
2634 }
2635
2636 /* the debug solution value of the disaggregation variables is set to
2637 * (v_i^T x + beta_i)^2 / (v_{n+1}^T x + beta_{n+1})
2638 * if (v_{n+1}^T x + beta_{n+1}) is different from 0.
2639 * Otherwise, the debug solution value is set to 0.
2640 */
2641
2642 nterms = nlhdlrexprdata->nterms;
2643
2644 /* find value of rhs */
2645 rhsval = evalSingleTerm(scip, nlhdlrexprdata, nterms - 1);
2646
2647 /* set value of disaggregation vars */
2648 ndisvars = nlhdlrexprdata->nterms - 1;
2649
2650 if( SCIPisZero(scip, rhsval) )
2651 {
2652 for( i = 0; i < ndisvars; ++i )
2653 {
2654 SCIP_CALL( SCIPdebugAddSolVal(scip, nlhdlrexprdata->disvars[i], 0.0) );
2655 }
2656 }
2657 else
2658 {
2659 /* set value for each disaggregation variable corresponding to quadratic term */
2660 for( i = 0; i < ndisvars; ++i )
2661 {
2662 lhsval = evalSingleTerm(scip, nlhdlrexprdata, i);
2663
2664 disvarval = SQR(lhsval) / rhsval;
2665
2666 SCIP_CALL( SCIPdebugAddSolVal(scip, nlhdlrexprdata->disvars[i], disvarval) );
2667 }
2668 }
2669 }
2670#endif
2671
2672 /* create the disaggregation row and store it in nlhdlrexprdata */
2673 SCIP_CALL( createDisaggrRow(scip, conshdlr, expr, nlhdlrexprdata) );
2674 }
2675
2676#ifdef SCIP_DEBUG
2677 SCIPdebugMsg(scip, "initlp for \n");
2678 printNlhdlrExprData(scip, nlhdlrexprdata);
2679#endif
2680
2681 /* add some initial cuts on well-selected coordinates */
2682 if( nlhdlrexprdata->nterms == 2 )
2683 {
2684 /* we have |v_1^T x + \beta_1| \leq v_2^T x + \beta_2
2685 *
2686 * we should linearize at points where the first term is -1 or 1, so we can take
2687 *
2688 * x = v_1 / ||v_1||^2 (+/-1 - beta_1)
2689 */
2690 SCIP_Real plusminus1;
2691 SCIP_Real norm;
2692 int i;
2693
2694 /* calculate ||v_1||^2 */
2695 norm = 0.0;
2696 for( i = nlhdlrexprdata->termbegins[0]; i < nlhdlrexprdata->termbegins[1]; ++i )
2697 norm += SQR(nlhdlrexprdata->transcoefs[i]);
2698 assert(norm > 0.0);
2699
2700 BMSclearMemoryArray(nlhdlrexprdata->varvals, nlhdlrexprdata->nvars);
2701
2702 for( plusminus1 = -1.0; plusminus1 <= 1.0; plusminus1 += 2.0 )
2703 {
2704 /* set x = v_1 / ||v_1||^2 (plusminus1 - beta_1) */
2705 for( i = nlhdlrexprdata->termbegins[0]; i < nlhdlrexprdata->termbegins[1]; ++i )
2706 nlhdlrexprdata->varvals[nlhdlrexprdata->transcoefsidx[i]] = nlhdlrexprdata->transcoefs[i] / norm * (plusminus1 - nlhdlrexprdata->offsets[0]);
2707 assert(SCIPisEQ(scip, evalSingleTerm(scip, nlhdlrexprdata, 0), plusminus1));
2708
2709 /* compute gradient cut */
2710 SCIP_CALL( generateCutSolSOC(scip, &rowprep, expr, cons, nlhdlrexprdata, -SCIPinfinity(scip), evalSingleTerm(scip, nlhdlrexprdata, 1)) );
2711
2712 if( rowprep != NULL )
2713 {
2714 SCIP_Bool success = FALSE;
2715
2716 SCIP_CALL( SCIPcleanupRowprep2(scip, rowprep, NULL, SCIPgetHugeValue(scip), &success) );
2717 if( success )
2718 {
2719 SCIP_ROW* cut;
2720 SCIP_CALL( SCIPgetRowprepRowCons(scip, &cut, rowprep, cons) );
2721 SCIP_CALL( SCIPaddRow(scip, cut, FALSE, infeasible) );
2722 SCIP_CALL( SCIPreleaseRow(scip, &cut) );
2723 }
2724
2725 SCIPfreeRowprep(scip, &rowprep);
2726 }
2727
2728 if( *infeasible )
2729 return SCIP_OKAY;
2730 }
2731 }
2732 else if( nlhdlrexprdata->nterms == 3 && nlhdlrexprdata->termbegins[0] != nlhdlrexprdata->termbegins[1] )
2733 {
2734 /* we have \sqrt{ (v_1^T x + \beta_1)^2 + (v_2^T x + \beta_2)^2 } \leq v_3^T x + \beta_3
2735 * with v_1 != 0
2736 *
2737 * we should linearize at points where the first and second term are (-1,0), (1,1), (1,-1), i.e.,
2738 *
2739 * v_1^T x + \beta_1 = -1 1 1
2740 * v_2^T x + \beta_2 = 0 1 -1
2741 *
2742 * Let i be the index of the first nonzero element in v_1.
2743 * Let j != i be an index of v_2, to be determined.
2744 * Assume all other entries of x will be set to 0.
2745 * Then we have
2746 * (v_1)_i x_i + (v_1)_j x_j = c with c = -1 - beta_1
2747 * (v_2)_i x_i + (v_2)_j x_j = d with d = 0 - beta_2
2748 *
2749 * Since (v_1)_i != 0, this gives
2750 * x_i = 1/(v_1)_i (c - (v_1)_j x_j)
2751 * Substituting in the 2nd equation, we get
2752 * (v_2)_i/(v_1)_i (c - (v_1)_j x_j) + (v_2)_j x_j = d
2753 * -> ((v_2)_j - (v_2)_i (v_1)_j / (v_1)_i) x_j = d - (v_2)_i/(v_1)_i c
2754 * Now find j such that (v_2)_j - (v_2)_i (v_1)_j / (v_1)_i != 0.
2755 *
2756 * If v_2 = 0, then linearize only for first term being -1 or 1 and don't care about value of second term.
2757 * We then set j arbitrary, x_i = 1/(v_1)_i c, other coordinates of x = zero.
2758 */
2759 static const SCIP_Real refpoints[3][2] = { {-1.0, 0.0}, {1.0, 1.0}, {1.0, -1.0} };
2760 SCIP_Real v1i, v1j = 0.0;
2761 SCIP_Real v2i, v2j = 0.0;
2762 SCIP_Bool v2zero;
2763 int i;
2764 int j = -1;
2765 int k;
2766 int pos;
2767
2768 i = nlhdlrexprdata->transcoefsidx[nlhdlrexprdata->termbegins[0]];
2769 v1i = nlhdlrexprdata->transcoefs[nlhdlrexprdata->termbegins[0]];
2770 assert(v1i != 0.0);
2771
2772 v2zero = nlhdlrexprdata->termbegins[1] == nlhdlrexprdata->termbegins[2];
2773
2774 /* get (v_2)_i; as it is a sparse vector, we need to search for i in transcoefsidx */
2775 v2i = 0.0;
2776 if( !v2zero && SCIPsortedvecFindInt(nlhdlrexprdata->transcoefsidx + nlhdlrexprdata->termbegins[1], i, nlhdlrexprdata->termbegins[2] - nlhdlrexprdata->termbegins[1], &pos) )
2777 {
2778 assert(nlhdlrexprdata->transcoefsidx[nlhdlrexprdata->termbegins[1] + pos] == i);
2779 v2i = nlhdlrexprdata->transcoefs[nlhdlrexprdata->termbegins[1] + pos];
2780 }
2781
2782 /* find a j, for now look only into indices with (v_2)_j != 0 */
2783 for( k = nlhdlrexprdata->termbegins[1]; k < nlhdlrexprdata->termbegins[2]; ++k )
2784 {
2785 /* check whether transcoefsidx[k] could be a good j */
2786
2787 if( nlhdlrexprdata->transcoefsidx[k] == i ) /* i == j is not good */
2788 continue;
2789
2790 /* get (v_1)_j; as it is a sparse vector, we need to search for j in transcoefsidx */
2791 v1j = 0.0;
2792 if( SCIPsortedvecFindInt(nlhdlrexprdata->transcoefsidx + nlhdlrexprdata->termbegins[0], nlhdlrexprdata->transcoefsidx[k], nlhdlrexprdata->termbegins[1] - nlhdlrexprdata->termbegins[0], &pos) )
2793 {
2794 assert(nlhdlrexprdata->transcoefsidx[nlhdlrexprdata->termbegins[0] + pos] == nlhdlrexprdata->transcoefsidx[k]);
2795 v1j = nlhdlrexprdata->transcoefs[nlhdlrexprdata->termbegins[0] + pos];
2796 }
2797
2798 v2j = nlhdlrexprdata->transcoefs[k];
2799
2800 if( SCIPisZero(scip, v2j - v2i * v1j / v1i) ) /* (v_2)_j - (v_2)_i (v_1)_j / (v_1)_i = 0 is also not good */
2801 continue;
2802
2803 j = nlhdlrexprdata->transcoefsidx[k];
2804 break;
2805 }
2806
2807 if( v2zero )
2808 {
2809 j = 0;
2810 v1j = 0.0;
2811 v2j = 0.0;
2812 }
2813
2814 if( j != -1 )
2815 {
2816 SCIP_Real c, d;
2817 int point;
2818
2819 BMSclearMemoryArray(nlhdlrexprdata->varvals, nlhdlrexprdata->nvars);
2820
2821 for( point = 0; point < (v2zero ? 2 : 3); ++point )
2822 {
2823 c = refpoints[point][0] - nlhdlrexprdata->offsets[0];
2824
2825 if( !v2zero )
2826 {
2827 /* set x_j and x_i */
2828 d = refpoints[point][1] - nlhdlrexprdata->offsets[1];
2829 nlhdlrexprdata->varvals[j] = (d - v2i/v1i*c) / (v2j - v2i * v1j / v1i);
2830 nlhdlrexprdata->varvals[i] = (c - v1j * nlhdlrexprdata->varvals[j]) / v1i;
2831
2832 SCIPdebugMsg(scip, "<%s>(%d) = %g, <%s>(%d) = %g\n",
2833 SCIPvarGetName(SCIPgetExprAuxVarNonlinear(nlhdlrexprdata->vars[i])), i, nlhdlrexprdata->varvals[i],
2834 SCIPvarGetName(SCIPgetExprAuxVarNonlinear(nlhdlrexprdata->vars[j])), j, nlhdlrexprdata->varvals[j]);
2835 }
2836 else
2837 {
2838 /* set x_i */
2839 nlhdlrexprdata->varvals[i] = c / v1i;
2840
2841 SCIPdebugMsg(scip, "<%s>(%d) = %g\n",
2842 SCIPvarGetName(SCIPgetExprAuxVarNonlinear(nlhdlrexprdata->vars[i])), i, nlhdlrexprdata->varvals[i]);
2843 }
2844
2845 assert(SCIPisEQ(scip, evalSingleTerm(scip, nlhdlrexprdata, 0), refpoints[point][0]));
2846 assert(v2zero || SCIPisEQ(scip, evalSingleTerm(scip, nlhdlrexprdata, 1), refpoints[point][1]));
2847
2848 /* compute gradient cut */
2849 SCIP_CALL( generateCutSolSOC(scip, &rowprep, expr, cons, nlhdlrexprdata, -SCIPinfinity(scip), evalSingleTerm(scip, nlhdlrexprdata, 2)) );
2850
2851 if( rowprep != NULL )
2852 {
2853 SCIP_Bool success = FALSE;
2854
2855 SCIP_CALL( SCIPcleanupRowprep2(scip, rowprep, NULL, SCIPgetHugeValue(scip), &success) );
2856 if( success )
2857 {
2858 SCIP_ROW* cut;
2859 SCIP_CALL( SCIPgetRowprepRowCons(scip, &cut, rowprep, cons) );
2860 SCIP_CALL( SCIPaddRow(scip, cut, FALSE, infeasible) );
2861 SCIP_CALL( SCIPreleaseRow(scip, &cut) );
2862 }
2863
2864 SCIPfreeRowprep(scip, &rowprep);
2865 }
2866
2867 if( *infeasible )
2868 return SCIP_OKAY;
2869 }
2870 }
2871 }
2872 else if( nlhdlrexprdata->nterms == 3 )
2873 {
2874 /* we have \sqrt{ \beta_1^2 + (v_2^T x + \beta_2)^2 } \leq v_3^T x + \beta_3
2875 * with v_2 != 0
2876 *
2877 * we should linearize at points where the second term is -1 or 1
2878 *
2879 * set x = v_2 / ||v_2||^2 (+/-1 - beta_2)
2880 */
2881 SCIP_Real plusminus1;
2882 SCIP_Real norm;
2883 int i;
2884
2885 /* calculate ||v_2||^2 */
2886 norm = 0.0;
2887 for( i = nlhdlrexprdata->termbegins[1]; i < nlhdlrexprdata->termbegins[2]; ++i )
2888 norm += SQR(nlhdlrexprdata->transcoefs[i]);
2889 assert(norm > 0.0);
2890
2891 BMSclearMemoryArray(nlhdlrexprdata->varvals, nlhdlrexprdata->nvars);
2892
2893 for( plusminus1 = -1.0; plusminus1 <= 1.0; plusminus1 += 2.0 )
2894 {
2895 /* set x = v_2 / ||v_2||^2 (plusminus1 - beta_2) */
2896 for( i = nlhdlrexprdata->termbegins[1]; i < nlhdlrexprdata->termbegins[2]; ++i )
2897 nlhdlrexprdata->varvals[nlhdlrexprdata->transcoefsidx[i]] = nlhdlrexprdata->transcoefs[i] / norm * (plusminus1 - nlhdlrexprdata->offsets[1]);
2898 assert(SCIPisEQ(scip, evalSingleTerm(scip, nlhdlrexprdata, 1), plusminus1));
2899
2900 /* compute gradient cut */
2901 SCIP_CALL( generateCutSolSOC(scip, &rowprep, expr, cons, nlhdlrexprdata, -SCIPinfinity(scip), evalSingleTerm(scip, nlhdlrexprdata, 2)) );
2902
2903 if( rowprep != NULL )
2904 {
2905 SCIP_Bool success = FALSE;
2906
2907 SCIP_CALL( SCIPcleanupRowprep2(scip, rowprep, NULL, SCIPgetHugeValue(scip), &success) );
2908 if( success )
2909 {
2910 SCIP_ROW* cut;
2911 SCIP_CALL( SCIPgetRowprepRowCons(scip, &cut, rowprep, cons) );
2912 SCIP_CALL( SCIPaddRow(scip, cut, FALSE, infeasible) );
2913 SCIP_CALL( SCIPreleaseRow(scip, &cut) );
2914 }
2915
2916 SCIPfreeRowprep(scip, &rowprep);
2917 }
2918
2919 if( *infeasible )
2920 return SCIP_OKAY;
2921 }
2922 }
2923 else
2924 {
2925 /* generate gradient cuts for the small rotated cones
2926 * \f[
2927 * \sqrt{4(v_k^T x + \beta_k)^2 + (v_n^T x + \beta_n - y_k)^2} - v_n^T x - \beta_n - y_k \leq 0.
2928 * \f]
2929 *
2930 * we should linearize again at points where the first and second term (inside sqr) are (-1/2,0), (1/2,1), (1/2,-1).
2931 * Since we have y_k, we can achieve this more easily here via
2932 * x = v_k/||v_k||^2 (+/-0.5 - beta_k)
2933 * y_k = v_n^T x + beta_n + 0/1/-1
2934 *
2935 * If v_k = 0, then we use x = 0 and linearize for second term being 1 and -1 only
2936 */
2937 static const SCIP_Real refpoints[3][2] = { {-0.5, 0.0}, {0.5, 1.0}, {0.5, -1.0} };
2938 SCIP_Real rhsval;
2939 SCIP_Real norm;
2940 int point;
2941 int i;
2942 int k;
2943 SCIP_Bool vkzero;
2944
2945 /* add disaggregation row to LP */
2946 SCIP_CALL( SCIPaddRow(scip, nlhdlrexprdata->disrow, FALSE, infeasible) );
2947
2948 if( *infeasible )
2949 return SCIP_OKAY;
2950
2951 for( k = 0; k < nlhdlrexprdata->nterms - 1; ++k )
2952 {
2953 vkzero = nlhdlrexprdata->termbegins[k+1] == nlhdlrexprdata->termbegins[k];
2954 assert(!vkzero || nlhdlrexprdata->offsets[k] != 0.0);
2955
2956 /* calculate ||v_k||^2 */
2957 norm = 0.0;
2958 for( i = nlhdlrexprdata->termbegins[k]; i < nlhdlrexprdata->termbegins[k+1]; ++i )
2959 norm += SQR(nlhdlrexprdata->transcoefs[i]);
2960 assert(vkzero || norm > 0.0);
2961
2962 BMSclearMemoryArray(nlhdlrexprdata->varvals, nlhdlrexprdata->nvars);
2963
2964 for( point = vkzero ? 1 : 0; point < 3; ++point )
2965 {
2966 /* set x = v_k / ||v_k||^2 (refpoints[point][0] - beta_k) / 2 */
2967 for( i = nlhdlrexprdata->termbegins[k]; i < nlhdlrexprdata->termbegins[k+1]; ++i )
2968 nlhdlrexprdata->varvals[nlhdlrexprdata->transcoefsidx[i]] = nlhdlrexprdata->transcoefs[i] / norm * (refpoints[point][0] - nlhdlrexprdata->offsets[k]); /*lint !e795*/
2969 assert(vkzero || SCIPisEQ(scip, evalSingleTerm(scip, nlhdlrexprdata, k), refpoints[point][0]));
2970
2971 /* set y_k = v_n^T x + beta_n + 0/1/-1 */
2972 rhsval = evalSingleTerm(scip, nlhdlrexprdata, nlhdlrexprdata->nterms - 1);
2973 nlhdlrexprdata->disvarvals[k] = rhsval + refpoints[point][1];
2974
2975 /* compute gradient cut */
2976 SCIP_CALL( generateCutSolDisagg(scip, &rowprep, expr, cons, nlhdlrexprdata, k, -SCIPinfinity(scip), rhsval) );
2977
2978 if( rowprep != NULL )
2979 {
2980 SCIP_Bool success = FALSE;
2981
2982 SCIP_CALL( SCIPcleanupRowprep2(scip, rowprep, NULL, SCIPgetHugeValue(scip), &success) );
2983 if( success )
2984 {
2985 SCIP_ROW* cut;
2986 SCIP_CALL( SCIPgetRowprepRowCons(scip, &cut, rowprep, cons) );
2987 SCIP_CALL( SCIPaddRow(scip, cut, FALSE, infeasible) );
2988 SCIP_CALL( SCIPreleaseRow(scip, &cut) );
2989 }
2990
2991 SCIPfreeRowprep(scip, &rowprep);
2992 }
2993
2994 if( *infeasible )
2995 return SCIP_OKAY;
2996 }
2997 }
2998 }
2999
3000 return SCIP_OKAY;
3001}
3002
3003
3004/** separation deinitialization method of a nonlinear handler (called during CONSEXITSOL) */
3005static
3007{ /*lint --e{715}*/
3008 assert(nlhdlrexprdata != NULL);
3009
3010 /* free disaggreagation row */
3011 if( nlhdlrexprdata->disrow != NULL )
3012 {
3013 SCIP_CALL( SCIPreleaseRow(scip, &nlhdlrexprdata->disrow) );
3014 }
3015
3016 SCIPfreeBlockMemoryArray(scip, &nlhdlrexprdata->varvals, nlhdlrexprdata->nvars);
3017
3018 return SCIP_OKAY;
3019}
3020
3021
3022/** nonlinear handler separation callback */
3023static
3025{ /*lint --e{715}*/
3026 SCIP_NLHDLRDATA* nlhdlrdata;
3027 SCIP_Real rhsval;
3028 int ndisaggrs;
3029 int k;
3030 SCIP_Bool infeasible;
3031
3032 assert(nlhdlrexprdata != NULL);
3033 assert(nlhdlrexprdata->nterms < 4 || nlhdlrexprdata->disrow != NULL);
3034 assert(nlhdlrexprdata->nterms > 1);
3035
3037
3038 if( branchcandonly )
3039 return SCIP_OKAY;
3040
3041 nlhdlrdata = SCIPnlhdlrGetData(nlhdlr);
3042 assert(nlhdlrdata != NULL);
3043
3044 /* update varvals
3045 * set variables close to integer to integer, in particular when close to zero
3046 * for simple soc's (no large v_i, no offsets), variables close to zero would give coefficients close to zero in the cut,
3047 * which the cut cleanup may have problems to relax (and we end up with local or much relaxed cuts)
3048 * also when close to other integers, rounding now may prevent some relaxation in cut cleanup
3049 */
3050 updateVarVals(scip, nlhdlrexprdata, sol, TRUE);
3051
3052 rhsval = evalSingleTerm(scip, nlhdlrexprdata, nlhdlrexprdata->nterms - 1);
3053
3054 /* if there are three or two terms just compute gradient cut */
3055 if( nlhdlrexprdata->nterms < 4 )
3056 {
3057 SCIP_ROWPREP* rowprep;
3058
3059 /* compute gradient cut */
3060 SCIP_CALL( generateCutSolSOC(scip, &rowprep, expr, cons, nlhdlrexprdata, SCIPgetLPFeastol(scip), rhsval) );
3061
3062 if( rowprep != NULL )
3063 {
3064 SCIP_CALL( addCut(scip, nlhdlrdata, rowprep, sol, cons, allowweakcuts, result) );
3065
3066 SCIPfreeRowprep(scip, &rowprep);
3067 }
3068 else
3069 {
3070 SCIPdebugMsg(scip, "failed to generate cut for SOC\n");
3071 }
3072
3073 return SCIP_OKAY;
3074 }
3075
3076 ndisaggrs = nlhdlrexprdata->nterms - 1;
3077
3078 /* check whether the aggregation row is in the LP */
3079 if( !SCIProwIsInLP(nlhdlrexprdata->disrow) && -SCIPgetRowSolFeasibility(scip, nlhdlrexprdata->disrow, sol) > SCIPgetLPFeastol(scip) )
3080 {
3081 SCIP_CALL( SCIPaddRow(scip, nlhdlrexprdata->disrow, TRUE, &infeasible) );
3082 SCIPdebugMsg(scip, "added disaggregation row to LP, cutoff=%u\n", infeasible);
3083
3084 if( infeasible )
3085 {
3087 return SCIP_OKAY;
3088 }
3089
3091 }
3092
3093 for( k = 0; k < ndisaggrs && *result != SCIP_CUTOFF; ++k )
3094 {
3095 SCIP_ROWPREP* rowprep;
3096
3097 /* compute gradient cut */
3098 SCIP_CALL( generateCutSolDisagg(scip, &rowprep, expr, cons, nlhdlrexprdata, k, SCIPgetLPFeastol(scip), rhsval) );
3099
3100 if( rowprep != NULL )
3101 {
3102 SCIP_CALL( addCut(scip, nlhdlrdata, rowprep, sol, cons, allowweakcuts, result) );
3103
3104 SCIPfreeRowprep(scip, &rowprep);
3105 }
3106 }
3107
3108 return SCIP_OKAY;
3109}
3110
3111static
3112SCIP_DECL_NLHDLRSOLLINEARIZE(nlhdlrSollinearizeSoc)
3113{ /*lint --e{715}*/
3114 SCIP_NLHDLRDATA* nlhdlrdata;
3115 SCIP_Real rhsval;
3116 int k;
3117
3118 assert(sol != NULL);
3119 assert(nlhdlrexprdata != NULL);
3120 assert(nlhdlrexprdata->nterms < 4 || nlhdlrexprdata->disrow != NULL);
3121 assert(nlhdlrexprdata->nterms > 1);
3122
3123 nlhdlrdata = SCIPnlhdlrGetData(nlhdlr);
3124 assert(nlhdlrdata != NULL);
3125
3126 /* update varvals */
3127 updateVarVals(scip, nlhdlrexprdata, sol, TRUE);
3128
3129 rhsval = evalSingleTerm(scip, nlhdlrexprdata, nlhdlrexprdata->nterms - 1);
3130
3131 /* if there are three or two terms just compute gradient cut */
3132 if( nlhdlrexprdata->nterms < 4 )
3133 {
3134 SCIP_ROWPREP* rowprep;
3135
3136 /* compute gradient cut */
3137 SCIP_CALL( generateCutSolSOC(scip, &rowprep, expr, cons, nlhdlrexprdata, -SCIPinfinity(scip), rhsval) );
3138
3139 if( rowprep != NULL )
3140 {
3141 SCIP_CALL( addCutPool(scip, nlhdlrdata, rowprep, sol, cons) );
3142
3143 SCIPfreeRowprep(scip, &rowprep);
3144 }
3145
3146 return SCIP_OKAY;
3147 }
3148
3149 for( k = 0; k < nlhdlrexprdata->nterms - 1; ++k )
3150 {
3151 SCIP_ROWPREP* rowprep;
3152
3153 /* compute gradient cut */
3154 SCIP_CALL( generateCutSolDisagg(scip, &rowprep, expr, cons, nlhdlrexprdata, k, -SCIPinfinity(scip), rhsval) );
3155
3156 if( rowprep != NULL )
3157 {
3158 SCIP_CALL( addCutPool(scip, nlhdlrdata, rowprep, sol, cons) );
3159
3160 SCIPfreeRowprep(scip, &rowprep);
3161 }
3162 }
3163
3164 return SCIP_OKAY;
3165}
3166
3167/*
3168 * nonlinear handler specific interface methods
3169 */
3170
3171/** includes SOC nonlinear handler in nonlinear constraint handler */
3173 SCIP* scip /**< SCIP data structure */
3174 )
3175{
3176 SCIP_NLHDLRDATA* nlhdlrdata;
3177 SCIP_NLHDLR* nlhdlr;
3178
3179 assert(scip != NULL);
3180
3181 /* create nonlinear handler data */
3182 SCIP_CALL( SCIPallocClearBlockMemory(scip, &nlhdlrdata) );
3183
3184 SCIP_CALL( SCIPincludeNlhdlrNonlinear(scip, &nlhdlr, NLHDLR_NAME, NLHDLR_DESC, NLHDLR_DETECTPRIORITY, NLHDLR_ENFOPRIORITY, nlhdlrDetectSoc, nlhdlrEvalauxSoc, nlhdlrdata) );
3185 assert(nlhdlr != NULL);
3186
3187 SCIPnlhdlrSetCopyHdlr(nlhdlr, nlhdlrCopyhdlrSoc);
3188 SCIPnlhdlrSetFreeHdlrData(nlhdlr, nlhdlrFreehdlrdataSoc);
3189 SCIPnlhdlrSetFreeExprData(nlhdlr, nlhdlrFreeExprDataSoc);
3190 SCIPnlhdlrSetSepa(nlhdlr, nlhdlrInitSepaSoc, nlhdlrEnfoSoc, NULL, nlhdlrExitSepaSoc);
3191 SCIPnlhdlrSetSollinearize(nlhdlr, nlhdlrSollinearizeSoc);
3192
3193 /* add soc nlhdlr parameters */
3194 /* TODO should we get rid of this and use separating/mineffiacy(root) instead, which is 1e-4? */
3195 SCIP_CALL( SCIPaddRealParam(scip, "nlhdlr/" NLHDLR_NAME "/mincutefficacy",
3196 "Minimum efficacy which a cut needs in order to be added.",
3197 &nlhdlrdata->mincutefficacy, FALSE, DEFAULT_MINCUTEFFICACY, 0.0, SCIPinfinity(scip), NULL, NULL) );
3198
3199 SCIP_CALL( SCIPaddBoolParam(scip, "nlhdlr/" NLHDLR_NAME "/compeigenvalues",
3200 "Should Eigenvalue computations be done to detect complex cases in quadratic constraints?",
3201 &nlhdlrdata->compeigenvalues, FALSE, DEFAULT_COMPEIGENVALUES, NULL, NULL) );
3202
3203 return SCIP_OKAY;
3204}
3205
3206/** checks whether constraint is SOC representable in original variables and returns the SOC representation
3207 *
3208 * The SOC representation has the form:
3209 * \f$\sqrt{\sum_{i=1}^{n} (v_i^T x + \beta_i)^2} - v_{n+1}^T x - \beta_{n+1} \lessgtr 0\f$,
3210 * where \f$n+1 = \text{nterms}\f$ and the inequality type is given by sidetype (`SCIP_SIDETYPE_RIGHT` if inequality
3211 * is \f$\leq\f$, `SCIP_SIDETYPE_LEFT` if \f$\geq\f$).
3212 *
3213 * For each term (i.e. for each \f$i\f$ in the above notation as well as \f$n+1\f$), the constant \f$\beta_i\f$ is given by the
3214 * corresponding element `offsets[i-1]` and `termbegins[i-1]` is the starting position of the term in arrays
3215 * `transcoefs` and `transcoefsidx`. The overall number of nonzeros is `termbegins[nterms]`.
3216 *
3217 * Arrays `transcoefs` and `transcoefsidx` have size `termbegins[nterms]` and define the linear expressions \f$v_i^T x\f$
3218 * for each term. For a term \f$i\f$ in the above notation, the nonzeroes are given by elements
3219 * `termbegins[i-1]...termbegins[i]` of `transcoefs` and `transcoefsidx`. There may be no nonzeroes for some term (i.e.,
3220 * constant terms are possible). `transcoefs` contains the coefficients \f$v_i\f$ and `transcoefsidx` contains positions of
3221 * variables in the `vars` array.
3222 *
3223 * The `vars` array has size `nvars` and contains \f$x\f$ variables; each variable is included at most once.
3224 *
3225 * The arrays should be freed by calling SCIPfreeSOCArraysNonlinear().
3226 *
3227 * This function uses the methods that are used in the detection algorithm of the SOC nonlinear handler.
3228 */
3230 SCIP* scip, /**< SCIP data structure */
3231 SCIP_CONS* cons, /**< nonlinear constraint */
3232 SCIP_Bool compeigenvalues, /**< whether eigenvalues should be computed to detect complex cases */
3233 SCIP_Bool* success, /**< pointer to store whether SOC structure has been detected */
3234 SCIP_SIDETYPE* sidetype, /**< pointer to store which side of cons is SOC representable; only
3235 * valid when success is TRUE */
3236 SCIP_VAR*** vars, /**< variables (x) that appear on both sides; no duplicates are allowed */
3237 SCIP_Real** offsets, /**< offsets of both sides (beta_i) */
3238 SCIP_Real** transcoefs, /**< non-zeros of linear transformation vectors (v_i) */
3239 int** transcoefsidx, /**< mapping of transformation coefficients to variable indices in vars */
3240 int** termbegins, /**< starting indices of transcoefs for each term */
3241 int* nvars, /**< total number of variables appearing (i.e. size of vars) */
3242 int* nterms /**< number of summands in the SQRT +1 for RHS (n+1) */
3243 )
3244{
3245 SCIP_NLHDLRDATA nlhdlrdata;
3246 SCIP_NLHDLREXPRDATA *nlhdlrexprdata;
3247 SCIP_Real conslhs;
3248 SCIP_Real consrhs;
3249 SCIP_EXPR* expr;
3250 SCIP_Bool enforcebelow;
3251 int i;
3252
3253 assert(cons != NULL);
3254
3255 expr = SCIPgetExprNonlinear(cons);
3256 assert(expr != NULL);
3257
3258 nlhdlrdata.mincutefficacy = 0.0;
3259 nlhdlrdata.compeigenvalues = compeigenvalues;
3260
3261 conslhs = SCIPgetLhsNonlinear(cons);
3262 consrhs = SCIPgetRhsNonlinear(cons);
3263
3264 SCIP_CALL( detectSOC(scip, &nlhdlrdata, expr, conslhs, consrhs, &nlhdlrexprdata, &enforcebelow, success) );
3265
3266 /* the constraint must be SOC representable in original variables */
3267 if( *success )
3268 {
3269 assert(nlhdlrexprdata != NULL);
3270
3271 for( i = 0; i < nlhdlrexprdata->nvars; ++i )
3272 {
3273 if( !SCIPisExprVar(scip, nlhdlrexprdata->vars[i]) )
3274 {
3275 *success = FALSE;
3276 break;
3277 }
3278 }
3279 }
3280
3281 if( *success )
3282 {
3283 *sidetype = enforcebelow ? SCIP_SIDETYPE_RIGHT : SCIP_SIDETYPE_LEFT;
3284 SCIP_CALL( SCIPallocBlockMemoryArray(scip, vars, nlhdlrexprdata->nvars) );
3285
3286 for( i = 0; i < nlhdlrexprdata->nvars; ++i )
3287 {
3288 (*vars)[i] = SCIPgetVarExprVar(nlhdlrexprdata->vars[i]);
3289 assert((*vars)[i] != NULL);
3290 }
3291 SCIPfreeBlockMemoryArray(scip, &nlhdlrexprdata->vars, nlhdlrexprdata->nvars);
3292 *offsets = nlhdlrexprdata->offsets;
3293 *transcoefs = nlhdlrexprdata->transcoefs;
3294 *transcoefsidx = nlhdlrexprdata->transcoefsidx;
3295 *termbegins = nlhdlrexprdata->termbegins;
3296 *nvars = nlhdlrexprdata->nvars;
3297 *nterms = nlhdlrexprdata->nterms;
3298 SCIPfreeBlockMemory(scip, &nlhdlrexprdata);
3299 }
3300 else
3301 {
3302 if( nlhdlrexprdata != NULL )
3303 {
3304 SCIP_CALL( freeNlhdlrExprData(scip, &nlhdlrexprdata) );
3305 }
3306 *vars = NULL;
3307 *offsets = NULL;
3308 *transcoefs = NULL;
3309 *transcoefsidx = NULL;
3310 *termbegins = NULL;
3311 *nvars = 0;
3312 *nterms = 0;
3313 }
3314
3315 return SCIP_OKAY;
3316}
3317
3318/** frees arrays created by SCIPisSOCNonlinear() */
3320 SCIP* scip, /**< SCIP data structure */
3321 SCIP_VAR*** vars, /**< variables that appear on both sides (x) */
3322 SCIP_Real** offsets, /**< offsets of both sides (beta_i) */
3323 SCIP_Real** transcoefs, /**< non-zeros of linear transformation vectors (v_i) */
3324 int** transcoefsidx, /**< mapping of transformation coefficients to variable indices in vars */
3325 int** termbegins, /**< starting indices of transcoefs for each term */
3326 int nvars, /**< total number of variables appearing */
3327 int nterms /**< number of summands in the SQRT +1 for RHS (n+1) */
3328 )
3329{
3330 int ntranscoefs;
3331
3332 if( nvars == 0 )
3333 return;
3334
3335 assert(vars != NULL);
3336 assert(offsets != NULL);
3337 assert(transcoefs != NULL);
3338 assert(transcoefsidx != NULL);
3339 assert(termbegins != NULL);
3340
3341 ntranscoefs = (*termbegins)[nterms];
3342
3343 SCIPfreeBlockMemoryArray(scip, termbegins, nterms + 1);
3344 SCIPfreeBlockMemoryArray(scip, transcoefsidx, ntranscoefs);
3345 SCIPfreeBlockMemoryArray(scip, transcoefs, ntranscoefs);
3348}
constraint handler for nonlinear constraints specified by algebraic expressions
methods for debugging
#define SCIPdebugGetSolVal(scip, var, val)
Definition debug.h:313
#define SCIPdebugAddSolVal(scip, var, val)
Definition debug.h:312
#define NULL
Definition def.h:257
#define SCIP_MAXSTRLEN
Definition def.h:278
#define SCIP_INVALID
Definition def.h:187
#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 SQR(x)
Definition def.h:208
#define TRUE
Definition def.h:102
#define FALSE
Definition def.h:103
#define MAX(x, y)
Definition def.h:229
#define SCIP_LONGINT_FORMAT
Definition def.h:157
#define SCIP_CALL(x)
Definition def.h:364
power and signed power expression handlers
sum expression handler
variable expression handler
unsigned int SCIPgetExprNAuxvarUsesNonlinear(SCIP_EXPR *expr)
int SCIPgetExprNLocksPosNonlinear(SCIP_EXPR *expr)
SCIP_VAR * SCIPgetExprAuxVarNonlinear(SCIP_EXPR *expr)
SCIP_EXPR * SCIPgetExprNonlinear(SCIP_CONS *cons)
SCIP_Real SCIPgetRhsNonlinear(SCIP_CONS *cons)
int SCIPgetExprNLocksNegNonlinear(SCIP_EXPR *expr)
SCIP_RETCODE SCIPregisterExprUsageNonlinear(SCIP *scip, SCIP_EXPR *expr, SCIP_Bool useauxvar, SCIP_Bool useactivityforprop, SCIP_Bool useactivityforsepabelow, SCIP_Bool useactivityforsepaabove)
SCIP_Real SCIPgetLhsNonlinear(SCIP_CONS *cons)
SCIP_RETCODE SCIPaddVar(SCIP *scip, SCIP_VAR *var)
Definition scip_prob.c:1907
void SCIPhashmapFree(SCIP_HASHMAP **hashmap)
Definition misc.c:3095
int SCIPhashmapGetImageInt(SCIP_HASHMAP *hashmap, void *origin)
Definition misc.c:3304
int SCIPhashmapGetNElements(SCIP_HASHMAP *hashmap)
Definition misc.c:3576
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
void SCIPhashsetFree(SCIP_HASHSET **hashset, BMS_BLKMEM *blkmem)
Definition misc.c:3833
SCIP_Bool SCIPhashsetExists(SCIP_HASHSET *hashset, void *element)
Definition misc.c:3860
int SCIPhashsetGetNElements(SCIP_HASHSET *hashset)
Definition misc.c:4035
SCIP_RETCODE SCIPhashsetInsert(SCIP_HASHSET *hashset, BMS_BLKMEM *blkmem, void *element)
Definition misc.c:3843
SCIP_RETCODE SCIPhashsetCreate(SCIP_HASHSET **hashset, BMS_BLKMEM *blkmem, int size)
Definition misc.c:3802
SCIP_RETCODE SCIPhashsetRemove(SCIP_HASHSET *hashset, void *element)
Definition misc.c:3901
void SCIPinfoMessage(SCIP *scip, FILE *file, const char *formatstr,...)
void SCIPverbMessage(SCIP *scip, SCIP_VERBLEVEL msgverblevel, FILE *file, const char *formatstr,...)
#define SCIPdebugMsg
void SCIPfreeSOCArraysNonlinear(SCIP *scip, SCIP_VAR ***vars, SCIP_Real **offsets, SCIP_Real **transcoefs, int **transcoefsidx, int **termbegins, int nvars, int nterms)
SCIP_RETCODE SCIPisSOCNonlinear(SCIP *scip, SCIP_CONS *cons, SCIP_Bool compeigenvalues, SCIP_Bool *success, SCIP_SIDETYPE *sidetype, SCIP_VAR ***vars, SCIP_Real **offsets, SCIP_Real **transcoefs, int **transcoefsidx, int **termbegins, int *nvars, int *nterms)
SCIP_RETCODE SCIPincludeNlhdlrSoc(SCIP *scip)
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 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
void SCIPswapReals(SCIP_Real *value1, SCIP_Real *value2)
Definition misc.c:10498
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 SCIPisCutApplicable(SCIP *scip, SCIP_ROW *cut)
Definition scip_cut.c:207
SCIP_RETCODE SCIPaddRow(SCIP *scip, SCIP_ROW *row, SCIP_Bool forcecut, SCIP_Bool *infeasible)
Definition scip_cut.c:225
int SCIPexprGetNChildren(SCIP_EXPR *expr)
Definition expr.c:3872
SCIP_Real SCIPgetExponentExprPow(SCIP_EXPR *expr)
Definition expr_pow.c:3449
SCIP_Bool SCIPisExprProduct(SCIP *scip, SCIP_EXPR *expr)
Definition scip_expr.c:1490
SCIP_Bool SCIPisExprSum(SCIP *scip, SCIP_EXPR *expr)
Definition scip_expr.c:1479
SCIP_Real * SCIPgetCoefsExprSum(SCIP_EXPR *expr)
Definition expr_sum.c:1554
SCIP_Bool SCIPisExprVar(SCIP *scip, SCIP_EXPR *expr)
Definition scip_expr.c:1457
SCIP_RETCODE SCIPprintExpr(SCIP *scip, SCIP_EXPR *expr, FILE *file)
Definition scip_expr.c:1512
SCIP_Bool SCIPisExprPower(SCIP *scip, SCIP_EXPR *expr)
Definition scip_expr.c:1501
SCIP_EXPR ** SCIPexprGetChildren(SCIP_EXPR *expr)
Definition expr.c:3882
SCIP_Real SCIPgetConstantExprSum(SCIP_EXPR *expr)
Definition expr_sum.c:1569
SCIP_VAR * SCIPgetVarExprVar(SCIP_EXPR *expr)
Definition expr_var.c:423
SCIP_INTERVAL SCIPexprGetActivity(SCIP_EXPR *expr)
Definition expr.c:4028
SCIP_RETCODE SCIPdismantleExpr(SCIP *scip, FILE *file, SCIP_EXPR *expr)
Definition scip_expr.c:1634
SCIP_RETCODE SCIPevalExprActivity(SCIP *scip, SCIP_EXPR *expr)
Definition scip_expr.c:1742
struct SCIP_Interval SCIP_INTERVAL
SCIP_Real SCIPgetLPFeastol(SCIP *scip)
Definition scip_lp.c:434
#define SCIPfreeBlockMemoryArray(scip, ptr, num)
Definition scip_mem.h:110
#define SCIPallocClearBlockMemory(scip, ptr)
Definition scip_mem.h:91
BMS_BLKMEM * SCIPblkmem(SCIP *scip)
Definition scip_mem.c:57
BMS_BUFMEM * SCIPbuffer(SCIP *scip)
Definition scip_mem.c:72
#define SCIPallocClearBufferArray(scip, ptr, num)
Definition scip_mem.h:126
#define SCIPallocBufferArray(scip, ptr, num)
Definition scip_mem.h:124
#define SCIPfreeBufferArray(scip, ptr)
Definition scip_mem.h:136
#define SCIPduplicateBufferArray(scip, ptr, source, num)
Definition scip_mem.h:132
#define SCIPallocBlockMemoryArray(scip, ptr, num)
Definition scip_mem.h:93
#define SCIPfreeBlockMemory(scip, ptr)
Definition scip_mem.h:108
#define SCIPfreeBlockMemoryArrayNull(scip, ptr, num)
Definition scip_mem.h:111
#define SCIPfreeBufferArrayNull(scip, ptr)
Definition scip_mem.h:137
#define SCIPallocBlockMemory(scip, ptr)
Definition scip_mem.h:89
#define SCIPduplicateBlockMemoryArray(scip, ptr, source, num)
Definition scip_mem.h:105
SCIP_NLHDLRDATA * SCIPnlhdlrGetData(SCIP_NLHDLR *nlhdlr)
Definition nlhdlr.c:217
void SCIPnlhdlrSetFreeExprData(SCIP_NLHDLR *nlhdlr,)
Definition nlhdlr.c:99
const char * SCIPnlhdlrGetName(SCIP_NLHDLR *nlhdlr)
Definition nlhdlr.c:167
void SCIPnlhdlrSetSollinearize(SCIP_NLHDLR *nlhdlr,)
Definition nlhdlr.c:155
void SCIPnlhdlrSetSepa(SCIP_NLHDLR *nlhdlr, SCIP_DECL_NLHDLRINITSEPA((*initsepa)), SCIP_DECL_NLHDLRENFO((*enfo)), SCIP_DECL_NLHDLRESTIMATE((*estimate)),)
Definition nlhdlr.c:137
void SCIPnlhdlrSetFreeHdlrData(SCIP_NLHDLR *nlhdlr,)
Definition nlhdlr.c:88
void SCIPnlhdlrSetCopyHdlr(SCIP_NLHDLR *nlhdlr,)
Definition nlhdlr.c:77
SCIP_RETCODE SCIPincludeNlhdlrNonlinear(SCIP *scip, SCIP_NLHDLR **nlhdlr, const char *name, const char *desc, int detectpriority, int enfopriority, SCIP_DECL_NLHDLRDETECT((*detect)), SCIP_DECL_NLHDLREVALAUX((*evalaux)), SCIP_NLHDLRDATA *nlhdlrdata)
SCIP_RETCODE SCIPcreateEmptyRowConshdlr(SCIP *scip, SCIP_ROW **row, SCIP_CONSHDLR *conshdlr, const char *name, SCIP_Real lhs, SCIP_Real rhs, SCIP_Bool local, SCIP_Bool modifiable, SCIP_Bool removable)
Definition scip_lp.c:1367
SCIP_RETCODE SCIPaddVarToRow(SCIP *scip, SCIP_ROW *row, SCIP_VAR *var, SCIP_Real val)
Definition scip_lp.c:1646
SCIP_Real SCIPgetRowSolFeasibility(SCIP *scip, SCIP_ROW *row, SCIP_SOL *sol)
Definition scip_lp.c:2131
SCIP_RETCODE SCIPreleaseRow(SCIP *scip, SCIP_ROW **row)
Definition scip_lp.c:1508
void SCIPmarkRowNotRemovableLocal(SCIP *scip, SCIP_ROW *row)
Definition scip_lp.c:1814
SCIP_Bool SCIProwIsInLP(SCIP_ROW *row)
Definition lp.c:17917
SCIP_Real SCIPgetSolVal(SCIP *scip, SCIP_SOL *sol, SCIP_VAR *var)
Definition scip_sol.c:1763
SCIP_Longint SCIPgetNLPs(SCIP *scip)
SCIP_Real SCIPinfinity(SCIP *scip)
SCIP_Bool SCIPisIntegral(SCIP *scip, SCIP_Real val)
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_Real SCIPround(SCIP *scip, SCIP_Real val)
SCIP_Real SCIPgetHugeValue(SCIP *scip)
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)
SCIP_Bool SCIPvarIsBinary(SCIP_VAR *var)
Definition var.c:23510
void SCIPvarMarkRelaxationOnly(SCIP_VAR *var)
Definition var.c:23650
SCIP_RETCODE SCIPaddVarLocksType(SCIP *scip, SCIP_VAR *var, SCIP_LOCKTYPE locktype, int nlocksdown, int nlocksup)
Definition scip_var.c:5118
const char * SCIPvarGetName(SCIP_VAR *var)
Definition var.c:23299
SCIP_RETCODE SCIPreleaseVar(SCIP *scip, SCIP_VAR **var)
Definition scip_var.c:1887
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 SCIPcleanupRowprep2(SCIP *scip, SCIP_ROWPREP *rowprep, SCIP_SOL *sol, SCIP_Real maxcoefbound, SCIP_Bool *success)
SCIP_RETCODE SCIPensureRowprepSize(SCIP *scip, SCIP_ROWPREP *rowprep, int size)
SCIP_Real SCIPgetRowprepViolation(SCIP *scip, SCIP_ROWPREP *rowprep, SCIP_SOL *sol, SCIP_Bool *reliable)
char * SCIProwprepGetName(SCIP_ROWPREP *rowprep)
SCIP_Bool SCIProwprepIsLocal(SCIP_ROWPREP *rowprep)
SCIP_RETCODE SCIPaddRowprepTerm(SCIP *scip, SCIP_ROWPREP *rowprep, SCIP_VAR *var, SCIP_Real coef)
SCIP_RETCODE SCIPgetRowprepRowCons(SCIP *scip, SCIP_ROW **row, SCIP_ROWPREP *rowprep, SCIP_CONS *cons)
SCIP_RETCODE SCIPcreateRowprep(SCIP *scip, SCIP_ROWPREP **rowprep, SCIP_SIDETYPE sidetype, SCIP_Bool local)
int SCIProwprepGetNVars(SCIP_ROWPREP *rowprep)
void SCIProwprepAddSide(SCIP_ROWPREP *rowprep, SCIP_Real side)
void SCIPfreeRowprep(SCIP *scip, SCIP_ROWPREP **rowprep)
SCIP_Bool SCIPsortedvecFindInt(int *intarray, int val, int len, int *pos)
int SCIPsnprintf(char *t, int len, const char *s,...)
Definition misc.c:10827
return SCIP_OKAY
int c
static SCIP_SOL * sol
assert(minobj< SCIPgetCutoffbound(scip))
int nvars
SCIP_VAR * var
static SCIP_VAR ** vars
static volatile int nterms
Definition interrupt.c:47
SCIP_Bool SCIPlapackIsAvailable(void)
SCIP_RETCODE SCIPlapackComputeEigenvalues(BMS_BUFMEM *bufmem, SCIP_Bool geteigenvectors, int N, SCIP_Real *a, SCIP_Real *w)
interface methods for lapack functions
#define BMSclearMemoryArray(ptr, num)
Definition memory.h:130
#define NLHDLR_DETECTPRIORITY
#define NLHDLR_ENFOPRIORITY
#define NLHDLR_DESC
#define NLHDLR_NAME
static SCIP_RETCODE addCutPool(SCIP *scip, SCIP_NLHDLRDATA *nlhdlrdata, SCIP_ROWPREP *rowprep, SCIP_SOL *sol, SCIP_CONS *cons)
Definition nlhdlr_soc.c:830
static SCIP_RETCODE tryFillNlhdlrExprDataQuad(SCIP *scip, SCIP_EXPR **occurringexprs, SCIP_Real *eigvecmatrix, SCIP_Real *eigvals, SCIP_Real *bp, int nvars, int *termbegins, SCIP_Real *transcoefs, int *transcoefsidx, SCIP_Real *offsets, SCIP_Real *lhsconstant, int *nterms, SCIP_Bool *success)
static SCIP_RETCODE freeNlhdlrExprData(SCIP *scip, SCIP_NLHDLREXPRDATA **nlhdlrexprdata)
Definition nlhdlr_soc.c:383
static void updateVarVals(SCIP *scip, SCIP_NLHDLREXPRDATA *nlhdlrexprdata, SCIP_SOL *sol, SCIP_Bool roundtinyfrac)
Definition nlhdlr_soc.c:411
static SCIP_RETCODE generateCutSolSOC(SCIP *scip, SCIP_ROWPREP **rowprep, SCIP_EXPR *expr, SCIP_CONS *cons, SCIP_NLHDLREXPRDATA *nlhdlrexprdata, SCIP_Real mincutviolation, SCIP_Real rhsval)
Definition nlhdlr_soc.c:493
static SCIP_RETCODE createDisaggrRow(SCIP *scip, SCIP_CONSHDLR *conshdlr, SCIP_EXPR *expr, SCIP_NLHDLREXPRDATA *nlhdlrexprdata)
Definition nlhdlr_soc.c:281
static SCIP_RETCODE detectSocNorm(SCIP *scip, SCIP_EXPR *expr, SCIP_NLHDLREXPRDATA **nlhdlrexprdata, SCIP_Bool *success)
static SCIP_RETCODE detectSOC(SCIP *scip, SCIP_NLHDLRDATA *nlhdlrdata, SCIP_EXPR *expr, SCIP_Real conslhs, SCIP_Real consrhs, SCIP_NLHDLREXPRDATA **nlhdlrexprdata, SCIP_Bool *enforcebelow, SCIP_Bool *success)
static SCIP_RETCODE checkAndCollectQuadratic(SCIP *scip, SCIP_EXPR *quadexpr, SCIP_HASHMAP *expr2idx, SCIP_EXPR **occurringexprs, int *nexprs, SCIP_Bool *success)
Definition nlhdlr_soc.c:874
#define DEFAULT_MINCUTEFFICACY
Definition nlhdlr_soc.c:58
#define DEFAULT_COMPEIGENVALUES
Definition nlhdlr_soc.c:59
static SCIP_RETCODE createDisaggrVars(SCIP *scip, SCIP_EXPR *expr, SCIP_NLHDLREXPRDATA *nlhdlrexprdata)
Definition nlhdlr_soc.c:215
static SCIP_RETCODE detectSocQuadraticComplex(SCIP *scip, SCIP_EXPR *expr, SCIP_Real conslhs, SCIP_Real consrhs, SCIP_NLHDLREXPRDATA **nlhdlrexprdata, SCIP_Bool *enforcebelow, SCIP_Bool *success)
static SCIP_RETCODE createNlhdlrExprData(SCIP *scip, SCIP_EXPR **vars, SCIP_Real *offsets, SCIP_Real *transcoefs, int *transcoefsidx, int *termbegins, int nvars, int nterms, SCIP_NLHDLREXPRDATA **nlhdlrexprdata)
Definition nlhdlr_soc.c:334
static void buildQuadExprMatrix(SCIP *scip, SCIP_EXPR *quadexpr, SCIP_HASHMAP *expr2idx, int nexprs, SCIP_Real *quadmatrix, SCIP_Real *linvector)
Definition nlhdlr_soc.c:989
static SCIP_RETCODE addCut(SCIP *scip, SCIP_NLHDLRDATA *nlhdlrdata, SCIP_ROWPREP *rowprep, SCIP_SOL *sol, SCIP_CONS *cons, SCIP_Bool allowweakcuts, SCIP_RESULT *result)
Definition nlhdlr_soc.c:762
static SCIP_RETCODE freeDisaggrVars(SCIP *scip, SCIP_NLHDLREXPRDATA *nlhdlrexprdata)
Definition nlhdlr_soc.c:250
static SCIP_RETCODE generateCutSolDisagg(SCIP *scip, SCIP_ROWPREP **rowprep, SCIP_EXPR *expr, SCIP_CONS *cons, SCIP_NLHDLREXPRDATA *nlhdlrexprdata, int disaggidx, SCIP_Real mincutviolation, SCIP_Real rhsval)
Definition nlhdlr_soc.c:626
static SCIP_Real evalSingleTerm(SCIP *scip, SCIP_NLHDLREXPRDATA *nlhdlrexprdata, int k)
Definition nlhdlr_soc.c:443
static SCIP_RETCODE detectSocQuadraticSimple(SCIP *scip, SCIP_EXPR *expr, SCIP_Real conslhs, SCIP_Real consrhs, SCIP_NLHDLREXPRDATA **nlhdlrexprdata, SCIP_Bool *enforcebelow, SCIP_Bool *success)
soc nonlinear handler
public functions of nonlinear handlers of nonlinear constraints
SCIP_Real sup
SCIP_Real inf
struct SCIP_Cons SCIP_CONS
Definition type_cons.h:63
struct SCIP_Conshdlr SCIP_CONSHDLR
Definition type_cons.h:62
struct SCIP_Expr SCIP_EXPR
Definition type_expr.h:55
struct SCIP_Row SCIP_ROW
Definition type_lp.h:105
@ SCIP_SIDETYPE_RIGHT
Definition type_lp.h:66
@ SCIP_SIDETYPE_LEFT
Definition type_lp.h:65
enum SCIP_SideType SCIP_SIDETYPE
Definition type_lp.h:68
@ SCIP_VERBLEVEL_FULL
struct SCIP_RowPrep SCIP_ROWPREP
Definition type_misc.h:173
struct SCIP_HashMap SCIP_HASHMAP
Definition type_misc.h:106
struct SCIP_HashSet SCIP_HASHSET
Definition type_misc.h:112
#define SCIP_NLHDLR_METHOD_SEPAABOVE
Definition type_nlhdlr.h:52
#define SCIP_DECL_NLHDLREVALAUX(x)
struct SCIP_NlhdlrData SCIP_NLHDLRDATA
#define SCIP_NLHDLR_METHOD_SEPABOTH
Definition type_nlhdlr.h:53
#define SCIP_DECL_NLHDLRCOPYHDLR(x)
Definition type_nlhdlr.h:70
#define SCIP_DECL_NLHDLRSOLLINEARIZE(x)
#define SCIP_DECL_NLHDLRFREEEXPRDATA(x)
Definition type_nlhdlr.h:94
#define SCIP_DECL_NLHDLRDETECT(x)
#define SCIP_DECL_NLHDLREXITSEPA(x)
struct SCIP_Nlhdlr SCIP_NLHDLR
#define SCIP_DECL_NLHDLRINITSEPA(x)
#define SCIP_DECL_NLHDLRFREEHDLRDATA(x)
Definition type_nlhdlr.h:82
struct SCIP_NlhdlrExprData SCIP_NLHDLREXPRDATA
#define SCIP_DECL_NLHDLRENFO(x)
#define SCIP_NLHDLR_METHOD_SEPABELOW
Definition type_nlhdlr.h:51
@ 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_INVALIDCALL
enum SCIP_Retcode SCIP_RETCODE
struct Scip SCIP
Definition type_scip.h:39
struct SCIP_Sol SCIP_SOL
Definition type_sol.h:57
struct SCIP_Var SCIP_VAR
Definition type_var.h:166
@ SCIP_VARTYPE_CONTINUOUS
Definition type_var.h:71
@ SCIP_LOCKTYPE_MODEL
Definition type_var.h:141