Scippy

SCIP

Solving Constraint Integer Programs

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