1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /*                                                                           */
3 /*                  This file is part of the program and library             */
4 /*         SCIP --- Solving Constraint Integer Programs                      */
5 /*                                                                           */
6 /*    Copyright (C) 2002-2021 Konrad-Zuse-Zentrum                            */
7 /*                            fuer Informationstechnik Berlin                */
8 /*                                                                           */
9 /*  SCIP is distributed under the terms of the ZIB Academic License.         */
10 /*                                                                           */
11 /*  You should have received a copy of the ZIB Academic License              */
12 /*  along with SCIP; see the file COPYING. If not visit scipopt.org.         */
13 /*                                                                           */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15 
16 /**@file   nlp.c
17  * @ingroup OTHER_CFILES
18  * @brief  NLP management methods and datastructures
19  * @author Thorsten Gellermann
20  * @author Stefan Vigerske
21  *
22  *  In NLP management, we have to differ between the current NLP and the NLPI problem
23  *  stored in the NLP solver. All NLP methods affect the current NLP only.
24  *  Before solving the current NLP with the NLP solver, the NLP solvers data
25  *  has to be updated to the current NLP with a call to nlpFlush().
26  *
27  *  @todo handle linear rows from LP
28  */
29 
30 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
31 
32 
33 #include "nlpi/nlpi.h"
34 #include "nlpi/pub_expr.h"
35 #include "nlpi/struct_expr.h"
36 #include "scip/clock.h"
37 #include "scip/event.h"
38 #include "scip/intervalarith.h"
39 #include "scip/nlp.h"
40 #include "scip/primal.h"
41 #include "scip/pub_event.h"
42 #include "scip/pub_lp.h"
43 #include "scip/pub_message.h"
44 #include "scip/pub_misc.h"
45 #include "scip/pub_misc_sort.h"
46 #include "scip/pub_nlp.h"
47 #include "scip/pub_var.h"
48 #include "scip/set.h"
49 #include "scip/sol.h"
50 #include "scip/struct_nlp.h"
51 /* to get nlp, set, ... in event handling */
52 #include "scip/struct_scip.h"
53 /* to get value of parameter "nlp/solver" and nlpis array and to get access to set->lp for releasing a variable */
54 #include "scip/struct_set.h"
55 #include "scip/struct_stat.h"
56 #include "scip/var.h"
57 #include <string.h>
58 
59 /* defines */
60 
61 #define EVENTHDLR_NAME   "nlpEventHdlr"      /**< name of NLP event handler that catches variable events */
62 #define EVENTHDLR_DESC   "handles all events necessary for maintaining NLP data"  /**< description of NLP event handler */
63 #define ADDNAMESTONLPI   0                   /**< whether to give variable and row names to NLPI */
64 
65 #ifdef __cplusplus
66 extern "C" {
67 #endif
68 
69 /* avoid inclusion of scip.h */ /*lint -e{2701}*/
70 BMS_BLKMEM* SCIPblkmem(
71    SCIP*                 scip                /**< SCIP data structure */
72    );
73 
74 #ifdef __cplusplus
75 }
76 #endif
77 
78 /*
79  * forward declarations
80  */
81 
82 /** NLP event handler execution method */
83 static
84 SCIP_DECL_EVENTEXEC( eventExecNlp );
85 
86 /** announces, that a row of the NLP was modified
87  * adjusts status of current solution
88  * calling method has to ensure that change is passed to the NLPI!
89  */
90 static
91 SCIP_RETCODE nlpRowChanged(
92    SCIP_NLP*             nlp,                /**< current NLP data */
93    SCIP_SET*             set,                /**< global SCIP settings */
94    SCIP_STAT*            stat,               /**< problem statistics data */
95    SCIP_NLROW*           nlrow               /**< nonlinear row which was changed */
96    );
97 
98 /*
99  * public expression tree methods
100  */
101 
102 /** returns variables of expression tree */
SCIPexprtreeGetVars(SCIP_EXPRTREE * tree)103 SCIP_VAR** SCIPexprtreeGetVars(
104    SCIP_EXPRTREE*        tree                /**< expression tree */
105    )
106 {
107    assert(tree != NULL);
108 
109    return (SCIP_VAR**)tree->vars;
110 }
111 
112 /** stores array of variables in expression tree */
SCIPexprtreeSetVars(SCIP_EXPRTREE * tree,int nvars,SCIP_VAR ** vars)113 SCIP_RETCODE SCIPexprtreeSetVars(
114    SCIP_EXPRTREE*        tree,               /**< expression tree */
115    int                   nvars,              /**< number of variables */
116    SCIP_VAR**            vars                /**< variables */
117    )
118 {
119    assert(tree != NULL);
120    assert(vars != NULL || nvars == 0);
121 
122    if( nvars == 0 )
123    {
124       BMSfreeBlockMemoryArrayNull(tree->blkmem, &tree->vars, tree->nvars);
125       tree->nvars = 0;
126    }
127    else if( tree->vars != NULL )
128    {
129       SCIP_ALLOC( BMSreallocBlockMemoryArray(tree->blkmem, &tree->vars, tree->nvars, nvars) );
130       BMScopyMemoryArray(tree->vars, (void**)vars, nvars);
131    }
132    else
133    {
134       SCIP_ALLOC( BMSduplicateBlockMemoryArray(tree->blkmem, &tree->vars, (void**)vars, nvars) );
135    }
136 
137    tree->nvars = nvars;
138 
139    assert(tree->vars != NULL || tree->nvars == 0);
140 
141    return SCIP_OKAY;
142 }
143 
144 /** adds variables to the expression tree variables array */
SCIPexprtreeAddVars(SCIP_EXPRTREE * tree,int nvars,SCIP_VAR ** vars)145 SCIP_RETCODE SCIPexprtreeAddVars(
146    SCIP_EXPRTREE*        tree,               /**< expression tree */
147    int                   nvars,              /**< number of variables */
148    SCIP_VAR**            vars                /**< variables */
149    )
150 {
151    assert(tree != NULL);
152    assert(vars != NULL || nvars == 0);
153    assert(tree->vars != NULL || tree->nvars == 0);
154 
155    if( nvars == 0 )
156       return SCIP_OKAY;
157 
158    if( tree->nvars == 0 )
159    {
160       SCIP_ALLOC( BMSduplicateBlockMemoryArray(tree->blkmem, &tree->vars, (void**)vars, nvars) );
161       tree->nvars = nvars;
162       return SCIP_OKAY;
163    }
164 
165    /* append vars to tree->vars array */
166    SCIP_ALLOC( BMSreallocBlockMemoryArray(tree->blkmem, &tree->vars, tree->nvars, tree->nvars + nvars) );
167    BMScopyMemoryArray(&tree->vars[tree->nvars], (void**)vars, nvars);  /*lint !e866*/
168    tree->nvars += nvars;
169 
170    return SCIP_OKAY;
171 }
172 
173 /** prints an expression tree using variable names from variables array */
SCIPexprtreePrintWithNames(SCIP_EXPRTREE * tree,SCIP_MESSAGEHDLR * messagehdlr,FILE * file)174 SCIP_RETCODE SCIPexprtreePrintWithNames(
175    SCIP_EXPRTREE*        tree,               /**< expression tree */
176    SCIP_MESSAGEHDLR*     messagehdlr,        /**< message handler */
177    FILE*                 file                /**< file for printing, or NULL for stdout */
178    )
179 {
180    const char** varnames;
181    int i;
182 
183    assert(tree != NULL);
184 
185    if( tree->nvars == 0 )
186    {
187       SCIPexprtreePrint(tree, messagehdlr, file, NULL, NULL);
188       return SCIP_OKAY;
189    }
190 
191    assert(tree->vars != NULL);
192 
193    SCIP_ALLOC( BMSallocMemoryArray(&varnames, tree->nvars) );
194    for( i = 0; i < tree->nvars; ++i )
195       varnames[i] = SCIPvarGetName((SCIP_VAR*)tree->vars[i]);
196 
197    SCIPexprtreePrint(tree, messagehdlr, file, varnames, NULL);
198 
199    BMSfreeMemoryArray(&varnames);
200 
201    return SCIP_OKAY;
202 }
203 
204 /** searches the variables array of an expression tree for a variable and returns its position, or -1 if not found
205  * Note that this is an O(n) operation!
206  */
SCIPexprtreeFindVar(SCIP_EXPRTREE * tree,SCIP_VAR * var)207 int SCIPexprtreeFindVar(
208    SCIP_EXPRTREE*        tree,               /**< expression tree */
209    SCIP_VAR*             var                 /**< variable to search for */
210    )
211 {
212    int i;
213 
214    assert(tree != NULL);
215    assert(var  != NULL);
216 
217    for( i = 0; i < tree->nvars; ++i )
218       if( (SCIP_VAR*)tree->vars[i] == var )
219          return i;
220 
221    return -1;
222 }
223 
224 /** removes fixed variables from an expression tree, so that at exit all variables are active */
SCIPexprtreeRemoveFixedVars(SCIP_EXPRTREE * tree,SCIP_SET * set,SCIP_Bool * changed,int * varpos,int * newvarsstart)225 SCIP_RETCODE SCIPexprtreeRemoveFixedVars(
226    SCIP_EXPRTREE*        tree,               /**< expression tree */
227    SCIP_SET*             set,                /**< global SCIP settings */
228    SCIP_Bool*            changed,            /**< buffer to store whether the tree was changed, i.e., whether there was a fixed variable */
229    int*                  varpos,             /**< array of length at least tree->nvars to store new indices of previously existing variables in expression tree, or -1 if variable was removed; set to NULL if not of interest */
230    int*                  newvarsstart        /**< buffer to store index in tree->vars array where new variables begin, or NULL if not of interest */
231    )
232 {
233    SCIP_HASHMAP* varhash;
234    int i;
235    int j;
236    int nvarsold;
237    SCIP_VAR* var;
238    SCIP_Real scalar;
239    SCIP_Real constant;
240    SCIP_EXPR** replaceexprs;
241    SCIP_Bool havefixedvar;
242    int idx;
243    int* newpos;
244    int offset;
245 
246    assert(tree != NULL);
247    assert(tree->vars != NULL || tree->nvars == 0);
248    assert(changed != NULL);
249 
250    *changed = FALSE;
251    if( newvarsstart != NULL )
252       *newvarsstart = tree->nvars;
253 
254    if( tree->nvars == 0 )
255       return SCIP_OKAY;
256 
257    /* create hash map from variable to indices in tree->vars and check if there is a non-fixed variable */
258    havefixedvar = FALSE;
259    SCIP_CALL( SCIPhashmapCreate(&varhash, tree->blkmem, tree->nvars) );
260    for( i = 0; i < tree->nvars; ++i )
261    {
262       /* it's not possible to add a variable twice to the varhash map */
263       if( SCIPhashmapExists(varhash, tree->vars[i]) )
264          continue;
265 
266       SCIP_CALL( SCIPhashmapInsertInt(varhash, tree->vars[i], i) );
267 
268       if( !SCIPvarIsActive((SCIP_VAR*)tree->vars[i]) )
269          havefixedvar = TRUE;
270    }
271 
272    if( !havefixedvar )
273    {
274       /* nothing to do */
275       if( varpos != NULL )
276          for( i = 0; i < tree->nvars; ++i )
277             varpos[i] = i;
278       SCIPhashmapFree(&varhash);
279       return SCIP_OKAY;
280    }
281 
282    /* we will do something */
283    *changed = TRUE;
284 
285    nvarsold = tree->nvars;
286 
287    /* array to store expressions that replace a variable expression in the tree */
288    SCIP_ALLOC( BMSallocBlockMemoryArray(tree->blkmem, &replaceexprs, nvarsold) );
289    BMSclearMemoryArray(replaceexprs, nvarsold);
290 
291    /* construct for each nonactive variable an expression that replaces this variable in the tree */
292    for( i = 0; i < nvarsold; ++i )
293    {
294       var = (SCIP_VAR*)tree->vars[i];
295 
296       if( SCIPvarIsActive(var) )
297          continue;
298 
299       scalar   = 1.0;
300       constant = 0.0;
301       SCIP_CALL( SCIPvarGetProbvarSum(&var, set, &scalar, &constant) );
302 
303       if( scalar == 0.0 )
304       {
305          /* variable is fixed, thus replace by constant expression in tree */
306          SCIP_CALL( SCIPexprCreate(tree->blkmem, &replaceexprs[i], SCIP_EXPR_CONST, constant) );
307          continue;
308       }
309 
310       if( SCIPvarIsActive(var) )
311       {
312          /* variable was aggregated or negated, thus replace by scalar * var + constant */
313          if( !SCIPhashmapExists(varhash, var) )
314          {
315             /* var not in tree yet, so add it */
316             SCIP_CALL( SCIPexprtreeAddVars(tree, 1, &var) );
317             idx = tree->nvars - 1;
318             SCIP_CALL( SCIPhashmapInsertInt(varhash, (void*)var, idx) );
319          }
320          else
321          {
322             idx = SCIPhashmapGetImageInt(varhash, (void*)var);
323          }
324          assert(idx >= 0 && idx < tree->nvars);
325          assert((SCIP_VAR*)tree->vars[idx] == var);
326 
327          SCIP_CALL( SCIPexprCreate(tree->blkmem, &replaceexprs[i], SCIP_EXPR_VARIDX, idx) );
328          if( scalar != 1.0 || constant != 0.0 )
329          {
330             /* multiply by scalar and add constant -> linear expression */
331             SCIP_CALL( SCIPexprCreateLinear(tree->blkmem, &replaceexprs[i], 1, &replaceexprs[i], &scalar, constant) );
332          }
333          continue;
334       }
335 
336       {
337          SCIP_EXPR** children;
338          SCIP_Real*  coefs;
339          int         nchildren;
340          SCIP_VAR*   mvar;
341          SCIP_Real   mscalar;
342 
343          /* var is now multi-aggregated, thus replace by scalar * (multaggrconst + sum_j multaggrscalar_j*multaggrvar_j) + constant */
344          assert( SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR );
345 
346          /* allocate array for children and coefficients */
347          SCIP_ALLOC( BMSallocBlockMemoryArray(tree->blkmem, &children, SCIPvarGetMultaggrNVars(var)) );  /*lint !e666 */
348          SCIP_ALLOC( BMSallocBlockMemoryArray(tree->blkmem, &coefs,    SCIPvarGetMultaggrNVars(var)) );  /*lint !e666 */
349          nchildren = 0;
350 
351          /* linear part
352           * turn each variable in SCIPvarGetMultaggrVars(var) into an active or multi-aggregated one and add corresponding term to summands */
353          for( j = 0; j < SCIPvarGetMultaggrNVars(var); ++j )
354          {
355             mvar      = SCIPvarGetMultaggrVars(var)[j];
356             mscalar   = scalar * SCIPvarGetMultaggrScalars(var)[j];
357             SCIP_CALL( SCIPvarGetProbvarSum(&mvar, set, &mscalar, &constant) );
358 
359             /* if variable mvar is fixed, constant has been added to constant and we can continue */
360             if( mscalar == 0.0 )
361                continue;
362 
363             assert(SCIPvarIsActive(mvar) || SCIPvarGetStatus(mvar) == SCIP_VARSTATUS_MULTAGGR);
364 
365             /* add mvar to tree, if not in tree yet */
366             if( !SCIPhashmapExists(varhash, mvar) )
367             {
368                /* var not in tree yet, so add it */
369                SCIP_CALL( SCIPexprtreeAddVars(tree, 1, &mvar) );
370                idx = tree->nvars - 1;
371                SCIP_CALL( SCIPhashmapInsertInt(varhash, (void*)mvar, idx) );
372             }
373             else
374             {
375                idx = SCIPhashmapGetImageInt(varhash, (void*)mvar);
376             }
377             assert(idx >= 0 && idx < tree->nvars);
378             assert((SCIP_VAR*)tree->vars[idx] == mvar);
379 
380             SCIP_CALL( SCIPexprCreate(tree->blkmem, &children[nchildren], SCIP_EXPR_VARIDX, idx) );
381             coefs[nchildren] = mscalar;
382             ++nchildren;
383          }
384 
385          /* constant part */
386          constant += scalar * SCIPvarGetMultaggrConstant(var);
387 
388          if( nchildren == 0 )
389          {
390             /* somehow all aggregation variables were fixed */
391             SCIP_CALL( SCIPexprCreate(tree->blkmem, &replaceexprs[i], SCIP_EXPR_CONST, constant) );
392          }
393          else if( nchildren == 1 && constant == 0.0 )
394          {
395             /* somehow everything collapsed to one summand -> use that one for replaceexprs[i]*/
396             replaceexprs[i] = children[0];
397          }
398          else
399          {
400             /* set replaceexprs[i] to linear expression in children */
401             SCIP_CALL( SCIPexprCreateLinear(tree->blkmem, &replaceexprs[i], nchildren, children, coefs, constant) );
402          }
403 
404          BMSfreeBlockMemoryArray(tree->blkmem, &children, SCIPvarGetMultaggrNVars(var));
405          BMSfreeBlockMemoryArray(tree->blkmem, &coefs,    SCIPvarGetMultaggrNVars(var));
406       }
407    }
408 
409    /* replace variables in tree by assembled expressions */
410    SCIP_CALL( SCIPexprtreeSubstituteVars(tree, replaceexprs) );
411    /* free replaceexprs */
412    for( i = 0; i < nvarsold; ++i )
413       if( replaceexprs[i] != NULL )
414          SCIPexprFreeDeep(tree->blkmem, &replaceexprs[i]);
415    BMSfreeBlockMemoryArray(tree->blkmem, &replaceexprs, nvarsold);
416 
417    /* the varhash is not needed anymore */
418    SCIPhashmapFree(&varhash);
419 
420    /* remove inactive variables from vars array and recompute variable indices */
421    SCIP_ALLOC( BMSallocBlockMemoryArray(tree->blkmem, &newpos, tree->nvars) );
422    offset = 0;
423    for( i = 0; i < tree->nvars; ++i )
424    {
425       if( SCIPvarIsActive((SCIP_VAR*)tree->vars[i]) || i >= nvarsold )
426       {
427          /* a new variable need to be either active or multi-aggregated */
428          assert(i < nvarsold || SCIPvarIsActive((SCIP_VAR*)tree->vars[i]) || SCIPvarGetStatus((SCIP_VAR*)tree->vars[i]) == SCIP_VARSTATUS_MULTAGGR);
429          newpos[i] = i - offset;
430       }
431       else
432       {
433          /* non-active variable are removed */
434          newpos[i] = -1;
435          ++offset;
436       }
437       if( varpos != NULL && i < nvarsold )
438          varpos[i] = newpos[i];
439    }
440    if( newvarsstart != NULL )
441       *newvarsstart -= offset;
442 
443    /* update indices in tree */
444    SCIPexprReindexVars(tree->root, newpos);
445 
446    /* move variable in expression tree vars array
447     * check if there is a fixed variable left */
448    havefixedvar = FALSE;
449    for( i = 0; i < tree->nvars; ++i )
450    {
451       if( newpos[i] == -1 )
452       {
453          /* variable was removed */
454          assert(!SCIPvarIsActive((SCIP_VAR*)tree->vars[i]));
455          continue;
456       }
457       /* variable is moved */
458       tree->vars[newpos[i]] = tree->vars[i];
459       if( !SCIPvarIsActive((SCIP_VAR*)tree->vars[i]) )
460          havefixedvar = TRUE;
461    }
462 
463    /* free newpos array; resize vars array */
464    BMSfreeBlockMemoryArray(tree->blkmem, &newpos, tree->nvars);
465    if( offset < tree->nvars )
466    {
467       SCIP_ALLOC( BMSreallocBlockMemoryArray(tree->blkmem, &tree->vars, tree->nvars, tree->nvars - offset) );
468       tree->nvars -= offset;
469    }
470    else
471    {
472       /* all variables were removed */
473       BMSfreeBlockMemoryArray(tree->blkmem, &tree->vars, tree->nvars);
474       tree->nvars = 0;
475    }
476 
477    if( havefixedvar )
478    {
479       /* if there are still fixed variables left, then this are newly added multi-aggregated variables
480        * it is then save to call this function recursively, since the original active variables should not be moved,
481        * i.e., varpos and *newvarsstart will remain valid
482        */
483       SCIP_Bool gotchange;
484 
485       SCIP_CALL( SCIPexprtreeRemoveFixedVars(tree, set, &gotchange, NULL, NULL) );
486       assert(gotchange);
487    }
488 
489    return SCIP_OKAY;
490 }
491 
492 /*
493  * private NLP nonlinear row methods
494  */
495 
496 /** announces, that the given linear coefficient in the constraint matrix changed */
497 static
nlrowLinearCoefChanged(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat,SCIP_VAR * var,SCIP_Real coef,SCIP_NLP * nlp)498 SCIP_RETCODE nlrowLinearCoefChanged(
499    SCIP_NLROW*           nlrow,              /**< nonlinear row */
500    SCIP_SET*             set,                /**< global SCIP settings */
501    SCIP_STAT*            stat,               /**< problem statistics data */
502    SCIP_VAR*             var,                /**< variable which coefficient changed */
503    SCIP_Real             coef,               /**< new coefficient of variable, 0.0 if deleted */
504    SCIP_NLP*             nlp                 /**< current NLP data */
505    )
506 {
507    assert(nlrow != NULL);
508    assert(var   != NULL);
509 
510    nlrow->activity = SCIP_INVALID;
511    nlrow->validactivitynlp = -1;
512    nlrow->pseudoactivity = SCIP_INVALID;
513    nlrow->validpsactivitydomchg = -1;
514    nlrow->minactivity = SCIP_INVALID;
515    nlrow->maxactivity = SCIP_INVALID;
516    nlrow->validactivitybdsdomchg = -1;
517 
518    if( nlrow->nlpindex >= 0 )
519    {
520       assert(nlp != NULL);
521 
522       /* notify NLP that row has changed */
523       SCIP_CALL( nlpRowChanged(nlp, set, stat, nlrow) );
524 
525       /* update NLPI problem, if row is in NLPI already */
526       if( nlrow->nlpiindex >= 0 )
527       {
528          int idx;
529 
530          /* get index of variable in NLPI */
531          assert(SCIPhashmapExists(nlp->varhash, var));
532          idx = SCIPhashmapGetImageInt(nlp->varhash, var);
533          assert(idx >= 0 && idx < nlp->nvars);
534 
535          idx = nlp->varmap_nlp2nlpi[idx];
536          assert(idx >= 0 && idx < nlp->nvars_solver);
537 
538          /* change coefficient in NLPI problem */
539          SCIP_CALL( SCIPnlpiChgLinearCoefs(nlp->solver, nlp->problem, nlrow->nlpiindex, 1, &idx, &coef) );
540       }
541    }
542 
543    return SCIP_OKAY;
544 }
545 
546 /** announces, that an element in the quadratic part of a nonlinear row changed */
547 static
nlrowQuadElemChanged(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat,SCIP_QUADELEM quadelem,SCIP_NLP * nlp)548 SCIP_RETCODE nlrowQuadElemChanged(
549    SCIP_NLROW*           nlrow,              /**< nonlinear row */
550    SCIP_SET*             set,                /**< global SCIP settings */
551    SCIP_STAT*            stat,               /**< problem statistics data */
552    SCIP_QUADELEM         quadelem,           /**< new element (variable indices and new values), quadelem.coef == 0 if it was deleted */
553    SCIP_NLP*             nlp                 /**< current NLP data */
554    )
555 {
556    assert(nlrow != NULL);
557    assert(quadelem.idx1 >= 0);
558    assert(quadelem.idx1 < nlrow->nquadvars);
559    assert(quadelem.idx2 >= 0);
560    assert(quadelem.idx2 < nlrow->nquadvars);
561 
562    nlrow->activity = SCIP_INVALID;
563    nlrow->validactivitynlp = -1;
564    nlrow->pseudoactivity = SCIP_INVALID;
565    nlrow->validpsactivitydomchg = -1;
566    nlrow->minactivity = SCIP_INVALID;
567    nlrow->maxactivity = SCIP_INVALID;
568    nlrow->validactivitybdsdomchg = -1;
569 
570    if( nlrow->nlpindex >= 0 )
571    {
572       assert(nlp != NULL);
573 
574       /* notify NLP that row has changed */
575       SCIP_CALL( nlpRowChanged(nlp, set, stat, nlrow) );
576 
577       /* update NLPI problem, if row is in NLPI already */
578       if( nlrow->nlpiindex >= 0 )
579       {
580          SCIP_QUADELEM elem;
581 
582          /* get NLPI index of first variable */
583          assert(nlrow->quadvars[quadelem.idx1] != NULL);
584          assert(SCIPhashmapExists(nlp->varhash, nlrow->quadvars[quadelem.idx1]));
585          elem.idx1 = SCIPhashmapGetImageInt(nlp->varhash, nlrow->quadvars[quadelem.idx1]);
586          assert(elem.idx1 >= 0 && elem.idx1 < nlp->nvars);
587 
588          elem.idx1 = nlp->varmap_nlp2nlpi[elem.idx1];
589          assert(elem.idx1 >= 0 && elem.idx1 < nlp->nvars_solver);
590 
591          /* get NLPI index of second variable */
592          assert(nlrow->quadvars[quadelem.idx2] != NULL);
593          assert(SCIPhashmapExists(nlp->varhash, nlrow->quadvars[quadelem.idx2]));
594          elem.idx2 = SCIPhashmapGetImageInt(nlp->varhash, nlrow->quadvars[quadelem.idx2]);
595          assert(elem.idx2 >= 0 && elem.idx2 < nlp->nvars);
596 
597          elem.idx2 = nlp->varmap_nlp2nlpi[elem.idx2];
598          assert(elem.idx2 >= 0 && elem.idx2 < nlp->nvars_solver);
599 
600          /* make sure idx1 <= idx2 */
601          if( elem.idx1 > elem.idx2 )
602          {
603             int tmp;
604             tmp = elem.idx2;
605             elem.idx2 = elem.idx1;
606             elem.idx1 = tmp;
607          }
608 
609          elem.coef = quadelem.coef;
610 
611          /* change coefficient in NLPI problem */
612          SCIP_CALL( SCIPnlpiChgQuadCoefs(nlp->solver, nlp->problem, nlrow->nlpiindex, 1, &elem) );
613       }
614    }
615 
616    return SCIP_OKAY;
617 }
618 
619 /** announces, that an expression tree changed */
620 static
nlrowExprtreeChanged(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp)621 SCIP_RETCODE nlrowExprtreeChanged(
622    SCIP_NLROW*           nlrow,              /**< nonlinear row */
623    SCIP_SET*             set,                /**< global SCIP settings */
624    SCIP_STAT*            stat,               /**< problem statistics data */
625    SCIP_NLP*             nlp                 /**< current NLP data */
626    )
627 {
628    assert(nlrow != NULL);
629 
630    nlrow->activity = SCIP_INVALID;
631    nlrow->validactivitynlp = -1;
632    nlrow->pseudoactivity = SCIP_INVALID;
633    nlrow->validpsactivitydomchg = -1;
634    nlrow->minactivity = SCIP_INVALID;
635    nlrow->maxactivity = SCIP_INVALID;
636    nlrow->validactivitybdsdomchg = -1;
637 
638    if( nlrow->nlpindex >= 0 )
639    {
640       assert(nlp != NULL);
641 
642       /* notify NLP that row has changed */
643       SCIP_CALL( nlpRowChanged(nlp, set, stat, nlrow) );
644 
645       if( nlrow->nlpiindex >= 0 )
646       {
647          /* change expression tree in NLPI problem */
648          int* nlinidxs;
649 
650          /* get indices of variables in expression tree part of row */
651          if( nlrow->exprtree != NULL )
652          {
653             int i;
654             int n;
655             SCIP_VAR* var;
656 
657             n = SCIPexprtreeGetNVars(nlrow->exprtree);
658             assert(n == 0 || SCIPexprtreeGetVars(nlrow->exprtree) != NULL);
659 
660             SCIP_CALL( SCIPsetAllocBufferArray(set, &nlinidxs, n) );
661 
662             for( i = 0; i < n; ++i )
663             {
664                var = SCIPexprtreeGetVars(nlrow->exprtree)[i];
665                assert(var != NULL);
666                assert(SCIPvarIsActive(var)); /* at this point, there should be only active variables in the row */
667 
668                assert(SCIPhashmapExists(nlp->varhash, var));
669                nlinidxs[i] = nlp->varmap_nlp2nlpi[SCIPhashmapGetImageInt(nlp->varhash, var)];
670             }
671 
672             SCIP_CALL( SCIPnlpiChgExprtree(nlp->solver, nlp->problem, nlrow->nlpiindex, nlinidxs, nlrow->exprtree) );
673 
674             SCIPsetFreeBufferArray(set, &nlinidxs);
675          }
676          else
677          {
678             SCIP_CALL( SCIPnlpiChgExprtree(nlp->solver, nlp->problem, nlrow->nlpiindex, NULL, NULL) );
679          }
680       }
681    }
682 
683    return SCIP_OKAY;
684 }
685 
686 /** announces, that a parameter in an expression tree has changed */
687 static
nlrowExprtreeParamChanged(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat,int paramidx,SCIP_NLP * nlp)688 SCIP_RETCODE nlrowExprtreeParamChanged(
689    SCIP_NLROW*           nlrow,              /**< nonlinear row */
690    SCIP_SET*             set,                /**< global SCIP settings */
691    SCIP_STAT*            stat,               /**< problem statistics data */
692    int                   paramidx,           /**< index of parameter which has changed, or -1 if all changed */
693    SCIP_NLP*             nlp                 /**< current NLP data */
694    )
695 {
696    assert(nlrow != NULL);
697    assert(nlrow->exprtree != NULL);
698    assert(paramidx >= -1);
699    assert(paramidx <  SCIPexprtreeGetNParams(nlrow->exprtree));
700 
701    nlrow->activity = SCIP_INVALID;
702    nlrow->validactivitynlp = -1;
703    nlrow->pseudoactivity = SCIP_INVALID;
704    nlrow->validpsactivitydomchg = -1;
705    nlrow->minactivity = SCIP_INVALID;
706    nlrow->maxactivity = SCIP_INVALID;
707    nlrow->validactivitybdsdomchg = -1;
708 
709    if( nlrow->nlpindex >= 0 )
710    {
711       assert(nlp != NULL);
712 
713       /* notify NLP that row has changed */
714       SCIP_CALL( nlpRowChanged(nlp, set, stat, nlrow) );
715 
716       if( nlrow->nlpiindex >= 0 )
717       {
718          if( paramidx >= 0 )
719          {
720             /* change coefficient in NLPI problem */
721             SCIP_CALL( SCIPnlpiChgNonlinCoef(nlp->solver, nlp->problem, nlrow->nlpiindex, paramidx, SCIPexprtreeGetParamVals(nlrow->exprtree)[paramidx]) );
722          }
723          else
724          {
725             SCIP_Real* paramvals;
726             int i;
727             int n;
728 
729             /* change all coefficients in NLPI problem */
730             n = SCIPexprtreeGetNParams(nlrow->exprtree);
731             paramvals = SCIPexprtreeGetParamVals(nlrow->exprtree);
732             for( i = 0; i < n; ++i )
733             {
734                SCIP_CALL( SCIPnlpiChgNonlinCoef(nlp->solver, nlp->problem, nlrow->nlpiindex, i, paramvals[i]) );
735             }
736          }
737       }
738    }
739 
740    return SCIP_OKAY;
741 }
742 
743 /** notifies nonlinear row, that its sides were changed */
744 static
nlrowSideChanged(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp)745 SCIP_RETCODE nlrowSideChanged(
746    SCIP_NLROW*           nlrow,              /**< nonlinear row */
747    SCIP_SET*             set,                /**< global SCIP settings */
748    SCIP_STAT*            stat,               /**< problem statistics data */
749    SCIP_NLP*             nlp                 /**< current NLP data */
750    )
751 {
752    assert(nlrow != NULL);
753 
754    if( nlrow->nlpindex >= 0 )
755    {
756       assert(nlp != NULL);
757 
758       /* notify NLP that row has changed */
759       SCIP_CALL( nlpRowChanged(nlp, set, stat, nlrow) );
760 
761       if( nlrow->nlpiindex >= 0 )
762       {
763          SCIP_Real lhs;
764          SCIP_Real rhs;
765 
766          /* change sides in NLPI problem */
767          lhs = nlrow->lhs;
768          rhs = nlrow->rhs;
769          if( !SCIPsetIsInfinity(set, -lhs) )
770             lhs -= nlrow->constant;
771          if( !SCIPsetIsInfinity(set,  rhs) )
772             rhs -= nlrow->constant;
773 
774          SCIP_CALL( SCIPnlpiChgConsSides(nlp->solver, nlp->problem, 1, &nlrow->nlpiindex, &lhs, &rhs) );
775       }
776    }
777 
778    return SCIP_OKAY;
779 }
780 
781 /** notifies nonlinear row, that its constant was changed */
782 static
nlrowConstantChanged(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp)783 SCIP_RETCODE nlrowConstantChanged(
784    SCIP_NLROW*           nlrow,              /**< nonlinear row */
785    SCIP_SET*             set,                /**< global SCIP settings */
786    SCIP_STAT*            stat,               /**< problem statistics data */
787    SCIP_NLP*             nlp                 /**< current NLP data */
788    )
789 {
790    assert(nlrow != NULL);
791 
792    nlrow->activity = SCIP_INVALID;
793    nlrow->validactivitynlp = -1;
794    nlrow->pseudoactivity = SCIP_INVALID;
795    nlrow->validpsactivitydomchg = -1;
796    nlrow->minactivity = SCIP_INVALID;
797    nlrow->maxactivity = SCIP_INVALID;
798    nlrow->validactivitybdsdomchg = -1;
799 
800    if( nlrow->nlpindex >= 0 )
801    {
802       assert(nlp != NULL);
803 
804       /* notify NLP that row has changed */
805       SCIP_CALL( nlpRowChanged(nlp, set, stat, nlrow) );
806 
807       if( nlrow->nlpiindex >= 0 )
808       {
809          SCIP_Real lhs;
810          SCIP_Real rhs;
811 
812          lhs = nlrow->lhs;
813          rhs = nlrow->rhs;
814          if( !SCIPsetIsInfinity(set, -lhs) )
815             lhs -= nlrow->constant;
816          if( !SCIPsetIsInfinity(set,  rhs) )
817             rhs -= nlrow->constant;
818 
819          /* change sides in NLPI problem */
820          SCIP_CALL( SCIPnlpiChgConsSides(nlp->solver, nlp->problem, 1, &nlrow->nlpiindex, &lhs, &rhs) );
821       }
822    }
823 
824    return SCIP_OKAY;
825 }
826 
827 /** sorts linear part of row entries such that lower variable indices precede higher ones */
828 static
nlrowSortLinear(SCIP_NLROW * nlrow)829 void nlrowSortLinear(
830    SCIP_NLROW*           nlrow               /**< nonlinear row to be sorted */
831    )
832 {
833    assert(nlrow != NULL);
834 
835    /* check, if row is already sorted in the LP part, or if the sorting should be delayed */
836    if( nlrow->linvarssorted )
837       return;
838 
839    /* sort linear coefficients */
840    SCIPsortPtrReal((void**)nlrow->linvars, nlrow->lincoefs, SCIPvarComp, nlrow->nlinvars);
841 
842    nlrow->linvarssorted = TRUE;
843 }
844 
845 /** searches linear variable in nonlinear row, returns position in linvars vector or -1 if not found */
846 static
nlrowSearchLinearCoef(SCIP_NLROW * nlrow,SCIP_VAR * var)847 int nlrowSearchLinearCoef(
848    SCIP_NLROW*           nlrow,              /**< nonlinear row to be searched in */
849    SCIP_VAR*             var                 /**< variable to be searched for */
850    )
851 {
852    int pos;
853 
854    assert(nlrow != NULL);
855    assert(var   != NULL);
856 
857    if( nlrow->nlinvars == 0 )
858       return -1;
859 
860    nlrowSortLinear(nlrow);
861    if( !SCIPsortedvecFindPtr((void**)nlrow->linvars, SCIPvarComp, (void*)var, nlrow->nlinvars, &pos) )
862       return -1;
863 
864    return pos;
865 }
866 
867 /** moves a coefficient in a nonlinear row to a different place, and updates all corresponding data structures */
868 static
nlrowMoveLinearCoef(SCIP_NLROW * nlrow,int oldpos,int newpos)869 void nlrowMoveLinearCoef(
870    SCIP_NLROW*           nlrow,              /**< NLP row */
871    int                   oldpos,             /**< old position of coefficient */
872    int                   newpos              /**< new position of coefficient */
873    )
874 {
875    assert(nlrow != NULL);
876    assert(0 <= oldpos && oldpos < nlrow->nlinvars);
877    assert(0 <= newpos && newpos < nlrow->nlinvars);
878    assert(nlrow->linvars[oldpos] != NULL);
879 
880    if( oldpos == newpos )
881       return;
882 
883    nlrow->linvars[newpos]  = nlrow->linvars[oldpos];
884    nlrow->lincoefs[newpos] = nlrow->lincoefs[oldpos];
885 
886    /* update sorted flags */
887    nlrow->linvarssorted = FALSE;
888 }
889 
890 /** adds a previously non existing linear coefficient to a nonlinear row */
891 static
nlrowAddLinearCoef(SCIP_NLROW * nlrow,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp,SCIP_VAR * var,SCIP_Real coef)892 SCIP_RETCODE nlrowAddLinearCoef(
893    SCIP_NLROW*           nlrow,              /**< nonlinear row */
894    BMS_BLKMEM*           blkmem,             /**< block memory */
895    SCIP_SET*             set,                /**< global SCIP settings */
896    SCIP_STAT*            stat,               /**< problem statistics data */
897    SCIP_NLP*             nlp,                /**< current NLP data */
898    SCIP_VAR*             var,                /**< variable */
899    SCIP_Real             coef                /**< value of coefficient */
900    )
901 {
902    int pos;
903 
904    assert(nlrow  != NULL);
905    assert(blkmem != NULL);
906    assert(var    != NULL);
907    assert(!SCIPsetIsZero(set, coef));
908 
909    /* assert that only active variables are added once the row is in the NLP */
910    assert(nlrow->nlpindex == -1 || SCIPvarIsActive(var) );
911 
912    SCIP_CALL( SCIPnlrowEnsureLinearSize(nlrow, blkmem, set, nlrow->nlinvars+1) );
913    assert(nlrow->linvars  != NULL);
914    assert(nlrow->lincoefs != NULL);
915 
916    pos = nlrow->nlinvars;
917    nlrow->nlinvars++;
918 
919    /* insert the variable */
920    nlrow->linvars [pos] = var;
921    nlrow->lincoefs[pos] = coef;
922 
923    SCIP_CALL( nlrowLinearCoefChanged(nlrow, set, stat, var, coef, nlp) );
924 
925    /* update sorted flag */
926    if( pos > 0 && SCIPvarCompare(nlrow->linvars[pos-1], nlrow->linvars[pos]) > 0 )
927       nlrow->linvarssorted = FALSE;
928 
929    SCIPsetDebugMsg(set, "added linear coefficient %g * <%s> at position %d to nonlinear row <%s>\n",
930       coef, SCIPvarGetName(var), pos, nlrow->name);
931 
932    return SCIP_OKAY;
933 }
934 
935 /** adds a linear coefficient to a nonlinear row
936  * if the variable exists in the linear part of the row already, the coefficients are added
937  * otherwise the variable is added to the row */
938 static
nlrowAddToLinearCoef(SCIP_NLROW * nlrow,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp,SCIP_VAR * var,SCIP_Real coef,SCIP_Bool removefixed)939 SCIP_RETCODE nlrowAddToLinearCoef(
940    SCIP_NLROW*           nlrow,              /**< nonlinear row */
941    BMS_BLKMEM*           blkmem,             /**< block memory */
942    SCIP_SET*             set,                /**< global SCIP settings */
943    SCIP_STAT*            stat,               /**< problem statistics data */
944    SCIP_NLP*             nlp,                /**< current NLP data */
945    SCIP_VAR*             var,                /**< variable */
946    SCIP_Real             coef,               /**< value of coefficient */
947    SCIP_Bool             removefixed         /**< whether to disaggregate var before adding */
948    )
949 {
950    int pos;
951 
952    assert(nlrow  != NULL);
953    assert(blkmem != NULL);
954    assert(var    != NULL);
955 
956    if( removefixed && !SCIPvarIsActive(var) )
957    {
958       SCIP_Real constant;
959 
960       constant = 0.0;
961       SCIP_CALL( SCIPvarGetProbvarSum(&var, set, &coef, &constant) );
962       if( constant != 0.0 )
963       {
964          nlrow->constant += constant;
965          SCIP_CALL( nlrowConstantChanged(nlrow, set, stat, nlp) );
966       }
967 
968       if( SCIPsetIsZero(set, coef) )
969          return SCIP_OKAY;
970 
971       if( !SCIPvarIsActive(var) )
972       {
973          int j;
974 
975          /* if var is still not active, then it is multi-aggregated */
976          assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);
977 
978          if( SCIPvarGetMultaggrConstant(var) != 0.0 )
979          {
980             nlrow->constant += coef * SCIPvarGetMultaggrConstant(var);
981             SCIP_CALL( nlrowConstantChanged(nlrow, set, stat, nlp) );
982          }
983 
984          for( j = 0; j < SCIPvarGetMultaggrNVars(var); ++j )
985          {
986             SCIP_CALL( nlrowAddToLinearCoef(nlrow, blkmem, set, stat, nlp, SCIPvarGetMultaggrVars(var)[j], SCIPvarGetMultaggrScalars(var)[j] * coef, TRUE) );
987          }
988 
989          return SCIP_OKAY;
990       }
991    }
992    else if( SCIPsetIsZero(set, coef) )
993       return SCIP_OKAY;
994 
995    assert(!removefixed || SCIPvarIsActive(var));
996 
997    pos = nlrowSearchLinearCoef(nlrow, var);
998 
999    if( pos == -1 )
1000    {
1001       /* add as new coefficient */
1002       SCIP_CALL( nlrowAddLinearCoef(nlrow, blkmem, set, stat, nlp, var, coef) );
1003    }
1004    else
1005    {
1006       assert(pos >= 0);
1007       assert(pos <  nlrow->nlinvars);
1008       assert(nlrow->linvars[pos] == var);
1009 
1010       /* add to previously existing coefficient */
1011       nlrow->lincoefs[pos] += coef;
1012    }
1013 
1014    return SCIP_OKAY;
1015 }
1016 
1017 /** deletes coefficient at given position from row */
1018 static
nlrowDelLinearCoefPos(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp,int pos)1019 SCIP_RETCODE nlrowDelLinearCoefPos(
1020    SCIP_NLROW*           nlrow,              /**< nonlinear row to be changed */
1021    SCIP_SET*             set,                /**< global SCIP settings */
1022    SCIP_STAT*            stat,               /**< problem statistics data */
1023    SCIP_NLP*             nlp,                /**< current NLP data */
1024    int                   pos                 /**< position in row vector to delete */
1025    )
1026 {
1027    SCIP_VAR* var;
1028 
1029    assert(nlrow != NULL);
1030    assert(set != NULL);
1031    assert(0 <= pos && pos < nlrow->nlinvars);
1032    assert(nlrow->linvars[pos] != NULL);
1033 
1034    var = nlrow->linvars[pos];
1035 
1036    /* move last coefficient to position of empty slot (should set sorted flag to FALSE, if not last variable was deleted) */
1037    nlrowMoveLinearCoef(nlrow, nlrow->nlinvars-1, pos);
1038    nlrow->nlinvars--;
1039    assert(pos == nlrow->nlinvars || nlrow->linvarssorted == FALSE);
1040 
1041    SCIP_CALL( nlrowLinearCoefChanged(nlrow, set, stat, var, 0.0, nlp) );
1042 
1043    return SCIP_OKAY;
1044 }
1045 
1046 /** changes a coefficient at given position of a nonlinear row */
1047 static
nlrowChgLinearCoefPos(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp,int pos,SCIP_Real coef)1048 SCIP_RETCODE nlrowChgLinearCoefPos(
1049    SCIP_NLROW*           nlrow,              /**< NLP row */
1050    SCIP_SET*             set,                /**< global SCIP settings */
1051    SCIP_STAT*            stat,               /**< problem statistics data */
1052    SCIP_NLP*             nlp,                /**< current NLP data */
1053    int                   pos,                /**< position in row vector to change */
1054    SCIP_Real             coef                /**< new value of coefficient */
1055    )
1056 {
1057    assert(nlrow != NULL);
1058    assert(0 <= pos && pos < nlrow->nlinvars);
1059    assert(nlrow->linvars[pos] != NULL);
1060 
1061    if( SCIPsetIsZero(set, coef) )
1062    {
1063       /* delete existing coefficient */
1064       SCIP_CALL( nlrowDelLinearCoefPos(nlrow, set, stat, nlp, pos) );
1065    }
1066    else if( !SCIPsetIsEQ(set, nlrow->lincoefs[pos], coef) )
1067    {
1068       /* change existing coefficient */
1069       nlrow->lincoefs[pos] = coef;
1070       SCIP_CALL( nlrowLinearCoefChanged(nlrow, set, stat, nlrow->linvars[pos], coef, nlp) );
1071    }
1072 
1073    return SCIP_OKAY;
1074 }
1075 
1076 /** sets up the variable hash for quadratic variables, if the number of variables exceeds some given threshold */
1077 static
nlrowSetupQuadVarsHash(SCIP_NLROW * nlrow,BMS_BLKMEM * blkmem)1078 SCIP_RETCODE nlrowSetupQuadVarsHash(
1079    SCIP_NLROW*           nlrow,              /**< nonlinear row */
1080    BMS_BLKMEM*           blkmem              /**< block memory */
1081    )
1082 {
1083    int i;
1084    assert(blkmem != NULL);
1085    assert(nlrow  != NULL);
1086    assert(nlrow->quadvarshash == NULL);
1087 
1088    if( nlrow->nquadvars < 3 )
1089       return SCIP_OKAY;
1090 
1091    SCIP_CALL( SCIPhashmapCreate(&nlrow->quadvarshash, blkmem, nlrow->nquadvars) );
1092    assert(nlrow->quadvarshash != NULL);
1093 
1094    for( i = 0; i < nlrow->nquadvars; ++i )
1095    {
1096       SCIP_CALL( SCIPhashmapInsertInt(nlrow->quadvarshash, (void*)nlrow->quadvars[i], i) );
1097    }
1098 
1099    return SCIP_OKAY;
1100 }
1101 
1102 /** sorts quadratic part of row entries */
1103 static
nlrowSortQuadElem(SCIP_NLROW * nlrow)1104 void nlrowSortQuadElem(
1105    SCIP_NLROW*           nlrow               /**< nonlinear row to be sorted */
1106    )
1107 {
1108    assert(nlrow != NULL);
1109    assert(nlrow->quadelems != NULL);
1110 
1111    /* check, if row is already sorted in the LP part, or if the sorting should be delayed */
1112    if( nlrow->quadelemssorted )
1113       return;
1114 
1115    /* sort quadratic elements */
1116    SCIPquadelemSort(nlrow->quadelems, nlrow->nquadelems);
1117 
1118    nlrow->quadelemssorted = TRUE;
1119 }
1120 
1121 /** searches quadratic elements in nonlinear row, returns position of given index pair in quadelems array or -1 if not found */
1122 static
nlrowSearchQuadElem(SCIP_NLROW * nlrow,int idx1,int idx2)1123 int nlrowSearchQuadElem(
1124    SCIP_NLROW*           nlrow,              /**< nonlinear row to be searched in */
1125    int                   idx1,               /**< index of first  variable to be searched for */
1126    int                   idx2                /**< index of second variable to be searched for */
1127    )
1128 {
1129    int pos;
1130 
1131    assert(nlrow != NULL);
1132    assert(idx1 >= 0);
1133    assert(idx1 <  nlrow->nquadvars);
1134    assert(idx2 >= 0);
1135    assert(idx2 <  nlrow->nquadvars);
1136 
1137    nlrowSortQuadElem(nlrow);
1138    if( !SCIPquadelemSortedFind(nlrow->quadelems, idx1, idx2, nlrow->nquadelems, &pos) )
1139       pos = -1;
1140 
1141    return pos;
1142 }
1143 
1144 /** moves a quadratic element in a nonlinear row to a different place, and updates all corresponding data structures */
1145 static
nlrowMoveQuadElement(SCIP_NLROW * nlrow,int oldpos,int newpos)1146 void nlrowMoveQuadElement(
1147    SCIP_NLROW*           nlrow,              /**< NLP row */
1148    int                   oldpos,             /**< old position of coefficient */
1149    int                   newpos              /**< new position of coefficient */
1150    )
1151 {
1152    assert(nlrow != NULL);
1153    assert(0 <= oldpos && oldpos < nlrow->nquadelems);
1154    assert(0 <= newpos && newpos < nlrow->nquadelems);
1155 
1156    if( oldpos == newpos )
1157       return;
1158 
1159    nlrow->quadelems[newpos] = nlrow->quadelems[oldpos];
1160 
1161    /* update sorted flags */
1162    nlrow->quadelemssorted = FALSE;
1163 }
1164 
1165 /** adds a previously non existing quadratic element to a nonlinear row */
1166 static
nlrowAddQuadElement(SCIP_NLROW * nlrow,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp,SCIP_QUADELEM elem)1167 SCIP_RETCODE nlrowAddQuadElement(
1168    SCIP_NLROW*           nlrow,              /**< nonlinear row */
1169    BMS_BLKMEM*           blkmem,             /**< block memory */
1170    SCIP_SET*             set,                /**< global SCIP settings */
1171    SCIP_STAT*            stat,               /**< problem statistics data */
1172    SCIP_NLP*             nlp,                /**< current NLP data */
1173    SCIP_QUADELEM         elem                /**< quadratic element to add */
1174    )
1175 {
1176    int pos;
1177 
1178    assert(nlrow  != NULL);
1179    assert(blkmem != NULL);
1180    assert(elem.idx1 >= 0);
1181    assert(elem.idx1 <  nlrow->nquadvars);
1182    assert(elem.idx2 >= 0);
1183    assert(elem.idx2 <  nlrow->nquadvars);
1184 
1185    if( SCIPsetIsZero(set, elem.coef) )
1186       return SCIP_OKAY;
1187 
1188    SCIP_CALL( SCIPnlrowEnsureQuadElementsSize(nlrow, blkmem, set, nlrow->nquadelems+1) );
1189    assert(nlrow->quadelems != NULL);
1190 
1191    pos = nlrow->nquadelems;
1192    nlrow->nquadelems++;
1193 
1194    /* insert the element */
1195    nlrow->quadelems[pos] = elem;
1196 
1197    /* notify row and NLP */
1198    SCIP_CALL( nlrowQuadElemChanged(nlrow, set, stat, elem, nlp) );
1199 
1200    /* update sorted flag */
1201    if( pos > 0 )
1202       nlrow->quadelemssorted = FALSE;
1203 
1204    SCIPsetDebugMsg(set, "added quadratic element %g * <%s> * <%s> at position %d to nonlinear row <%s>\n",
1205       elem.coef, SCIPvarGetName(nlrow->quadvars[elem.idx1]), SCIPvarGetName(nlrow->quadvars[elem.idx2]), pos, nlrow->name);
1206 
1207    return SCIP_OKAY;
1208 }
1209 
1210 /** deletes coefficient at given position from row */
1211 static
nlrowDelQuadElemPos(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp,int pos)1212 SCIP_RETCODE nlrowDelQuadElemPos(
1213    SCIP_NLROW*           nlrow,              /**< nonlinear row to be changed */
1214    SCIP_SET*             set,                /**< global SCIP settings */
1215    SCIP_STAT*            stat,               /**< problem statistics data */
1216    SCIP_NLP*             nlp,                /**< current NLP data */
1217    int                   pos                 /**< position in row vector to delete */
1218    )
1219 {
1220    SCIP_QUADELEM elem;
1221 
1222    assert(nlrow != NULL);
1223    assert(set != NULL);
1224    assert(0 <= pos && pos < nlrow->nquadelems);
1225 
1226    SCIPsetDebugMsg(set, "delete quad element (%d,%d) at pos %d\n", nlrow->quadelems[pos].idx1, nlrow->quadelems[pos].idx2, pos);
1227 
1228    elem = nlrow->quadelems[pos];
1229 
1230    /* move last coefficient to position of empty slot (should set sorted flag to FALSE, if not last element was deleted) */
1231    nlrowMoveQuadElement(nlrow, nlrow->nquadelems-1, pos);
1232    nlrow->nquadelems--;
1233    assert(pos == nlrow->nquadelems || nlrow->quadelemssorted == FALSE);
1234 
1235    /* notify row and NLP */
1236    elem.coef = 0.0;
1237    SCIP_CALL( nlrowQuadElemChanged(nlrow, set, stat, elem, nlp) );
1238 
1239    return SCIP_OKAY;
1240 }
1241 
1242 /** changes a coefficient at given position of quadratic element in nonlinear row */
1243 static
nlrowChgQuadElemPos(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp,int pos,SCIP_Real coef)1244 SCIP_RETCODE nlrowChgQuadElemPos(
1245    SCIP_NLROW*           nlrow,              /**< NLP row */
1246    SCIP_SET*             set,                /**< global SCIP settings */
1247    SCIP_STAT*            stat,               /**< problem statistics data */
1248    SCIP_NLP*             nlp,                /**< current NLP data */
1249    int                   pos,                /**< position in quadratic elements array to change */
1250    SCIP_Real             coef                /**< new value of coefficient */
1251    )
1252 {
1253    assert(nlrow != NULL);
1254    assert(0 <= pos && pos < nlrow->nquadelems);
1255 
1256    SCIPsetDebugMsg(set, "change quad element (%d,%d) at pos %d to %g\n", nlrow->quadelems[pos].idx1, nlrow->quadelems[pos].idx2, pos, coef);
1257 
1258    if( SCIPsetIsZero(set, coef) )
1259    {
1260       /* delete existing coefficient */
1261       SCIP_CALL( nlrowDelQuadElemPos(nlrow, set, stat, nlp, pos) );
1262    }
1263    else if( !SCIPsetIsEQ(set, nlrow->quadelems[pos].coef, coef) )
1264    {
1265       /* change existing coefficient */
1266       nlrow->quadelems[pos].coef = coef;
1267       SCIP_CALL( nlrowQuadElemChanged(nlrow, set, stat, nlrow->quadelems[pos], nlp) );
1268    }
1269 
1270    return SCIP_OKAY;
1271 }
1272 
1273 /** calculates minimal and maximal activity of row w.r.t. the variable's bounds */
1274 static
nlrowCalcActivityBounds(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat)1275 SCIP_RETCODE nlrowCalcActivityBounds(
1276    SCIP_NLROW*           nlrow,              /**< nonlinear row */
1277    SCIP_SET*             set,                /**< global SCIP settings */
1278    SCIP_STAT*            stat                /**< problem statistics data */
1279    )
1280 {
1281    SCIP_Real inf;
1282    SCIP_INTERVAL activity;
1283    SCIP_INTERVAL bounds;
1284    int i;
1285 
1286    assert(nlrow != NULL);
1287    assert(set   != NULL);
1288    assert(stat  != NULL);
1289 
1290    inf = SCIPsetInfinity(set);
1291 
1292    /* calculate activity bounds */
1293    SCIPintervalSet(&activity, nlrow->constant);
1294    for( i = 0; i < nlrow->nlinvars && !SCIPintervalIsEntire(inf, activity); ++i )
1295    {
1296       SCIPintervalSetBounds(&bounds, SCIPvarGetLbLocal(nlrow->linvars[i]), SCIPvarGetUbLocal(nlrow->linvars[i]));
1297       SCIPintervalMulScalar(inf, &bounds, bounds, nlrow->lincoefs[i]);
1298       SCIPintervalAdd(inf, &activity, activity, bounds);
1299    }
1300 
1301    /* @todo make sure quadelems is sorted */
1302    for( i = 0; i < nlrow->nquadelems && !SCIPintervalIsEntire(inf, activity); )
1303    {
1304       SCIP_Real a;
1305       SCIP_INTERVAL b, tmp;
1306       int idx1;
1307 
1308       idx1 = nlrow->quadelems[i].idx1;
1309       SCIPintervalSetBounds(&bounds, SCIPvarGetLbLocal(nlrow->quadvars[idx1]), SCIPvarGetUbLocal(nlrow->quadvars[idx1]));
1310 
1311       /* for x_i*(a*x_i + sum_j b_jx_j) we assemble a and sum_j b_jx_j */
1312       a = 0.0;
1313       SCIPintervalSet(&b, 0.0);
1314       do
1315       {
1316          if( nlrow->quadelems[i].idx1 == nlrow->quadelems[i].idx2 )
1317          {
1318             a = nlrow->quadelems[i].coef;
1319          }
1320          else
1321          {
1322             SCIPintervalSetBounds(&tmp, SCIPvarGetLbLocal(nlrow->quadvars[nlrow->quadelems[i].idx2]), SCIPvarGetUbLocal(nlrow->quadvars[nlrow->quadelems[i].idx2]));
1323             SCIPintervalMulScalar(inf, &tmp, tmp, nlrow->quadelems[i].coef);
1324             SCIPintervalAdd(inf, &b, b, tmp);
1325          }
1326          ++i;
1327       }
1328       while( i < nlrow->nquadvars && idx1 == nlrow->quadelems[i].idx1 );
1329 
1330       /* compute bounds for a*x_i^2 + b*x_i and add to activity bounds */
1331       SCIPintervalQuad(inf, &bounds, a, b, bounds);
1332       SCIPintervalAdd(inf, &activity, activity, bounds);
1333    }
1334 
1335    if( nlrow->exprtree != NULL && !SCIPintervalIsEntire(inf, activity))
1336    {
1337       SCIP_INTERVAL* varvals;
1338       int n;
1339 
1340       n = SCIPexprtreeGetNVars(nlrow->exprtree);
1341 
1342       SCIP_CALL( SCIPsetAllocBufferArray(set, &varvals, n) );
1343 
1344       for( i = 0; i < n; ++i )
1345       {
1346          SCIPintervalSetBounds(&varvals[i], SCIPvarGetLbLocal(SCIPexprtreeGetVars(nlrow->exprtree)[i]), SCIPvarGetUbLocal(SCIPexprtreeGetVars(nlrow->exprtree)[i]));
1347       }
1348 
1349       SCIP_CALL( SCIPexprtreeEvalInt(nlrow->exprtree, inf, varvals, &bounds) );
1350       SCIPintervalAdd(inf, &activity, activity, bounds);
1351 
1352       SCIPsetFreeBufferArray(set, &varvals);
1353    }
1354 
1355    nlrow->minactivity = SCIPintervalGetInf(activity);
1356    nlrow->maxactivity = SCIPintervalGetSup(activity);
1357 
1358    nlrow->validactivitybdsdomchg = stat->domchgcount;
1359 
1360    return SCIP_OKAY;
1361 }
1362 
1363 /** makes sure that there is no fixed variable at position pos of the linear part of a nonlinear row
1364  * a fixed variable is replaced with the corresponding constant or disaggregated term
1365  */
1366 static
nlrowRemoveFixedLinearCoefPos(SCIP_NLROW * nlrow,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp,int pos)1367 SCIP_RETCODE nlrowRemoveFixedLinearCoefPos(
1368    SCIP_NLROW*           nlrow,              /**< nonlinear row */
1369    BMS_BLKMEM*           blkmem,             /**< block memory */
1370    SCIP_SET*             set,                /**< global SCIP settings */
1371    SCIP_STAT*            stat,               /**< problem statistics data */
1372    SCIP_NLP*             nlp,                /**< current NLP data */
1373    int                   pos                 /**< position of variable in linear variables array */
1374    )
1375 {
1376    SCIP_Real oldconstant;
1377    SCIP_VAR* var;
1378 
1379    assert(nlrow  != NULL);
1380    assert(blkmem != NULL);
1381    assert(pos >= 0);
1382    assert(pos <  nlrow->nlinvars);
1383 
1384    var = nlrow->linvars[pos];
1385 
1386    if( SCIPvarIsActive(var) )
1387       return SCIP_OKAY;
1388 
1389    oldconstant = nlrow->constant;
1390 
1391    /* replace fixed, aggregated, or negated variable */
1392    SCIP_CALL( SCIPvarGetProbvarSum( &nlrow->linvars[pos], set, &nlrow->lincoefs[pos], &nlrow->constant) );
1393 
1394    /* if var had been fixed, entry should be removed from row */
1395    if( nlrow->lincoefs[pos] == 0.0 )
1396    {
1397       nlrowMoveLinearCoef(nlrow, nlrow->nlinvars-1, pos);
1398       nlrow->nlinvars--;
1399 
1400       if( pos < nlrow->nlinvars )
1401       {
1402          SCIP_CALL( nlrowRemoveFixedLinearCoefPos(nlrow, blkmem, set, stat, nlp, pos) );
1403       }
1404 
1405       return SCIP_OKAY;
1406    }
1407    nlrow->linvarssorted = FALSE;
1408 
1409    /* notify nlrow that coefficient of var is now 0.0 in row */
1410    SCIP_CALL( nlrowLinearCoefChanged(nlrow, set, stat, var, 0.0, nlp) );
1411 
1412    /* notify nlrow that constant of row has changed */
1413    if( oldconstant != nlrow->constant )  /*lint !e777*/
1414       SCIP_CALL( nlrowConstantChanged(nlrow, set, stat, nlp) );
1415 
1416    if( SCIPvarIsActive(nlrow->linvars[pos]) )
1417    {
1418       /* if var was aggregated or negated, notify nlrow about new coefficient */
1419       SCIP_CALL( nlrowLinearCoefChanged(nlrow, set, stat, nlrow->linvars[pos], nlrow->lincoefs[pos], nlp) );
1420    }
1421    else
1422    {
1423       SCIP_Real coef;
1424       int i;
1425 
1426       /* if not removed or active, the new variable should be multi-aggregated */
1427       assert(SCIPvarGetStatus(nlrow->linvars[pos]) == SCIP_VARSTATUS_MULTAGGR);
1428 
1429       var  = nlrow->linvars[pos];
1430       coef = nlrow->lincoefs[pos];
1431 
1432       /* remove the variable from the row */
1433       SCIP_CALL( nlrowDelLinearCoefPos(nlrow, set, stat, nlp, pos) );
1434 
1435       /* add multi-aggregated term to row */
1436       if( SCIPvarGetMultaggrConstant(var) != 0.0 )
1437       {
1438          nlrow->constant += coef * SCIPvarGetMultaggrConstant(var);
1439          SCIP_CALL( nlrowConstantChanged(nlrow, set, stat, nlp) );
1440       }
1441       SCIP_CALL( SCIPnlrowEnsureLinearSize(nlrow, blkmem, set, nlrow->nlinvars + SCIPvarGetMultaggrNVars(var)) );
1442       for( i = 0; i < SCIPvarGetMultaggrNVars(var); ++i )
1443       {
1444          if( SCIPsetIsZero(set, coef * SCIPvarGetMultaggrScalars(var)[i]) )
1445             continue;
1446          SCIP_CALL( nlrowAddLinearCoef(nlrow, blkmem, set, stat, nlp, SCIPvarGetMultaggrVars(var)[i], coef * SCIPvarGetMultaggrScalars(var)[i]) );
1447          assert(SCIPvarGetMultaggrVars(var)[i] == nlrow->linvars[nlrow->nlinvars-1]);
1448          if( !SCIPvarIsActive(SCIPvarGetMultaggrVars(var)[i]) )
1449          {
1450             /* if newly added variable is fixed, replace it now */
1451             SCIP_CALL( nlrowRemoveFixedLinearCoefPos(nlrow, blkmem, set, stat, nlp, nlrow->nlinvars-1) );
1452          }
1453       }
1454 
1455       /* due to nlrowDelLinearCoefPos, an inactive variable may have moved to position pos
1456        * if that is the case, call ourself recursively
1457        */
1458       if( pos < nlrow->nlinvars && !SCIPvarIsActive(nlrow->linvars[pos]) )
1459       {
1460          SCIP_CALL( nlrowRemoveFixedLinearCoefPos(nlrow, blkmem, set, stat, nlp, pos) );
1461       }
1462    }
1463 
1464    return SCIP_OKAY;
1465 }
1466 
1467 /** removes fixed variables from the linear part of a nonlinear row */
1468 static
nlrowRemoveFixedLinearCoefs(SCIP_NLROW * nlrow,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp)1469 SCIP_RETCODE nlrowRemoveFixedLinearCoefs(
1470    SCIP_NLROW*           nlrow,              /**< nonlinear row */
1471    BMS_BLKMEM*           blkmem,             /**< block memory */
1472    SCIP_SET*             set,                /**< global SCIP settings */
1473    SCIP_STAT*            stat,               /**< problem statistics data */
1474    SCIP_NLP*             nlp                 /**< current NLP data */
1475    )
1476 {
1477    int i;
1478    int oldlen;
1479 
1480    assert(nlrow != NULL);
1481    assert(nlrow->linvars != NULL || nlrow->nlinvars == 0);
1482 
1483    oldlen = nlrow->nlinvars;
1484    for( i = 0; i < MIN(oldlen, nlrow->nlinvars); ++i )
1485    {
1486       assert(nlrow->linvars[i] != NULL);
1487       SCIP_CALL( nlrowRemoveFixedLinearCoefPos(nlrow, blkmem, set, stat, nlp, i) );
1488    }
1489 
1490    return SCIP_OKAY;
1491 }
1492 
1493 /** removes fixed quadratic variables of a nonlinear row by replacing them with the corresponding constant or disaggregated terms */
1494 static
nlrowRemoveFixedQuadVars(SCIP_NLROW * nlrow,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp)1495 SCIP_RETCODE nlrowRemoveFixedQuadVars(
1496    SCIP_NLROW*           nlrow,              /**< nonlinear row */
1497    BMS_BLKMEM*           blkmem,             /**< block memory */
1498    SCIP_SET*             set,                /**< global SCIP settings */
1499    SCIP_STAT*            stat,               /**< problem statistics data */
1500    SCIP_NLP*             nlp                 /**< current NLP data */
1501    )
1502 {
1503    int i;
1504    int nvarsold;
1505    SCIP_Bool* used;
1506    SCIP_QUADELEM elem;
1507    SCIP_QUADELEM newelem;
1508    int idx2;
1509    SCIP_Bool havechange;
1510 
1511    SCIP_VAR* var1;
1512    SCIP_Real coef1;
1513    SCIP_Real constant1;
1514    SCIP_VAR* var2;
1515    SCIP_Real coef2;
1516    SCIP_Real constant2;
1517 
1518    assert(nlrow  != NULL);
1519    assert(blkmem != NULL);
1520 
1521    if( nlrow->nquadvars == 0 )
1522       return SCIP_OKAY;
1523 
1524    SCIPsetDebugMsg(set, "removing fixed quadratic variables from nlrow\n");
1525 
1526    nvarsold = nlrow->nquadvars;
1527    havechange = FALSE;
1528 
1529    /* allocate array to count number of uses for each variable */
1530    SCIP_CALL( SCIPsetAllocBufferArray(set, &used, nlrow->nquadvars) );
1531    BMSclearMemoryArray(used, nlrow->nquadvars);
1532 
1533    i = 0;
1534    while( i < nlrow->nquadelems )
1535    {
1536       elem = nlrow->quadelems[i];
1537 
1538       assert(elem.idx1 < nlrow->nquadvars);
1539       assert(elem.idx2 < nlrow->nquadvars);
1540       if( SCIPvarIsActive(nlrow->quadvars[elem.idx1]) && SCIPvarIsActive(nlrow->quadvars[elem.idx2]) )
1541       {
1542          /* both variables of quadratic element are active
1543           * thus, we just remember that we saw them and can continue with the next element
1544           */
1545          if( elem.idx1 < nvarsold )
1546             used[elem.idx1] = TRUE;
1547          if( elem.idx2 < nvarsold )
1548             used[elem.idx2] = TRUE;
1549          ++i;
1550          continue;
1551       }
1552 
1553       SCIPsetDebugMsg(set, "removing fixed quadratic variables from %dth element %g <%s> <%s>\n",
1554          i, elem.coef, SCIPvarGetName(nlrow->quadvars[elem.idx1]), SCIPvarGetName(nlrow->quadvars[elem.idx2]));
1555 
1556       /* if one of the variable is not active, we remove the element and insert new disaggregated ones */
1557       SCIP_CALL( nlrowDelQuadElemPos(nlrow, set, stat, nlp, i) );
1558       havechange = TRUE;
1559 
1560       var1 = nlrow->quadvars[elem.idx1];
1561       var2 = nlrow->quadvars[elem.idx2];
1562       coef1 = 1.0;
1563       coef2 = 1.0;
1564       constant1 = 0.0;
1565       constant2 = 0.0;
1566 
1567       SCIP_CALL( SCIPvarGetProbvarSum(&var1, set, &coef1, &constant1) );
1568       SCIP_CALL( SCIPvarGetProbvarSum(&var2, set, &coef2, &constant2) );
1569 
1570       if( coef1 == 0.0 && coef2 == 0.0 )
1571       {
1572          /* both variables were fixed, so we may add a constant term and continue */
1573          if( constant1 != 0.0 && constant2 != 0.0 )
1574          {
1575             nlrow->constant += elem.coef * constant1 * constant2;
1576             SCIP_CALL( nlrowConstantChanged(nlrow, set, stat, nlp) );
1577          }
1578          continue;
1579       }
1580 
1581       if( coef1 == 0.0 )
1582       {
1583          /* only the first variable was fixed, so we may add a linear term
1584           * elem.coef * x * y -> elem.coef * constant1 * (coef2 * var2 + constant2) */
1585          if( constant1 != 0.0 )
1586          {
1587             SCIP_CALL( nlrowAddToLinearCoef(nlrow, blkmem, set, stat, nlp, var2, elem.coef * constant1 * coef2, TRUE) );
1588             if( constant2 != 0.0 )
1589             {
1590                nlrow->constant += elem.coef * constant1 * constant2;
1591                SCIP_CALL( nlrowConstantChanged(nlrow, set, stat, nlp) );
1592             }
1593          }
1594          /* continue with next element that is at position i now */
1595          continue;
1596       }
1597 
1598       if( coef2 == 0.0 )
1599       {
1600          /* only the second variable was fixed, so we may add a linear term
1601           * elem.coef * x * y -> elem.coef * (coef1 * var1 + constant1) * constant2 */
1602          if( constant2 != 0.0 )
1603          {
1604             SCIP_CALL( nlrowAddToLinearCoef(nlrow, blkmem, set, stat, nlp, var1, elem.coef * coef1 * constant2, TRUE) );
1605             if( constant1 != 0.0 )
1606             {
1607                nlrow->constant += elem.coef * constant1 * constant2;
1608                SCIP_CALL( nlrowConstantChanged(nlrow, set, stat, nlp) );
1609             }
1610          }
1611          /* continue with next element that is at position i now */
1612          continue;
1613       }
1614 
1615       if( var1 == var2 && !SCIPvarIsActive(var1) )
1616       {
1617          SCIP_Real tmp;
1618          int* multaggrvaridxs;
1619          int j, k;
1620 
1621          assert(SCIPvarGetStatus(var1) == SCIP_VARSTATUS_MULTAGGR);
1622          assert(coef1 == coef2);  /*lint !e777*/
1623          assert(constant1 == constant2);  /*lint !e777*/
1624          /* square term which variable is multi-aggregated
1625           * elem.coef * x^2 -> elem.coef * (coef1 * (multaggrconstant + sum_i multaggrscalar_i*multaggrvar_i) + constant1)^2
1626           *    = elem.coef * ( (coef1 * multaggrconstant + constant1)^2 +
1627           *                    2 * (coef1 * multaggrconstant + constant1) * coef1 * (sum_j multaggrscalar_j*multaggrvar_j) +
1628           *                    coef1^2 * (sum_{j,k} multaggrscalar_j*multaggrscalar_k*multaggrvar_j*multaggrvar_k)
1629           *                  )
1630           */
1631 
1632          /* add constant part */
1633          tmp = coef1 * SCIPvarGetMultaggrConstant(var1) + constant1;
1634          if( tmp != 0.0 )
1635          {
1636             nlrow->constant += elem.coef * tmp * tmp;
1637             SCIP_CALL( nlrowConstantChanged(nlrow, set, stat, nlp) );
1638          }
1639 
1640          /* add linear part */
1641          if( constant1 != 0.0 || SCIPvarGetMultaggrConstant(var1) != 0.0 )
1642          {
1643             for( j = 0; j < SCIPvarGetMultaggrNVars(var1); ++j )
1644             {
1645                SCIP_CALL( nlrowAddToLinearCoef(nlrow, blkmem, set, stat, nlp, SCIPvarGetMultaggrVars(var1)[j],
1646                      2.0 * elem.coef * (coef1 * SCIPvarGetMultaggrConstant(var1) + constant1) * coef1 * SCIPvarGetMultaggrScalars(var1)[j], TRUE) );
1647             }
1648          }
1649 
1650          /* setup array with indices of multi-aggregated variables in quadvars */
1651          SCIP_CALL( SCIPsetAllocBufferArray(set, &multaggrvaridxs, SCIPvarGetMultaggrNVars(var1)) );
1652          for( j = 0; j < SCIPvarGetMultaggrNVars(var1); ++j )
1653          {
1654             multaggrvaridxs[j] = SCIPnlrowSearchQuadVar(nlrow, SCIPvarGetMultaggrVars(var1)[j]);
1655             if( multaggrvaridxs[j] == -1 )
1656             {
1657                /* variable multaggrvar_j not existing in quadvars array yet, so add it */
1658                SCIP_CALL( SCIPnlrowAddQuadVar(nlrow, blkmem, set, SCIPvarGetMultaggrVars(var1)[j]) );
1659                multaggrvaridxs[j] = nlrow->nquadvars-1;
1660             }
1661             assert(nlrow->quadvars[multaggrvaridxs[j]] == SCIPvarGetMultaggrVars(var1)[j]);
1662          }
1663 
1664          /* add quadratic elements elem.coef * coef1^2 * (sum_{j,k} multaggrscalar_j*multaggrscalar_k*multaggrvar_j*multaggrvar_k) */
1665          for( j = 0; j < SCIPvarGetMultaggrNVars(var1); ++j )
1666          {
1667             /* bilinear terms */
1668             for( k = 0; k < j; ++k )
1669             {
1670                newelem.idx1 = MIN(multaggrvaridxs[j], multaggrvaridxs[k]);
1671                newelem.idx2 = MAX(multaggrvaridxs[j], multaggrvaridxs[k]);
1672                newelem.coef = 2 * elem.coef * coef1 * coef1 * SCIPvarGetMultaggrScalars(var1)[j] * SCIPvarGetMultaggrScalars(var1)[k];
1673                SCIP_CALL( SCIPnlrowAddQuadElement(nlrow, blkmem, set, stat, nlp, newelem) );
1674             }
1675 
1676             /* square term */
1677             newelem.idx1 = multaggrvaridxs[j];
1678             newelem.idx2 = multaggrvaridxs[j];
1679             newelem.coef = elem.coef * coef1 * coef1 * SCIPvarGetMultaggrScalars(var1)[j] * SCIPvarGetMultaggrScalars(var1)[j];
1680             SCIP_CALL( SCIPnlrowAddQuadElement(nlrow, blkmem, set, stat, nlp, newelem) );
1681          }
1682 
1683          SCIPsetFreeBufferArray(set, &multaggrvaridxs);
1684 
1685          /* continue with next element that is at position i now */
1686          continue;
1687       }
1688 
1689       assert(var1 != NULL);
1690       assert(var2 != NULL);
1691       if( SCIPvarIsActive(var1) && !SCIPvarIsActive(var2) )
1692       {
1693          /* if the second variable is multi-aggregated, but the first one is not, swap both terms */
1694          SCIP_VAR* tmpvar;
1695          SCIP_Real tmpcoef;
1696          SCIP_Real tmpconstant;
1697 
1698          tmpvar      = var1;
1699          tmpcoef     = coef1;
1700          tmpconstant = constant1;
1701          var2      = var1;
1702          coef2     = coef1;
1703          constant2 = constant1;
1704          var1      = tmpvar;
1705          coef1     = tmpcoef;
1706          constant1 = tmpconstant;
1707       }
1708 
1709       if( !SCIPvarIsActive(var1) )
1710       {
1711          SCIP_Real tmp;
1712          int j;
1713 
1714          assert(SCIPvarGetStatus(var1) == SCIP_VARSTATUS_MULTAGGR);
1715 
1716          /* the first variable is multi-aggregated, add a constant and sequences of linear and quadratic terms:
1717           * elem.coef * x * y -> elem.coef * (coef1 * (multaggrconstant + sum_i multaggrscalar_i*multaggrvar_i) + constant1) * (coef2 * var2 + constant2)
1718           *    = elem.coef * ( (coef1 * multaggrconstant + constant1) * constant2 +
1719           *                    (coef1 * multaggrconstant + constant1) * coef2 * var2 +
1720           *                    (coef1 * (sum_j multaggrscalar_j*multaggrvar_j)) * constant2 +
1721           *                    (coef1 * (sum_j multaggrscalar_j*multaggrvar_j)) * coef2 * var2
1722           *                  )
1723           */
1724 
1725          /* add constant part */
1726          tmp = elem.coef * (coef1 * SCIPvarGetMultaggrConstant(var1) + constant1) * constant2;
1727          if( tmp != 0.0 )
1728          {
1729             nlrow->constant += tmp;
1730             SCIP_CALL( nlrowConstantChanged(nlrow, set, stat, nlp) );
1731          }
1732 
1733          /* add linear part */
1734          SCIP_CALL( nlrowAddToLinearCoef(nlrow, blkmem, set, stat, nlp, var2, elem.coef * (coef1 * SCIPvarGetMultaggrConstant(var1) + constant1) * coef2, TRUE) );
1735          if( constant2 != 0.0 )
1736          {
1737             for( j = 0; j < SCIPvarGetMultaggrNVars(var1); ++j )
1738             {
1739                SCIP_CALL( nlrowAddToLinearCoef(nlrow, blkmem, set, stat, nlp, SCIPvarGetMultaggrVars(var1)[j], elem.coef * coef1 * SCIPvarGetMultaggrScalars(var1)[j] * constant2, TRUE) );
1740             }
1741          }
1742 
1743          /* get index of var2 in quadvars array */
1744          idx2 = SCIPnlrowSearchQuadVar(nlrow, var2);
1745          if( idx2 == -1 )
1746          {
1747             /* variable var2 not existing in quadvars array yet, so add it */
1748             SCIP_CALL( SCIPnlrowAddQuadVar(nlrow, blkmem, set, var2) );
1749             idx2 = nlrow->nquadvars-1;
1750             assert(nlrow->quadvars[idx2] == var2);
1751          }
1752 
1753          /* add quadratic elements elem.coef * coef1 * (sum_j multaggrscalar_j*multaggrvar_j) * coef2 * var2 */
1754          for( j = 0; j < SCIPvarGetMultaggrNVars(var1); ++j )
1755          {
1756             newelem.idx1 = SCIPnlrowSearchQuadVar(nlrow, SCIPvarGetMultaggrVars(var1)[j]);
1757             if( newelem.idx1 == -1 )
1758             {
1759                /* variable not existing in quadvars array yet, so add it */
1760                SCIP_CALL( SCIPnlrowAddQuadVar(nlrow, blkmem, set, SCIPvarGetMultaggrVars(var1)[j]) );
1761                newelem.idx1 = nlrow->nquadvars-1;
1762                assert(nlrow->quadvars[newelem.idx1] == SCIPvarGetMultaggrVars(var1)[j]);
1763             }
1764 
1765             newelem.idx2 = idx2;
1766 
1767             /* swap indices if newelem.idx1 <= newelem.idx2 */
1768             if( newelem.idx1 > idx2 )
1769             {
1770                newelem.idx2 = newelem.idx1;
1771                newelem.idx1 = idx2;
1772             }
1773 
1774             newelem.coef = elem.coef * coef1 * coef2 * SCIPvarGetMultaggrScalars(var1)[j];
1775 
1776             SCIP_CALL( SCIPnlrowAddQuadElement(nlrow, blkmem, set, stat, nlp, newelem) );
1777 
1778             /* continue with next element that is at position i now */
1779             continue;
1780          }
1781       }
1782 
1783       assert(SCIPvarIsActive(var1));
1784       assert(SCIPvarIsActive(var2));
1785       /* add elem.coef * (coef1 * var1 + constant1) * (coef2 * var2 + constant2) */
1786       /* add constant part */
1787       if( constant1 != 0.0 && constant2 != 0.0 )
1788       {
1789          nlrow->constant += elem.coef * constant1 * constant2;
1790          SCIP_CALL( nlrowConstantChanged(nlrow, set, stat, nlp) );
1791       }
1792       /* add linear coefficients */
1793       SCIP_CALL( nlrowAddToLinearCoef(nlrow, blkmem, set, stat, nlp, var1, elem.coef * coef1 * constant2, TRUE) );
1794       SCIP_CALL( nlrowAddToLinearCoef(nlrow, blkmem, set, stat, nlp, var2, elem.coef * coef2 * constant1, TRUE) );
1795       /* get index of var1 in quadvars array */
1796       newelem.idx1 = SCIPnlrowSearchQuadVar(nlrow, var1);
1797       if( newelem.idx1 == -1 )
1798       {
1799          /* variable var2 not existing in quadvars array yet, so add it */
1800          SCIP_CALL( SCIPnlrowAddQuadVar(nlrow, blkmem, set, var1) );
1801          newelem.idx1 = nlrow->nquadvars-1;
1802          assert(nlrow->quadvars[newelem.idx1] == var1);
1803       }
1804       /* get index of var2 in quadvars array */
1805       newelem.idx2 = SCIPnlrowSearchQuadVar(nlrow, var2);
1806       if( newelem.idx2 == -1 )
1807       {
1808          /* variable var2 not existing in quadvars array yet, so add it */
1809          SCIP_CALL( SCIPnlrowAddQuadVar(nlrow, blkmem, set, var2) );
1810          newelem.idx2 = nlrow->nquadvars-1;
1811          assert(nlrow->quadvars[newelem.idx2] == var2);
1812       }
1813       /* make sure idx1 <= idx2 */
1814       if( newelem.idx1 > newelem.idx2 )
1815       {
1816          idx2 = newelem.idx2;
1817          newelem.idx2 = newelem.idx1;
1818          newelem.idx1 = idx2;
1819       }
1820       newelem.coef = elem.coef * coef1 * coef2;
1821       /* add new quadratic element */
1822       SCIP_CALL( SCIPnlrowAddQuadElement(nlrow, blkmem, set, stat, nlp, newelem) );
1823 
1824       /* continue with next element that is at position i now */
1825    }
1826 
1827    /* clean up unused variables */
1828    if( nlrow->nquadelems == 0 )
1829    {
1830       /* the complete quadratic part was fixed or linearized, so we just free up all memory */
1831       BMSfreeBlockMemoryArray(blkmem, &nlrow->quadvars, nlrow->quadvarssize);
1832       if( nlrow->quadvarshash != NULL )
1833          SCIPhashmapFree(&nlrow->quadvarshash);
1834       BMSfreeBlockMemoryArray(blkmem, &nlrow->quadelems, nlrow->quadelemssize);
1835       nlrow->nquadvars = 0;
1836       nlrow->quadvarssize = 0;
1837       nlrow->nquadelems = 0;
1838       nlrow->quadelemssize = 0;
1839       nlrow->quadelemssorted = TRUE;
1840    }
1841    else if( havechange )
1842    {
1843       /* something had changed, so we likely have quadratic variables to remove */
1844       int* newpos;
1845       int offset;
1846 
1847       /* compute new positions of variables in quadvars array */
1848       SCIP_CALL( SCIPsetAllocBufferArray(set, &newpos, nlrow->nquadvars) );
1849 
1850       offset = 0;
1851       for( i = 0; i < nvarsold; ++i )
1852       {
1853          /* previously existing variables should either be active or not used anymore */
1854          assert(!used[i] || SCIPvarIsActive(nlrow->quadvars[i]));
1855 
1856          if( !used[i] )
1857          {
1858             /* variable has been removed */
1859             newpos[i] = -1;
1860             ++offset;
1861          }
1862          else
1863          {
1864             /* variable will move to position i-offset */
1865             newpos[i] = i-offset;
1866          }
1867       }
1868       for( ; i < nlrow->nquadvars; ++i )
1869       {
1870          if( !SCIPvarIsActive(nlrow->quadvars[i]) )
1871          {
1872             /* it can have happened that a new quadratic variable was added that is not active (when multiplying two multi-aggregations)
1873              * in this case, the variable was only temporarily used and should not be used anymore (this is asserted in the next for-loop below),
1874              * thus we can remove it
1875              */
1876             newpos[i] = -1;
1877             ++offset;
1878          }
1879          else
1880          {
1881             /* variable will move to position i-offset */
1882             newpos[i] = i-offset;
1883          }
1884       }
1885 
1886       /* adjust variable indices in quadratic elements */
1887       for( i = 0; i < nlrow->nquadelems; ++i )
1888       {
1889          assert(newpos[nlrow->quadelems[i].idx1] >= 0);
1890          assert(newpos[nlrow->quadelems[i].idx2] >= 0);
1891          nlrow->quadelems[i].idx1 = newpos[nlrow->quadelems[i].idx1];
1892          nlrow->quadelems[i].idx2 = newpos[nlrow->quadelems[i].idx2];
1893          assert(nlrow->quadelems[i].idx1 <= nlrow->quadelems[i].idx2); /* the way we shrink the quadvars array, variables should stay in the same relative position to each other */
1894       }
1895 
1896       /* move variables in quadvars array and update quadvarshash */
1897       for( i = 0; i < nlrow->nquadvars; ++i )
1898       {
1899          if( newpos[i] == -1 )
1900          {
1901             if( nlrow->quadvarshash != NULL )
1902             {
1903                SCIP_CALL( SCIPhashmapRemove(nlrow->quadvarshash, (void*)nlrow->quadvars[i]) );
1904             }
1905          }
1906          else
1907          {
1908             nlrow->quadvars[newpos[i]] = nlrow->quadvars[i];
1909             if( nlrow->quadvarshash != NULL )
1910             {
1911                SCIP_CALL( SCIPhashmapSetImageInt(nlrow->quadvarshash, (void*)nlrow->quadvars[i], newpos[i]) );
1912             }
1913          }
1914       }
1915       nlrow->nquadvars -= offset;
1916 
1917       SCIPsetFreeBufferArray(set, &newpos);
1918    }
1919 
1920    SCIPsetFreeBufferArray(set, &used);
1921 
1922    SCIPsetDebugMsg(set, "finished removing fixed quadratic variables\n");
1923 
1924    return SCIP_OKAY;
1925 }
1926 
1927 /** removes fixed variables from expression tree of a nonlinear row */
1928 static
nlrowRemoveFixedExprtreeVars(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp)1929 SCIP_RETCODE nlrowRemoveFixedExprtreeVars(
1930    SCIP_NLROW*           nlrow,              /**< nonlinear row */
1931    SCIP_SET*             set,                /**< global SCIP settings */
1932    SCIP_STAT*            stat,               /**< problem statistics data */
1933    SCIP_NLP*             nlp                 /**< current NLP data */
1934    )
1935 {
1936    SCIP_Bool changed;
1937 
1938    if( nlrow->exprtree == NULL )
1939       return SCIP_OKAY;
1940 
1941    SCIP_CALL( SCIPexprtreeRemoveFixedVars(nlrow->exprtree, set, &changed, NULL, NULL) );
1942    if( changed )
1943    {
1944       SCIP_CALL( nlrowExprtreeChanged(nlrow, set, stat, nlp) );
1945    }
1946 
1947    if( SCIPexprtreeGetNVars(nlrow->exprtree) == 0 && SCIPexprtreeGetNParams(nlrow->exprtree) == 0 )
1948    {
1949       /* if expression tree is constant and not parameterized now, remove it */
1950       SCIP_Real exprval;
1951       SCIP_CALL( SCIPexprtreeEval(nlrow->exprtree, NULL, &exprval) );
1952       SCIP_CALL( SCIPnlrowChgConstant(nlrow, set, stat, nlp, nlrow->constant + exprval) );
1953 
1954       SCIP_CALL( SCIPexprtreeFree(&nlrow->exprtree) );
1955    }
1956 
1957    return SCIP_OKAY;
1958 }
1959 
1960 /** removes fixed variable from nonlinear row */
1961 static
nlrowRemoveFixedVar(SCIP_NLROW * nlrow,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp,SCIP_VAR * var)1962 SCIP_RETCODE nlrowRemoveFixedVar(
1963    SCIP_NLROW*           nlrow,              /**< nonlinear row */
1964    BMS_BLKMEM*           blkmem,             /**< block memory */
1965    SCIP_SET*             set,                /**< global SCIP settings */
1966    SCIP_STAT*            stat,               /**< problem statistics data */
1967    SCIP_NLP*             nlp,                /**< current NLP data */
1968    SCIP_VAR*             var                 /**< variable that had been fixed */
1969    )
1970 {
1971    int pos;
1972 
1973    assert(nlrow != NULL);
1974    assert(var   != NULL);
1975    assert(!SCIPvarIsActive(var));
1976 
1977    /* search for variable in linear part and remove if existing */
1978    pos = nlrowSearchLinearCoef(nlrow, var);
1979    if( pos >= 0 )
1980    {
1981       SCIP_CALL( nlrowRemoveFixedLinearCoefPos(nlrow, blkmem, set, stat, nlp, pos) );
1982    }
1983 
1984    /* search for variable in quadratic part and remove all fixed quadratic variables if existing */
1985    pos = SCIPnlrowSearchQuadVar(nlrow, var);
1986    if( pos >= 0 )
1987    {
1988       SCIP_CALL( nlrowRemoveFixedQuadVars(nlrow, blkmem, set, stat, nlp) );
1989    }
1990 
1991    /* search for variable in non-quadratic part and remove all fixed variables in expression tree if existing */
1992    if( nlrow->exprtree != NULL && SCIPexprtreeFindVar(nlrow->exprtree, var) >= 0 )
1993    {
1994       SCIP_CALL( nlrowRemoveFixedExprtreeVars(nlrow, set, stat, nlp) );
1995    }
1996 
1997    return SCIP_OKAY;
1998 }
1999 
2000 /*
2001  * public NLP nonlinear row methods
2002  */
2003 
2004 /** create a new nonlinear row
2005  * the new row is already captured
2006  */
SCIPnlrowCreate(SCIP_NLROW ** nlrow,BMS_BLKMEM * blkmem,SCIP_SET * set,const char * name,SCIP_Real constant,int nlinvars,SCIP_VAR ** linvars,SCIP_Real * lincoefs,int nquadvars,SCIP_VAR ** quadvars,int nquadelems,SCIP_QUADELEM * quadelems,SCIP_EXPRTREE * exprtree,SCIP_Real lhs,SCIP_Real rhs,SCIP_EXPRCURV curvature)2007 SCIP_RETCODE SCIPnlrowCreate(
2008    SCIP_NLROW**          nlrow,              /**< buffer to store pointer to nonlinear row */
2009    BMS_BLKMEM*           blkmem,             /**< block memory */
2010    SCIP_SET*             set,                /**< global SCIP settings */
2011    const char*           name,               /**< name of nonlinear row */
2012    SCIP_Real             constant,           /**< constant */
2013    int                   nlinvars,           /**< number of linear variables */
2014    SCIP_VAR**            linvars,            /**< linear variables, or NULL if nlinvars == 0 */
2015    SCIP_Real*            lincoefs,           /**< linear coefficients, or NULL if nlinvars == 0 */
2016    int                   nquadvars,          /**< number of variables in quadratic terms */
2017    SCIP_VAR**            quadvars,           /**< variables in quadratic terms, or NULL if nquadvars == 0 */
2018    int                   nquadelems,         /**< number of entries in quadratic term matrix */
2019    SCIP_QUADELEM*        quadelems,          /**< elements of quadratic term matrix, or NULL if nquadelems == 0 */
2020    SCIP_EXPRTREE*        exprtree,           /**< expression tree, or NULL */
2021    SCIP_Real             lhs,                /**< left hand side */
2022    SCIP_Real             rhs,                /**< right hand side */
2023    SCIP_EXPRCURV         curvature           /**< curvature of the nonlinear row */
2024    )
2025 {
2026 #ifndef NDEBUG
2027    int i;
2028 #endif
2029 
2030    assert(nlrow  != NULL);
2031    assert(blkmem != NULL);
2032    assert(set    != NULL);
2033    assert(name   != NULL);
2034    assert(!SCIPsetIsInfinity(set, ABS(constant)));
2035    assert(nlinvars   == 0 || linvars   != NULL);
2036    assert(nlinvars   == 0 || lincoefs  != NULL);
2037    assert(nquadvars  == 0 || quadvars  != NULL);
2038    assert(nquadelems == 0 || quadelems != NULL);
2039    assert(nquadelems == 0 || nquadvars > 0);
2040    assert(SCIPsetIsRelLE(set, lhs, rhs));
2041 
2042    SCIP_ALLOC( BMSallocBlockMemory(blkmem, nlrow) );
2043 
2044    /* constant part */
2045    assert(!SCIPsetIsInfinity(set, REALABS(constant)));
2046    (*nlrow)->constant = constant;
2047 
2048 #ifndef NDEBUG
2049    for( i = 0; i < nlinvars; ++i )
2050    {
2051       assert(linvars[i] != NULL);
2052       assert(!SCIPsetIsInfinity(set, REALABS(lincoefs[i])));
2053    }
2054 #endif
2055 
2056    /* linear part */
2057    (*nlrow)->nlinvars = nlinvars;
2058    (*nlrow)->linvarssize = nlinvars;
2059    if( nlinvars > 0 )
2060    {
2061       SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &(*nlrow)->linvars,  linvars,  nlinvars) );
2062       SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &(*nlrow)->lincoefs, lincoefs, nlinvars) );
2063       (*nlrow)->linvarssorted = FALSE;
2064    }
2065    else
2066    {
2067       (*nlrow)->linvars  = NULL;
2068       (*nlrow)->lincoefs = NULL;
2069       (*nlrow)->linvarssorted = TRUE;
2070    }
2071 
2072    /* quadratic variables */
2073 #ifndef NDEBUG
2074    for( i = 0; i < nquadvars; ++i )
2075       assert(quadvars[i] != NULL);
2076 #endif
2077 
2078    (*nlrow)->nquadvars    = nquadvars;
2079    (*nlrow)->quadvarssize = nquadvars;
2080    (*nlrow)->quadvarshash = NULL;
2081    if( nquadvars > 0 )
2082    {
2083       SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &(*nlrow)->quadvars, quadvars, nquadvars) );
2084       SCIP_CALL( nlrowSetupQuadVarsHash(*nlrow, blkmem) );
2085    }
2086    else
2087    {
2088       (*nlrow)->quadvars = NULL;
2089    }
2090 
2091    /* quadratic elements */
2092 #ifndef NDEBUG
2093    for( i = 0; i < nquadelems; ++i )
2094    {
2095       assert(quadelems[i].idx1 >= 0);
2096       assert(quadelems[i].idx1 <  nquadvars);
2097       assert(quadelems[i].idx2 >= 0);
2098       assert(quadelems[i].idx2 <  nquadvars);
2099       assert(quadelems[i].idx1 <= quadelems[i].idx2);
2100       assert(!SCIPsetIsInfinity(set, REALABS(quadelems[i].coef)));
2101    }
2102 #endif
2103 
2104    (*nlrow)->nquadelems = nquadelems;
2105    (*nlrow)->quadelemssize = nquadelems;
2106    if( nquadelems > 0 )
2107    {
2108       assert(nquadvars > 0);
2109       SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &(*nlrow)->quadelems, quadelems, nquadelems) );
2110       (*nlrow)->quadelemssorted = FALSE;
2111    }
2112    else
2113    {
2114       (*nlrow)->quadelems       = NULL;
2115       (*nlrow)->quadelemssorted = TRUE;
2116    }
2117 
2118    /* non-quadratic part */
2119    if( exprtree != NULL )
2120    {
2121       SCIP_CALL( SCIPexprtreeCopy( blkmem, &(*nlrow)->exprtree, exprtree) );
2122    }
2123    else
2124    {
2125       (*nlrow)->exprtree = NULL;
2126    }
2127 
2128    /* left and right hand sides, asserted above that lhs is relatively less equal than rhs */
2129    (*nlrow)->lhs = MIN(lhs, rhs);
2130    (*nlrow)->rhs = MAX(lhs, rhs);
2131 
2132    /* miscellaneous */
2133    SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &(*nlrow)->name, name, strlen(name)+1) );
2134    (*nlrow)->activity = SCIP_INVALID;
2135    (*nlrow)->validactivitynlp = FALSE;
2136    (*nlrow)->pseudoactivity = SCIP_INVALID;
2137    (*nlrow)->validpsactivitydomchg = FALSE;
2138    (*nlrow)->minactivity = SCIP_INVALID;
2139    (*nlrow)->maxactivity = SCIP_INVALID;
2140    (*nlrow)->validactivitybdsdomchg = FALSE;
2141    (*nlrow)->nlpindex = -1;
2142    (*nlrow)->nlpiindex = -1;
2143    (*nlrow)->nuses = 0;
2144    (*nlrow)->dualsol = 0.0;
2145    (*nlrow)->curvature = curvature;
2146 
2147    /* capture the nonlinear row */
2148    SCIPnlrowCapture(*nlrow);
2149 
2150    return SCIP_OKAY;
2151 }
2152 
2153 /** create a nonlinear row that is a copy of a given row
2154  * the new row is already captured
2155  */
SCIPnlrowCreateCopy(SCIP_NLROW ** nlrow,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_NLROW * sourcenlrow)2156 SCIP_RETCODE SCIPnlrowCreateCopy(
2157    SCIP_NLROW**          nlrow,              /**< buffer to store pointer to nonlinear row */
2158    BMS_BLKMEM*           blkmem,             /**< block memory */
2159    SCIP_SET*             set,                /**< global SCIP settings */
2160    SCIP_NLROW*           sourcenlrow         /**< nonlinear row to copy */
2161    )
2162 {
2163    assert(nlrow  != NULL);
2164    assert(blkmem != NULL);
2165    assert(set    != NULL);
2166    assert(sourcenlrow != NULL);
2167 
2168    SCIP_CALL( SCIPnlrowCreate(nlrow, blkmem, set, sourcenlrow->name,
2169          sourcenlrow->constant,
2170          sourcenlrow->nlinvars, sourcenlrow->linvars, sourcenlrow->lincoefs,
2171          sourcenlrow->nquadvars, sourcenlrow->quadvars, sourcenlrow->nquadelems, sourcenlrow->quadelems,
2172          sourcenlrow->exprtree,
2173          sourcenlrow->lhs, sourcenlrow->rhs, sourcenlrow->curvature) );
2174 
2175    (*nlrow)->linvarssorted          = sourcenlrow->linvarssorted;
2176    (*nlrow)->quadelemssorted        = sourcenlrow->quadelemssorted;
2177    (*nlrow)->activity               = sourcenlrow->activity;
2178    (*nlrow)->validactivitynlp       = sourcenlrow->validactivitynlp;
2179    (*nlrow)->pseudoactivity         = sourcenlrow->pseudoactivity;
2180    (*nlrow)->validpsactivitydomchg  = sourcenlrow->validpsactivitydomchg;
2181    (*nlrow)->minactivity            = sourcenlrow->minactivity;
2182    (*nlrow)->maxactivity            = sourcenlrow->maxactivity;
2183    (*nlrow)->validactivitybdsdomchg = sourcenlrow->validactivitybdsdomchg;
2184 
2185    return SCIP_OKAY;
2186 }
2187 
2188 /** create a new nonlinear row from a linear row
2189  * the new row is already captured
2190  */
SCIPnlrowCreateFromRow(SCIP_NLROW ** nlrow,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_ROW * row)2191 SCIP_RETCODE SCIPnlrowCreateFromRow(
2192    SCIP_NLROW**          nlrow,              /**< buffer to store pointer to nonlinear row */
2193    BMS_BLKMEM*           blkmem,             /**< block memory */
2194    SCIP_SET*             set,                /**< global SCIP settings */
2195    SCIP_ROW*             row                 /**< the linear row to copy */
2196    )
2197 {
2198    int rownz;
2199 
2200    assert(nlrow  != NULL);
2201    assert(blkmem != NULL);
2202    assert(set    != NULL);
2203    assert(row    != NULL);
2204 
2205    rownz = SCIProwGetNNonz(row);
2206 
2207    if( rownz > 1 )
2208    {
2209       SCIP_VAR** rowvars;
2210       int i;
2211 
2212       SCIP_CALL( SCIPsetAllocBufferArray(set, &rowvars, rownz) );
2213 
2214       for( i = 0; i < rownz; ++i )
2215       {
2216          rowvars[i] = SCIPcolGetVar(SCIProwGetCols(row)[i]);
2217          assert(rowvars[i] != NULL);
2218       }
2219 
2220       SCIP_CALL( SCIPnlrowCreate(nlrow, blkmem, set, SCIProwGetName(row),
2221             SCIProwGetConstant(row),
2222             rownz, rowvars, SCIProwGetVals(row),
2223             0, NULL, 0, NULL,
2224             NULL,
2225             SCIProwGetLhs(row), SCIProwGetRhs(row),
2226             SCIP_EXPRCURV_LINEAR) );
2227 
2228       SCIPsetFreeBufferArray(set, &rowvars);
2229    }
2230    else if( rownz == 1 )
2231    {
2232       SCIP_VAR* rowvar;
2233 
2234       rowvar = SCIPcolGetVar(SCIProwGetCols(row)[0]);
2235 
2236       SCIP_CALL( SCIPnlrowCreate(nlrow, blkmem, set, SCIProwGetName(row),
2237             SCIProwGetConstant(row),
2238             1, &rowvar, SCIProwGetVals(row),
2239             0, NULL, 0, NULL,
2240             NULL,
2241             SCIProwGetLhs(row), SCIProwGetRhs(row),
2242             SCIP_EXPRCURV_LINEAR) );
2243    }
2244    else
2245    {
2246       SCIP_CALL( SCIPnlrowCreate(nlrow, blkmem, set, SCIProwGetName(row),
2247             SCIProwGetConstant(row),
2248             0, NULL, NULL,
2249             0, NULL, 0, NULL,
2250             NULL,
2251             SCIProwGetLhs(row), SCIProwGetRhs(row),
2252             SCIP_EXPRCURV_LINEAR) );
2253    }
2254 
2255    return SCIP_OKAY;
2256 }
2257 
2258 /** frees a nonlinear row */
SCIPnlrowFree(SCIP_NLROW ** nlrow,BMS_BLKMEM * blkmem)2259 SCIP_RETCODE SCIPnlrowFree(
2260    SCIP_NLROW**          nlrow,              /**< pointer to NLP row */
2261    BMS_BLKMEM*           blkmem              /**< block memory */
2262    )
2263 {
2264    assert(blkmem != NULL);
2265    assert(nlrow  != NULL);
2266    assert(*nlrow != NULL);
2267    assert((*nlrow)->nuses == 0);
2268    assert((*nlrow)->nlpindex == -1);
2269    assert((*nlrow)->nlpiindex == -1);
2270 
2271    /* linear part */
2272    BMSfreeBlockMemoryArrayNull(blkmem, &(*nlrow)->linvars,   (*nlrow)->linvarssize);
2273    BMSfreeBlockMemoryArrayNull(blkmem, &(*nlrow)->lincoefs,  (*nlrow)->linvarssize);
2274 
2275    /* quadratic part */
2276    BMSfreeBlockMemoryArrayNull(blkmem, &(*nlrow)->quadvars,  (*nlrow)->quadvarssize);
2277    BMSfreeBlockMemoryArrayNull(blkmem, &(*nlrow)->quadelems, (*nlrow)->quadelemssize);
2278    if( (*nlrow)->quadvarshash != NULL )
2279       SCIPhashmapFree(&(*nlrow)->quadvarshash);
2280 
2281    /* non-quadratic part */
2282    if( (*nlrow)->exprtree != NULL )
2283    {
2284       SCIP_CALL( SCIPexprtreeFree(&(*nlrow)->exprtree) );
2285    }
2286 
2287    /* miscellaneous */
2288    BMSfreeBlockMemoryArray(blkmem, &(*nlrow)->name, strlen((*nlrow)->name)+1);
2289 
2290    BMSfreeBlockMemory(blkmem, nlrow);
2291 
2292    return SCIP_OKAY;
2293 }
2294 
2295 /** output nonlinear row to file stream */
SCIPnlrowPrint(SCIP_NLROW * nlrow,SCIP_MESSAGEHDLR * messagehdlr,FILE * file)2296 SCIP_RETCODE SCIPnlrowPrint(
2297    SCIP_NLROW*           nlrow,              /**< NLP row */
2298    SCIP_MESSAGEHDLR*     messagehdlr,        /**< message handler */
2299    FILE*                 file                /**< output file (or NULL for standard output) */
2300    )
2301 {
2302    int i;
2303 
2304    assert(nlrow != NULL);
2305 
2306    /* print row name */
2307    if( nlrow->name != NULL && nlrow->name[0] != '\0' )
2308    {
2309       SCIPmessageFPrintInfo(messagehdlr, file, "%s: ", nlrow->name);
2310    }
2311 
2312    /* print left hand side */
2313    SCIPmessageFPrintInfo(messagehdlr, file, "%.15g <= ", nlrow->lhs);
2314 
2315    /* print constant */
2316    SCIPmessageFPrintInfo(messagehdlr, file, "%.15g ", nlrow->constant);
2317 
2318    /* print linear coefficients */
2319    for( i = 0; i < nlrow->nlinvars; ++i )
2320    {
2321       assert(nlrow->linvars[i] != NULL);
2322       assert(SCIPvarGetName(nlrow->linvars[i]) != NULL);
2323       SCIPmessageFPrintInfo(messagehdlr, file, "%+.15g<%s> ", nlrow->lincoefs[i], SCIPvarGetName(nlrow->linvars[i]));
2324    }
2325 
2326    /* print quadratic elements */
2327    for( i = 0; i < nlrow->nquadelems; ++i )
2328    {
2329       assert(SCIPvarGetName(nlrow->quadvars[nlrow->quadelems[i].idx1]) != NULL);
2330       assert(SCIPvarGetName(nlrow->quadvars[nlrow->quadelems[i].idx2]) != NULL);
2331       if( nlrow->quadelems[i].idx1 == nlrow->quadelems[i].idx2 )
2332          SCIPmessageFPrintInfo(messagehdlr, file, "%+.15gsqr(<%s>) ", nlrow->quadelems[i].coef, SCIPvarGetName(nlrow->quadvars[nlrow->quadelems[i].idx1]));
2333       else
2334          SCIPmessageFPrintInfo(messagehdlr, file, "%+.15g<%s><%s> ", nlrow->quadelems[i].coef, SCIPvarGetName(nlrow->quadvars[nlrow->quadelems[i].idx1]), SCIPvarGetName(nlrow->quadvars[nlrow->quadelems[i].idx2]));
2335    }
2336 
2337    /* print non-quadratic part */
2338    if( nlrow->exprtree != NULL )
2339    {
2340       SCIPmessageFPrintInfo(messagehdlr, file, " + ");
2341       SCIP_CALL( SCIPexprtreePrintWithNames(nlrow->exprtree, messagehdlr, file) );
2342    }
2343 
2344    /* print right hand side */
2345    SCIPmessageFPrintInfo(messagehdlr, file, "<= %.15g\n", nlrow->rhs);
2346 
2347    return SCIP_OKAY;
2348 }
2349 
2350 /** increases usage counter of NLP nonlinear row */
SCIPnlrowCapture(SCIP_NLROW * nlrow)2351 void SCIPnlrowCapture(
2352    SCIP_NLROW*           nlrow               /**< nonlinear row to capture */
2353    )
2354 {
2355    assert(nlrow != NULL);
2356    assert(nlrow->nuses >= 0);
2357 
2358    SCIPdebugMessage("capture nonlinear row <%s> with nuses=%d\n", nlrow->name, nlrow->nuses);
2359    nlrow->nuses++;
2360 }
2361 
2362 /** decreases usage counter of NLP nonlinear row */
SCIPnlrowRelease(SCIP_NLROW ** nlrow,BMS_BLKMEM * blkmem,SCIP_SET * set)2363 SCIP_RETCODE SCIPnlrowRelease(
2364    SCIP_NLROW**          nlrow,              /**< nonlinear row to free */
2365    BMS_BLKMEM*           blkmem,             /**< block memory */
2366    SCIP_SET*             set                 /**< global SCIP settings */
2367    )
2368 {
2369    assert(blkmem != NULL);
2370    assert(nlrow  != NULL);
2371    assert(*nlrow != NULL);
2372    assert((*nlrow)->nuses >= 1);
2373 
2374    SCIPsetDebugMsg(set, "release nonlinear row <%s> with nuses=%d\n", (*nlrow)->name, (*nlrow)->nuses);
2375    (*nlrow)->nuses--;
2376    if( (*nlrow)->nuses == 0 )
2377    {
2378       SCIP_CALL( SCIPnlrowFree(nlrow, blkmem) );
2379    }
2380 
2381    *nlrow = NULL;
2382 
2383    return SCIP_OKAY;
2384 } /*lint !e715*/
2385 
2386 /** ensures, that linear coefficient array of nonlinear row can store at least num entries */
SCIPnlrowEnsureLinearSize(SCIP_NLROW * nlrow,BMS_BLKMEM * blkmem,SCIP_SET * set,int num)2387 SCIP_RETCODE SCIPnlrowEnsureLinearSize(
2388    SCIP_NLROW*           nlrow,              /**< NLP row */
2389    BMS_BLKMEM*           blkmem,             /**< block memory */
2390    SCIP_SET*             set,                /**< global SCIP settings */
2391    int                   num                 /**< minimum number of entries to store */
2392    )
2393 {
2394    assert(nlrow != NULL);
2395    assert(nlrow->nlinvars <= nlrow->linvarssize);
2396 
2397    if( num > nlrow->linvarssize )
2398    {
2399       int newsize;
2400 
2401       newsize = SCIPsetCalcMemGrowSize(set, num);
2402       SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &nlrow->linvars,    nlrow->linvarssize, newsize) );
2403       SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &nlrow->lincoefs,   nlrow->linvarssize, newsize) );
2404       nlrow->linvarssize = newsize;
2405    }
2406    assert(num <= nlrow->linvarssize);
2407 
2408    return SCIP_OKAY;
2409 }
2410 
2411 /** adds a previously non existing linear coefficient to an NLP nonlinear row */
SCIPnlrowAddLinearCoef(SCIP_NLROW * nlrow,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp,SCIP_VAR * var,SCIP_Real val)2412 SCIP_RETCODE SCIPnlrowAddLinearCoef(
2413    SCIP_NLROW*           nlrow,              /**< NLP nonlinear row */
2414    BMS_BLKMEM*           blkmem,             /**< block memory */
2415    SCIP_SET*             set,                /**< global SCIP settings */
2416    SCIP_STAT*            stat,               /**< problem statistics data */
2417    SCIP_NLP*             nlp,                /**< current NLP data */
2418    SCIP_VAR*             var,                /**< variable */
2419    SCIP_Real             val                 /**< value of coefficient */
2420    )
2421 {
2422    /* if row is in NLP already, make sure that only active variables are added */
2423    if( nlrow->nlpindex >= 0 )
2424    {
2425       SCIP_Real constant;
2426 
2427       /* get corresponding active or multi-aggregated variable */
2428       constant = 0.0;
2429       SCIP_CALL( SCIPvarGetProbvarSum(&var, set, &val, &constant) );
2430 
2431       /* add constant */
2432       SCIP_CALL( SCIPnlrowChgConstant(nlrow, set, stat, nlp, nlrow->constant + constant) );
2433 
2434       if( val == 0.0 )
2435          /* var has been fixed */
2436          return SCIP_OKAY;
2437 
2438       if( !SCIPvarIsActive(var) )
2439       {
2440          /* var should be multi-aggregated, so call this function recursively */
2441          int i;
2442 
2443          assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);
2444          for( i = 0; i < SCIPvarGetMultaggrNVars(var); ++i )
2445          {
2446             SCIP_CALL( SCIPnlrowAddLinearCoef(nlrow, blkmem, set, stat, nlp, SCIPvarGetMultaggrVars(var)[i], SCIPvarGetMultaggrScalars(var)[i] * val) );
2447          }
2448          return SCIP_OKAY;
2449       }
2450 
2451       /* var is active, so can go on like normal */
2452    }
2453 
2454    SCIP_CALL( nlrowAddLinearCoef(nlrow, blkmem, set, stat, nlp, var, val) );
2455 
2456    return SCIP_OKAY;
2457 }
2458 
2459 /** deletes linear coefficient from nonlinear row */
SCIPnlrowDelLinearCoef(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp,SCIP_VAR * var)2460 SCIP_RETCODE SCIPnlrowDelLinearCoef(
2461    SCIP_NLROW*           nlrow,              /**< nonlinear row to be changed */
2462    SCIP_SET*             set,                /**< global SCIP settings */
2463    SCIP_STAT*            stat,               /**< problem statistics data */
2464    SCIP_NLP*             nlp,                /**< current NLP data */
2465    SCIP_VAR*             var                 /**< coefficient to be deleted */
2466    )
2467 {
2468    int pos;
2469 
2470    assert(nlrow != NULL);
2471    assert(var   != NULL);
2472 
2473    /* if the row is in the NLP already, we can only have active variables, so var should also be active; in non-debug mode, one gets an error below */
2474    assert(nlrow->nlpindex == -1 || SCIPvarIsActive(var) );
2475 
2476    /* search the position of the variable in the row's variable vector */
2477    pos = nlrowSearchLinearCoef(nlrow, var);
2478    if( pos == -1 )
2479    {
2480       SCIPerrorMessage("coefficient for variable <%s> doesn't exist in nonlinear row <%s>\n", SCIPvarGetName(var), nlrow->name);
2481       return SCIP_INVALIDDATA;
2482    }
2483    assert(0 <= pos && pos < nlrow->nlinvars);
2484    assert(nlrow->linvars[pos] == var);
2485 
2486    /* delete the variable from the row's variable vector */
2487    SCIP_CALL( nlrowDelLinearCoefPos(nlrow, set, stat, nlp, pos) );
2488 
2489    return SCIP_OKAY;
2490 }
2491 
2492 /** changes or adds a linear coefficient to a nonlinear row */
SCIPnlrowChgLinearCoef(SCIP_NLROW * nlrow,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp,SCIP_VAR * var,SCIP_Real coef)2493 SCIP_RETCODE SCIPnlrowChgLinearCoef(
2494    SCIP_NLROW*           nlrow,              /**< nonlinear row */
2495    BMS_BLKMEM*           blkmem,             /**< block memory */
2496    SCIP_SET*             set,                /**< global SCIP settings */
2497    SCIP_STAT*            stat,               /**< problem statistics data */
2498    SCIP_NLP*             nlp,                /**< current NLP data */
2499    SCIP_VAR*             var,                /**< variable */
2500    SCIP_Real             coef                /**< new value of coefficient */
2501    )
2502 {
2503    int pos;
2504 
2505    assert(nlrow != NULL);
2506    assert(nlp != NULL);
2507    assert(var != NULL);
2508 
2509    /* search the position of the variable in the row's linvars vector */
2510    pos = nlrowSearchLinearCoef(nlrow, var);
2511 
2512    /* check, if column already exists in the row's linear variables vector */
2513    if( pos == -1 )
2514    {
2515       if( !SCIPsetIsZero(set, coef) )
2516       {
2517          /* add previously not existing coefficient */
2518          SCIP_CALL( nlrowAddLinearCoef(nlrow, blkmem, set, stat, nlp, var, coef) );
2519       }
2520    }
2521    else
2522    {
2523       /* change the coefficient in the row */
2524       SCIP_CALL( nlrowChgLinearCoefPos(nlrow, set, stat, nlp, pos, coef) );
2525    }
2526 
2527    return SCIP_OKAY;
2528 }
2529 
2530 /** ensures, that quadratic variables array of nonlinear row can store at least num entries */
SCIPnlrowEnsureQuadVarsSize(SCIP_NLROW * nlrow,BMS_BLKMEM * blkmem,SCIP_SET * set,int num)2531 SCIP_RETCODE SCIPnlrowEnsureQuadVarsSize(
2532    SCIP_NLROW*           nlrow,              /**< NLP row */
2533    BMS_BLKMEM*           blkmem,             /**< block memory */
2534    SCIP_SET*             set,                /**< global SCIP settings */
2535    int                   num                 /**< minimum number of entries to store */
2536    )
2537 {
2538    assert(nlrow != NULL);
2539    assert(nlrow->nquadvars <= nlrow->quadvarssize);
2540 
2541    if( num > nlrow->quadvarssize )
2542    {
2543       int newsize;
2544 
2545       newsize = SCIPsetCalcMemGrowSize(set, num);
2546       SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &nlrow->quadvars, nlrow->quadvarssize, newsize) );
2547       nlrow->quadvarssize = newsize;
2548    }
2549    assert(num <= nlrow->quadvarssize);
2550 
2551    return SCIP_OKAY;
2552 }
2553 
2554 /** adds variable to quadvars array of row */
SCIPnlrowAddQuadVar(SCIP_NLROW * nlrow,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_VAR * var)2555 SCIP_RETCODE SCIPnlrowAddQuadVar(
2556    SCIP_NLROW*           nlrow,              /**< nonlinear row */
2557    BMS_BLKMEM*           blkmem,             /**< block memory */
2558    SCIP_SET*             set,                /**< global SCIP settings */
2559    SCIP_VAR*             var                 /**< variable to search for */
2560    )
2561 {
2562    assert(blkmem != NULL);
2563    assert(nlrow  != NULL);
2564    assert(var    != NULL);
2565 
2566    /* assert that only active variables are added once the row is in the NLP */
2567    assert(nlrow->nlpindex == -1 || SCIPvarIsActive(var) );
2568 
2569    /* assert that variable has not been added already */
2570    assert(SCIPnlrowSearchQuadVar(nlrow, var) == -1);
2571 
2572    SCIP_CALL( SCIPnlrowEnsureQuadVarsSize(nlrow, blkmem, set, nlrow->nquadvars+1) );
2573    nlrow->quadvars[nlrow->nquadvars] = var;
2574    nlrow->nquadvars++;
2575 
2576    if( nlrow->quadvarshash == NULL )
2577    {
2578       SCIP_CALL( nlrowSetupQuadVarsHash(nlrow, blkmem) );
2579    }
2580    else
2581    {
2582       SCIP_CALL( SCIPhashmapInsertInt(nlrow->quadvarshash, (void*)var, nlrow->nquadvars-1) );
2583    }
2584    assert(SCIPnlrowSearchQuadVar(nlrow, var) == nlrow->nquadvars-1);
2585 
2586    return SCIP_OKAY;
2587 }
2588 
2589 /** ensures, that quadratic elements array of nonlinear row can store at least num entries */
SCIPnlrowEnsureQuadElementsSize(SCIP_NLROW * nlrow,BMS_BLKMEM * blkmem,SCIP_SET * set,int num)2590 SCIP_RETCODE SCIPnlrowEnsureQuadElementsSize(
2591    SCIP_NLROW*           nlrow,              /**< NLP row */
2592    BMS_BLKMEM*           blkmem,             /**< block memory */
2593    SCIP_SET*             set,                /**< global SCIP settings */
2594    int                   num                 /**< minimum number of entries to store */
2595    )
2596 {
2597    assert(nlrow != NULL);
2598    assert(nlrow->nquadelems <= nlrow->quadelemssize);
2599 
2600    if( num > nlrow->quadelemssize )
2601    {
2602       int newsize;
2603 
2604       newsize = SCIPsetCalcMemGrowSize(set, num);
2605       SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &nlrow->quadelems, nlrow->quadelemssize, newsize) );
2606       nlrow->quadelemssize = newsize;
2607    }
2608    assert(num <= nlrow->quadelemssize);
2609 
2610    return SCIP_OKAY;
2611 }
2612 
2613 /** adds a previously non existing quadratic element to an NLP nonlinear row */
SCIPnlrowAddQuadElement(SCIP_NLROW * nlrow,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp,SCIP_QUADELEM elem)2614 SCIP_RETCODE SCIPnlrowAddQuadElement(
2615    SCIP_NLROW*           nlrow,              /**< NLP nonlinear row */
2616    BMS_BLKMEM*           blkmem,             /**< block memory */
2617    SCIP_SET*             set,                /**< global SCIP settings */
2618    SCIP_STAT*            stat,               /**< problem statistics data */
2619    SCIP_NLP*             nlp,                /**< current NLP data */
2620    SCIP_QUADELEM         elem                /**< quadratic element to add */
2621    )
2622 {
2623    SCIP_CALL( nlrowAddQuadElement(nlrow, blkmem, set, stat, nlp, elem) );
2624 
2625    return SCIP_OKAY;
2626 }
2627 
2628 /** deletes quadratic element from nonlinear row */
SCIPnlrowDelQuadElement(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp,int idx1,int idx2)2629 SCIP_RETCODE SCIPnlrowDelQuadElement(
2630    SCIP_NLROW*           nlrow,              /**< nonlinear row to be changed */
2631    SCIP_SET*             set,                /**< global SCIP settings */
2632    SCIP_STAT*            stat,               /**< problem statistics data */
2633    SCIP_NLP*             nlp,                /**< current NLP data */
2634    int                   idx1,               /**< index of first variable in element */
2635    int                   idx2                /**< index of second variable in element */
2636    )
2637 {
2638    int pos;
2639 
2640    assert(nlrow != NULL);
2641    assert(idx1 >= 0);
2642    assert(idx1 <  nlrow->nquadvars);
2643    assert(idx2 >= 0);
2644    assert(idx2 <  nlrow->nquadvars);
2645    assert(idx1 <= idx2);
2646 
2647    /* search the position of the variable in the row's variable vector */
2648    pos = nlrowSearchQuadElem(nlrow, idx1, idx2);
2649    if( pos == -1 )
2650    {
2651       SCIPerrorMessage("coefficient for index pair (%d, %d) doesn't exist in nonlinear row <%s>\n", idx1, idx2, nlrow->name);
2652       return SCIP_INVALIDDATA;
2653    }
2654    assert(0 <= pos && pos < nlrow->nquadelems);
2655 
2656    /* delete the element from the row's quadratic elements array */
2657    SCIP_CALL( nlrowDelQuadElemPos(nlrow, set, stat, nlp, pos) );
2658 
2659    return SCIP_OKAY;
2660 }
2661 
2662 /** changes or adds a quadratic element to a nonlinear row */
SCIPnlrowChgQuadElem(SCIP_NLROW * nlrow,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp,SCIP_QUADELEM elem)2663 SCIP_RETCODE SCIPnlrowChgQuadElem(
2664    SCIP_NLROW*           nlrow,              /**< nonlinear row */
2665    BMS_BLKMEM*           blkmem,             /**< block memory */
2666    SCIP_SET*             set,                /**< global SCIP settings */
2667    SCIP_STAT*            stat,               /**< problem statistics data */
2668    SCIP_NLP*             nlp,                /**< current NLP data */
2669    SCIP_QUADELEM         elem                /**< new quadratic element */
2670    )
2671 {
2672    int pos;
2673 
2674    assert(nlrow != NULL);
2675    assert(nlp != NULL);
2676 
2677    /* search the position of the variable in the row's linvars vector */
2678    pos = nlrowSearchQuadElem(nlrow, elem.idx1, elem.idx2);
2679 
2680    if( pos == -1 )
2681    {
2682       /* add previously not existing element */
2683       SCIP_CALL( nlrowAddQuadElement(nlrow, blkmem, set, stat, nlp, elem) );
2684    }
2685    else
2686    {
2687       /* change the coefficient in the row */
2688       SCIP_CALL( nlrowChgQuadElemPos(nlrow, set, stat, nlp, pos, elem.coef) );
2689    }
2690 
2691    return SCIP_OKAY;
2692 }
2693 
2694 /** replaces an expression tree in nonlinear row */
SCIPnlrowChgExprtree(SCIP_NLROW * nlrow,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp,SCIP_EXPRTREE * exprtree)2695 SCIP_RETCODE SCIPnlrowChgExprtree(
2696    SCIP_NLROW*           nlrow,              /**< nonlinear row */
2697    BMS_BLKMEM*           blkmem,             /**< block memory */
2698    SCIP_SET*             set,                /**< global SCIP settings */
2699    SCIP_STAT*            stat,               /**< problem statistics data */
2700    SCIP_NLP*             nlp,                /**< current NLP data */
2701    SCIP_EXPRTREE*        exprtree            /**< new expression tree */
2702    )
2703 {
2704    assert(nlrow  != NULL);
2705    assert(blkmem != NULL);
2706 
2707    /* free previous expression tree */
2708    if( nlrow->exprtree != NULL )
2709    {
2710       SCIP_CALL( SCIPexprtreeFree(&nlrow->exprtree) );
2711       assert(nlrow->exprtree == NULL);
2712    }
2713 
2714    /* adds new expression tree */
2715    if( exprtree != NULL )
2716    {
2717       SCIP_CALL( SCIPexprtreeCopy(blkmem, &nlrow->exprtree, exprtree) );
2718 
2719       /* if row is already in NLP, ensure that exprtree has only active variables */
2720       if( nlrow->nlpindex >= 0 )
2721       {
2722          SCIP_Bool dummy;
2723          SCIP_CALL( SCIPexprtreeRemoveFixedVars(nlrow->exprtree, set, &dummy, NULL, NULL) );
2724       }  /*lint !e438*/
2725    }
2726 
2727    /* notify row about the change */
2728    SCIP_CALL( nlrowExprtreeChanged(nlrow, set, stat, nlp) );
2729 
2730    return SCIP_OKAY;
2731 }
2732 
2733 /** changes a parameter in an expression of a nonlinear row */
SCIPnlrowChgExprtreeParam(SCIP_NLROW * nlrow,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp,int paramidx,SCIP_Real paramval)2734 SCIP_RETCODE SCIPnlrowChgExprtreeParam(
2735    SCIP_NLROW*           nlrow,              /**< nonlinear row */
2736    BMS_BLKMEM*           blkmem,             /**< block memory */
2737    SCIP_SET*             set,                /**< global SCIP settings */
2738    SCIP_STAT*            stat,               /**< problem statistics data */
2739    SCIP_NLP*             nlp,                /**< current NLP data */
2740    int                   paramidx,           /**< index of parameter in expression tree's parameter array */
2741    SCIP_Real             paramval            /**< new value of parameter */
2742    )
2743 {
2744    assert(nlrow  != NULL);
2745    assert(blkmem != NULL);
2746    assert(nlrow->exprtree != NULL);
2747 
2748    SCIPexprtreeSetParamVal(nlrow->exprtree, paramidx, paramval);
2749 
2750    /* notify row about the change */
2751    SCIP_CALL( nlrowExprtreeParamChanged(nlrow, set, stat, paramidx, nlp) );
2752 
2753    return SCIP_OKAY;
2754 }
2755 
2756 /** changes all parameters in an expression of a nonlinear row */
SCIPnlrowChgExprtreeParams(SCIP_NLROW * nlrow,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp,SCIP_Real * paramvals)2757 SCIP_RETCODE SCIPnlrowChgExprtreeParams(
2758    SCIP_NLROW*           nlrow,              /**< nonlinear row */
2759    BMS_BLKMEM*           blkmem,             /**< block memory */
2760    SCIP_SET*             set,                /**< global SCIP settings */
2761    SCIP_STAT*            stat,               /**< problem statistics data */
2762    SCIP_NLP*             nlp,                /**< current NLP data */
2763    SCIP_Real*            paramvals           /**< new values of parameters */
2764    )
2765 {
2766    assert(nlrow  != NULL);
2767    assert(blkmem != NULL);
2768    assert(nlrow->exprtree != NULL);
2769 
2770    SCIP_CALL( SCIPexprtreeSetParams(nlrow->exprtree, SCIPexprtreeGetNParams(nlrow->exprtree), paramvals) );
2771 
2772    /* notify row about the change */
2773    SCIP_CALL( nlrowExprtreeParamChanged(nlrow, set, stat, -1, nlp) );
2774 
2775    return SCIP_OKAY;
2776 }
2777 
2778 /** changes constant of nonlinear row */
SCIPnlrowChgConstant(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp,SCIP_Real constant)2779 SCIP_RETCODE SCIPnlrowChgConstant(
2780    SCIP_NLROW*           nlrow,              /**< nonlinear row */
2781    SCIP_SET*             set,                /**< global SCIP settings */
2782    SCIP_STAT*            stat,               /**< problem statistics data */
2783    SCIP_NLP*             nlp,                /**< current NLP data */
2784    SCIP_Real             constant            /**< new constant */
2785    )
2786 {
2787    assert(nlrow != NULL);
2788 
2789    if( !SCIPsetIsEQ(set, nlrow->constant, constant) )
2790    {
2791       nlrow->constant = constant;
2792       SCIP_CALL( nlrowConstantChanged(nlrow, set, stat, nlp) );
2793    }
2794 
2795    return SCIP_OKAY;
2796 }
2797 
2798 /** changes left hand side of nonlinear row */
SCIPnlrowChgLhs(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp,SCIP_Real lhs)2799 SCIP_RETCODE SCIPnlrowChgLhs(
2800    SCIP_NLROW*           nlrow,              /**< nonlinear row */
2801    SCIP_SET*             set,                /**< global SCIP settings */
2802    SCIP_STAT*            stat,               /**< problem statistics data */
2803    SCIP_NLP*             nlp,                /**< current NLP data */
2804    SCIP_Real             lhs                 /**< new left hand side */
2805    )
2806 {
2807    assert(nlrow != NULL);
2808 
2809    if( !SCIPsetIsEQ(set, nlrow->lhs, lhs) )
2810    {
2811       nlrow->lhs = lhs;
2812       SCIP_CALL( nlrowSideChanged(nlrow, set, stat, nlp) );
2813    }
2814 
2815    return SCIP_OKAY;
2816 }
2817 
2818 /** changes right hand side of nonlinear row */
SCIPnlrowChgRhs(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp,SCIP_Real rhs)2819 SCIP_RETCODE SCIPnlrowChgRhs(
2820    SCIP_NLROW*           nlrow,              /**< nonlinear row */
2821    SCIP_SET*             set,                /**< global SCIP settings */
2822    SCIP_STAT*            stat,               /**< problem statistics data */
2823    SCIP_NLP*             nlp,                /**< current NLP data */
2824    SCIP_Real             rhs                 /**< new right hand side */
2825    )
2826 {
2827    assert(nlrow != NULL);
2828 
2829    if( !SCIPsetIsEQ(set, nlrow->rhs, rhs) )
2830    {
2831       nlrow->rhs = rhs;
2832       SCIP_CALL( nlrowSideChanged(nlrow, set, stat, nlp) );
2833    }
2834 
2835    return SCIP_OKAY;
2836 }
2837 
2838 /** removes (or substitutes) all fixed, negated, aggregated, multi-aggregated variables from the linear, quadratic, and non-quadratic terms of a nonlinear row */
SCIPnlrowRemoveFixedVars(SCIP_NLROW * nlrow,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp)2839 SCIP_RETCODE SCIPnlrowRemoveFixedVars(
2840    SCIP_NLROW*           nlrow,              /**< nonlinear row */
2841    BMS_BLKMEM*           blkmem,             /**< block memory */
2842    SCIP_SET*             set,                /**< global SCIP settings */
2843    SCIP_STAT*            stat,               /**< problem statistics data */
2844    SCIP_NLP*             nlp                 /**< current NLP data */
2845    )
2846 {
2847    SCIP_CALL( nlrowRemoveFixedLinearCoefs(nlrow, blkmem, set, stat, nlp) );
2848    SCIP_CALL( nlrowRemoveFixedQuadVars(nlrow, blkmem, set, stat, nlp) );
2849    SCIP_CALL( nlrowRemoveFixedExprtreeVars(nlrow, set, stat, nlp) );
2850 
2851    return SCIP_OKAY;
2852 }
2853 
2854 /** recalculates the current activity of a nonlinear row */
SCIPnlrowRecalcNLPActivity(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp)2855 SCIP_RETCODE SCIPnlrowRecalcNLPActivity(
2856    SCIP_NLROW*           nlrow,              /**< nonlinear row */
2857    SCIP_SET*             set,                /**< global SCIP settings */
2858    SCIP_STAT*            stat,               /**< problem statistics */
2859    SCIP_NLP*             nlp                 /**< current NLP data */
2860    )
2861 {
2862    SCIP_Real val1, val2;
2863    int i;
2864    int previdx1;
2865 
2866    assert(nlrow  != NULL);
2867    assert(stat   != NULL);
2868    assert(nlp    != NULL);
2869 
2870    if( nlp->solstat > SCIP_NLPSOLSTAT_LOCINFEASIBLE )
2871    {
2872       SCIPerrorMessage("do not have NLP solution for computing NLP activity\n");
2873       return SCIP_ERROR;
2874    }
2875 
2876    nlrow->activity = nlrow->constant;
2877    for( i = 0; i < nlrow->nlinvars; ++i )
2878    {
2879       assert(nlrow->linvars[i] != NULL);
2880       assert(SCIPvarGetNLPSol(nlrow->linvars[i]) < SCIP_INVALID);
2881 
2882       nlrow->activity += nlrow->lincoefs[i] * SCIPvarGetNLPSol(nlrow->linvars[i]);
2883    }
2884 
2885    val1 = 0.0; /* for lint */
2886    previdx1 = -1;
2887    for( i = 0; i < nlrow->nquadelems; ++i )
2888    {
2889       /* if first index of quadelems is the same as in last round, val1 is still up to date */
2890       if( previdx1 != nlrow->quadelems[i].idx1 )
2891       {
2892          previdx1 = nlrow->quadelems[i].idx1;
2893          val1 = SCIPvarGetNLPSol(nlrow->quadvars[previdx1]);
2894          assert(val1 < SCIP_INVALID);
2895 
2896          if( val1 == 0.0 )
2897             continue;
2898       }
2899 
2900       val2 = SCIPvarGetNLPSol(nlrow->quadvars[nlrow->quadelems[i].idx2]);
2901       assert(val2 < SCIP_INVALID);
2902 
2903       nlrow->activity += nlrow->quadelems[i].coef * val1 * val2;
2904    }
2905 
2906    if( nlrow->exprtree != NULL )
2907    {
2908       SCIP_Real* varvals;
2909       int n;
2910 
2911       n = SCIPexprtreeGetNVars(nlrow->exprtree);
2912 
2913       SCIP_CALL( SCIPsetAllocBufferArray(set, &varvals, n) );
2914 
2915       for( i = 0; i < n; ++i )
2916       {
2917          varvals[i] = SCIPvarGetNLPSol(SCIPexprtreeGetVars(nlrow->exprtree)[i]);
2918       }
2919 
2920       SCIP_CALL( SCIPexprtreeEval(nlrow->exprtree, varvals, &val1) );
2921       nlrow->activity += val1;
2922 
2923       SCIPsetFreeBufferArray(set, &varvals);
2924    }
2925 
2926    nlrow->validactivitynlp = stat->nnlps;
2927 
2928    return SCIP_OKAY;
2929 }
2930 
2931 /** returns the activity of a nonlinear row in the current NLP solution */
SCIPnlrowGetNLPActivity(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp,SCIP_Real * activity)2932 SCIP_RETCODE SCIPnlrowGetNLPActivity(
2933    SCIP_NLROW*           nlrow,              /**< nonlinear row */
2934    SCIP_SET*             set,                /**< global SCIP settings */
2935    SCIP_STAT*            stat,               /**< problem statistics */
2936    SCIP_NLP*             nlp,                /**< current NLP data */
2937    SCIP_Real*            activity            /**< buffer to store activity value */
2938    )
2939 {
2940    assert(nlrow  != NULL);
2941    assert(stat   != NULL);
2942    assert(activity != NULL);
2943 
2944    assert(nlrow->validactivitynlp <= stat->nnlps);
2945 
2946    if( nlrow->validactivitynlp != stat->nnlps )
2947    {
2948       SCIP_CALL( SCIPnlrowRecalcNLPActivity(nlrow, set, stat, nlp) );
2949    }
2950    assert(nlrow->validactivitynlp == stat->nnlps);
2951    assert(nlrow->activity < SCIP_INVALID);
2952 
2953    *activity = nlrow->activity;
2954 
2955    return SCIP_OKAY;
2956 }
2957 
2958 /** gives the feasibility of a nonlinear row in the current NLP solution: negative value means infeasibility */
SCIPnlrowGetNLPFeasibility(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLP * nlp,SCIP_Real * feasibility)2959 SCIP_RETCODE SCIPnlrowGetNLPFeasibility(
2960    SCIP_NLROW*           nlrow,              /**< nonlinear row */
2961    SCIP_SET*             set,                /**< global SCIP settings */
2962    SCIP_STAT*            stat,               /**< problem statistics */
2963    SCIP_NLP*             nlp,                /**< current NLP data */
2964    SCIP_Real*            feasibility         /**< buffer to store feasibility value */
2965    )
2966 {
2967    SCIP_Real activity;
2968 
2969    assert(nlrow != NULL);
2970    assert(feasibility != NULL);
2971 
2972    SCIP_CALL( SCIPnlrowGetNLPActivity(nlrow, set, stat, nlp, &activity) );
2973    *feasibility = MIN(nlrow->rhs - activity, activity - nlrow->lhs);
2974 
2975    return SCIP_OKAY;
2976 }
2977 
2978 /** calculates the current pseudo activity of a nonlinear row */
SCIPnlrowRecalcPseudoActivity(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat)2979 SCIP_RETCODE SCIPnlrowRecalcPseudoActivity(
2980    SCIP_NLROW*           nlrow,              /**< nonlinear row */
2981    SCIP_SET*             set,                /**< global SCIP settings */
2982    SCIP_STAT*            stat                /**< problem statistics */
2983    )
2984 {
2985    SCIP_Real val1, val2;
2986    int i;
2987 
2988    assert(nlrow  != NULL);
2989    assert(stat   != NULL);
2990 
2991    nlrow->pseudoactivity = nlrow->constant;
2992    for( i = 0; i < nlrow->nlinvars; ++i )
2993    {
2994       assert(nlrow->linvars[i] != NULL);
2995 
2996       val1 = SCIPvarGetBestBoundLocal(nlrow->linvars[i]);
2997       nlrow->pseudoactivity += nlrow->lincoefs[i] * val1;
2998    }
2999 
3000    for( i = 0; i < nlrow->nquadelems; ++i )
3001    {
3002       val1 = SCIPvarGetBestBoundLocal(nlrow->quadvars[nlrow->quadelems[i].idx1]);
3003       if( val1 == 0.0 )
3004          continue;
3005 
3006       val2 = SCIPvarGetBestBoundLocal(nlrow->quadvars[nlrow->quadelems[i].idx2]);
3007       nlrow->pseudoactivity += nlrow->quadelems[i].coef * val1 * val2;
3008    }
3009 
3010    if( nlrow->exprtree != NULL )
3011    {
3012       SCIP_Real* varvals;
3013       int n;
3014 
3015       n = SCIPexprtreeGetNVars(nlrow->exprtree);
3016 
3017       SCIP_CALL( SCIPsetAllocBufferArray(set, &varvals, n) );
3018 
3019       for( i = 0; i < n; ++i )
3020          varvals[i] = SCIPvarGetBestBoundLocal(SCIPexprtreeGetVars(nlrow->exprtree)[i]);
3021 
3022       SCIP_CALL( SCIPexprtreeEval(nlrow->exprtree, varvals, &val1) );
3023       nlrow->pseudoactivity += val1;
3024 
3025       SCIPsetFreeBufferArray(set, &varvals);
3026    }
3027 
3028    nlrow->validpsactivitydomchg = stat->domchgcount;
3029 
3030    return SCIP_OKAY;
3031 }
3032 
3033 /** returns the pseudo activity of a nonlinear row in the current pseudo solution */
SCIPnlrowGetPseudoActivity(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat,SCIP_Real * pseudoactivity)3034 SCIP_RETCODE SCIPnlrowGetPseudoActivity(
3035    SCIP_NLROW*           nlrow,              /**< nonlinear row */
3036    SCIP_SET*             set,                /**< global SCIP settings */
3037    SCIP_STAT*            stat,               /**< problem statistics */
3038    SCIP_Real*            pseudoactivity      /**< buffer to store pseudo activity value */
3039    )
3040 {
3041    assert(nlrow != NULL);
3042    assert(stat  != NULL);
3043    assert(pseudoactivity != NULL);
3044    assert(nlrow->validpsactivitydomchg <= stat->domchgcount);
3045 
3046    /* check, if pseudo activity has to be calculated */
3047    if( nlrow->validpsactivitydomchg != stat->domchgcount )
3048    {
3049       SCIP_CALL( SCIPnlrowRecalcPseudoActivity(nlrow, set, stat) );
3050    }
3051    assert(nlrow->validpsactivitydomchg == stat->domchgcount);
3052    assert(nlrow->pseudoactivity < SCIP_INVALID);
3053 
3054    *pseudoactivity = nlrow->pseudoactivity;
3055 
3056    return SCIP_OKAY;
3057 }
3058 
3059 /** returns the pseudo feasibility of a nonlinear row in the current pseudo solution: negative value means infeasibility */
SCIPnlrowGetPseudoFeasibility(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat,SCIP_Real * pseudofeasibility)3060 SCIP_RETCODE SCIPnlrowGetPseudoFeasibility(
3061    SCIP_NLROW*           nlrow,              /**< nonlinear row */
3062    SCIP_SET*             set,                /**< global SCIP settings */
3063    SCIP_STAT*            stat,               /**< problem statistics */
3064    SCIP_Real*            pseudofeasibility   /**< buffer to store pseudo feasibility value */
3065    )
3066 {
3067    SCIP_Real pseudoactivity;
3068 
3069    assert(nlrow != NULL);
3070    assert(stat  != NULL);
3071    assert(pseudofeasibility != NULL);
3072 
3073    SCIP_CALL( SCIPnlrowGetPseudoActivity(nlrow, set, stat, &pseudoactivity) );
3074    *pseudofeasibility = MIN(nlrow->rhs - pseudoactivity, pseudoactivity - nlrow->lhs);
3075 
3076    return SCIP_OKAY;
3077 }
3078 
3079 /** returns the activity of a nonlinear row for a given solution */
SCIPnlrowGetSolActivity(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat,SCIP_SOL * sol,SCIP_Real * activity)3080 SCIP_RETCODE SCIPnlrowGetSolActivity(
3081    SCIP_NLROW*           nlrow,              /**< nonlinear row */
3082    SCIP_SET*             set,                /**< global SCIP settings */
3083    SCIP_STAT*            stat,               /**< problem statistics data */
3084    SCIP_SOL*             sol,                /**< primal CIP solution */
3085    SCIP_Real*            activity            /**< buffer to store activity value */
3086    )
3087 {
3088    SCIP_Real inf;
3089    SCIP_Real val1, val2;
3090    int i;
3091 
3092    assert(nlrow != NULL);
3093    assert(set != NULL);
3094    assert(stat != NULL);
3095    assert(activity != NULL);
3096 
3097    *activity = nlrow->constant;
3098    for( i = 0; i < nlrow->nlinvars; ++i )
3099    {
3100       assert(nlrow->linvars[i] != NULL);
3101 
3102       val1 = SCIPsolGetVal(sol, set, stat, nlrow->linvars[i]);
3103       if( val1 == SCIP_UNKNOWN ) /*lint !e777*/
3104       {
3105          *activity = SCIP_INVALID;
3106          return SCIP_OKAY;
3107       }
3108       *activity += nlrow->lincoefs[i] * val1;
3109    }
3110 
3111    for( i = 0; i < nlrow->nquadelems; ++i )
3112    {
3113       val1 = SCIPsolGetVal(sol, set, stat, nlrow->quadvars[nlrow->quadelems[i].idx1]);
3114       if( val1 == SCIP_UNKNOWN ) /*lint !e777*/
3115       {
3116          *activity = SCIP_INVALID;
3117          return SCIP_OKAY;
3118       }
3119       if( val1 == 0.0 )
3120          continue;
3121 
3122       val2 = SCIPsolGetVal(sol, set, stat, nlrow->quadvars[nlrow->quadelems[i].idx2]);
3123       if( val2 == SCIP_UNKNOWN ) /*lint !e777*/
3124       {
3125          *activity = SCIP_INVALID;
3126          return SCIP_OKAY;
3127       }
3128       *activity += nlrow->quadelems[i].coef * val1 * val2;
3129    }
3130 
3131    if( nlrow->exprtree != NULL )
3132    {
3133       SCIP_Real* varvals;
3134       int n;
3135 
3136       n = SCIPexprtreeGetNVars(nlrow->exprtree);
3137 
3138       SCIP_CALL( SCIPsetAllocBufferArray(set, &varvals, n) );
3139 
3140       for( i = 0; i < n; ++i )
3141       {
3142          varvals[i] = SCIPsolGetVal(sol, set, stat, SCIPexprtreeGetVars(nlrow->exprtree)[i]);
3143          if( varvals[i] == SCIP_UNKNOWN ) /*lint !e777*/
3144          {
3145             *activity = SCIP_INVALID;
3146             SCIPsetFreeBufferArray(set, &varvals);
3147             return SCIP_OKAY;
3148          }
3149       }
3150 
3151       SCIP_CALL( SCIPexprtreeEval(nlrow->exprtree, varvals, &val1) );
3152       *activity += val1;
3153 
3154       SCIPsetFreeBufferArray(set, &varvals);
3155    }
3156 
3157    inf = SCIPsetInfinity(set);
3158    *activity = MAX(*activity, -inf);
3159    *activity = MIN(*activity, +inf);
3160 
3161    return SCIP_OKAY;
3162 }
3163 
3164 /** returns the feasibility of a nonlinear row for the given solution */
SCIPnlrowGetSolFeasibility(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat,SCIP_SOL * sol,SCIP_Real * feasibility)3165 SCIP_RETCODE SCIPnlrowGetSolFeasibility(
3166    SCIP_NLROW*           nlrow,              /**< nonlinear row */
3167    SCIP_SET*             set,                /**< global SCIP settings */
3168    SCIP_STAT*            stat,               /**< problem statistics data */
3169    SCIP_SOL*             sol,                /**< primal CIP solution */
3170    SCIP_Real*            feasibility         /**< buffer to store feasibility value */
3171    )
3172 {
3173    SCIP_Real activity;
3174 
3175    assert(nlrow != NULL);
3176    assert(feasibility != NULL);
3177 
3178    SCIP_CALL( SCIPnlrowGetSolActivity(nlrow, set, stat, sol, &activity) );
3179 
3180    *feasibility = MIN(nlrow->rhs - activity, activity - nlrow->lhs);
3181 
3182    return SCIP_OKAY;
3183 }
3184 
3185 /** returns the minimal activity of a nonlinear row w.r.t. the variables' bounds */
SCIPnlrowGetActivityBounds(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat,SCIP_Real * minactivity,SCIP_Real * maxactivity)3186 SCIP_RETCODE SCIPnlrowGetActivityBounds(
3187    SCIP_NLROW*           nlrow,              /**< nonlinear row */
3188    SCIP_SET*             set,                /**< global SCIP settings */
3189    SCIP_STAT*            stat,               /**< problem statistics data */
3190    SCIP_Real*            minactivity,        /**< buffer to store minimal activity, or NULL */
3191    SCIP_Real*            maxactivity         /**< buffer to store maximal activity, or NULL */
3192    )
3193 {
3194    assert(nlrow != NULL);
3195    assert(set != NULL);
3196    assert(stat != NULL);
3197    assert(nlrow->validactivitybdsdomchg <= stat->domchgcount);
3198 
3199    /* check, if activity bounds has to be calculated */
3200    if( nlrow->validactivitybdsdomchg != stat->domchgcount )
3201    {
3202       SCIP_CALL( nlrowCalcActivityBounds(nlrow, set, stat) );
3203    }
3204    assert(nlrow->validactivitybdsdomchg == stat->domchgcount);
3205    assert(nlrow->minactivity < SCIP_INVALID);
3206    assert(nlrow->maxactivity < SCIP_INVALID);
3207 
3208    if( minactivity != NULL )
3209       *minactivity = nlrow->minactivity;
3210    if( maxactivity != NULL )
3211       *maxactivity = nlrow->maxactivity;
3212 
3213    return SCIP_OKAY;
3214 }
3215 
3216 /** returns whether the nonlinear row is redundant w.r.t. the variables' bounds */
SCIPnlrowIsRedundant(SCIP_NLROW * nlrow,SCIP_SET * set,SCIP_STAT * stat,SCIP_Bool * isredundant)3217 SCIP_RETCODE SCIPnlrowIsRedundant(
3218    SCIP_NLROW*           nlrow,              /**< nonlinear row */
3219    SCIP_SET*             set,                /**< global SCIP settings */
3220    SCIP_STAT*            stat,               /**< problem statistics data */
3221    SCIP_Bool*            isredundant         /**< buffer to store whether row is redundant */
3222    )
3223 {
3224    SCIP_Real minactivity;
3225    SCIP_Real maxactivity;
3226 
3227    assert(nlrow != NULL);
3228    assert(set != NULL);
3229    assert(isredundant != NULL);
3230 
3231    SCIP_CALL( SCIPnlrowGetActivityBounds(nlrow, set, stat, &minactivity, &maxactivity) );
3232 
3233    *isredundant = TRUE;
3234    if( (!SCIPsetIsInfinity(set, -nlrow->lhs) && SCIPsetIsFeasLT(set, minactivity, nlrow->lhs)) ||
3235       ( !SCIPsetIsInfinity(set,  nlrow->rhs) && SCIPsetIsFeasGT(set, maxactivity, nlrow->rhs)) )
3236       *isredundant = FALSE;
3237 
3238    return SCIP_OKAY;
3239 }
3240 
3241 /** gets constant */
SCIPnlrowGetConstant(SCIP_NLROW * nlrow)3242 SCIP_Real SCIPnlrowGetConstant(
3243    SCIP_NLROW*           nlrow               /**< NLP row */
3244    )
3245 {
3246    assert(nlrow != NULL);
3247 
3248    return nlrow->constant;
3249 }
3250 
3251 /** gets number of variables of linear part */
SCIPnlrowGetNLinearVars(SCIP_NLROW * nlrow)3252 int SCIPnlrowGetNLinearVars(
3253    SCIP_NLROW*           nlrow               /**< NLP row */
3254    )
3255 {
3256    assert(nlrow != NULL);
3257 
3258    return nlrow->nlinvars;
3259 }
3260 
3261 /** gets array with variables of linear part */
SCIPnlrowGetLinearVars(SCIP_NLROW * nlrow)3262 SCIP_VAR** SCIPnlrowGetLinearVars(
3263    SCIP_NLROW*           nlrow               /**< NLP row */
3264    )
3265 {
3266    assert(nlrow != NULL);
3267 
3268    return nlrow->linvars;
3269 }
3270 
3271 /** gets array with coefficients in linear part */
SCIPnlrowGetLinearCoefs(SCIP_NLROW * nlrow)3272 SCIP_Real* SCIPnlrowGetLinearCoefs(
3273    SCIP_NLROW*           nlrow               /**< NLP row */
3274    )
3275 {
3276    assert(nlrow != NULL);
3277 
3278    return nlrow->lincoefs;
3279 }
3280 
3281 /** gets number of quadratic variables in quadratic part */
SCIPnlrowGetNQuadVars(SCIP_NLROW * nlrow)3282 int SCIPnlrowGetNQuadVars(
3283    SCIP_NLROW*           nlrow               /**< NLP row */
3284    )
3285 {
3286    assert(nlrow != NULL);
3287 
3288    return nlrow->nquadvars;
3289 }
3290 
3291 /** gets quadratic variables in quadratic part */
SCIPnlrowGetQuadVars(SCIP_NLROW * nlrow)3292 SCIP_VAR** SCIPnlrowGetQuadVars(
3293    SCIP_NLROW*           nlrow               /**< NLP row */
3294    )
3295 {
3296    assert(nlrow != NULL);
3297 
3298    return nlrow->quadvars;
3299 }
3300 
3301 /** gives position of variable in quadvars array of row, or -1 if not found */
SCIPnlrowSearchQuadVar(SCIP_NLROW * nlrow,SCIP_VAR * var)3302 int SCIPnlrowSearchQuadVar(
3303    SCIP_NLROW*           nlrow,              /**< nonlinear row */
3304    SCIP_VAR*             var                 /**< variable to search for */
3305    )
3306 {
3307    int pos;
3308 
3309    assert(nlrow != NULL);
3310    assert(var   != NULL);
3311 
3312    if( nlrow->quadvarshash != NULL )
3313    {
3314       pos = SCIPhashmapExists(nlrow->quadvarshash, var) ? SCIPhashmapGetImageInt(nlrow->quadvarshash, var) : -1;
3315    }
3316    else
3317    {
3318       for( pos = nlrow->nquadvars-1; pos >= 0; --pos )
3319          if( nlrow->quadvars[pos] == var )
3320             break;
3321    }
3322 
3323    assert(pos == -1 || (pos < nlrow->nquadvars && nlrow->quadvars[pos] == var));
3324 
3325    return pos;
3326 }
3327 
3328 /** gets number of quadratic elements in quadratic part */
SCIPnlrowGetNQuadElems(SCIP_NLROW * nlrow)3329 int SCIPnlrowGetNQuadElems(
3330    SCIP_NLROW*           nlrow               /**< NLP row */
3331    )
3332 {
3333    assert(nlrow != NULL);
3334 
3335    return nlrow->nquadelems;
3336 }
3337 
3338 /** gets quadratic elements in quadratic part */
SCIPnlrowGetQuadElems(SCIP_NLROW * nlrow)3339 SCIP_QUADELEM* SCIPnlrowGetQuadElems(
3340    SCIP_NLROW*           nlrow               /**< NLP row */
3341    )
3342 {
3343    assert(nlrow != NULL);
3344 
3345    return nlrow->quadelems;
3346 }
3347 
3348 /** gets array with coefficients in linear part */
SCIPnlrowGetQuadData(SCIP_NLROW * nlrow,int * nquadvars,SCIP_VAR *** quadvars,int * nquadelems,SCIP_QUADELEM ** quadelems)3349 void SCIPnlrowGetQuadData(
3350    SCIP_NLROW*           nlrow,              /**< NLP row */
3351    int*                  nquadvars,          /**< buffer to store number of variables in quadratic term, or NULL if not of interest */
3352    SCIP_VAR***           quadvars,           /**< buffer to store pointer to array of variables in quadratic term, or NULL if not of interest */
3353    int*                  nquadelems,         /**< buffer to store number of entries in quadratic term, or NULL if not of interest */
3354    SCIP_QUADELEM**       quadelems           /**< buffer to store pointer to array of entries in quadratic term, or NULL if not of interest */
3355    )
3356 {
3357    assert(nlrow != NULL);
3358 
3359    if( nquadvars  != NULL )
3360       *nquadvars  = nlrow->nquadvars;
3361    if( quadvars   != NULL )
3362       *quadvars   = nlrow->quadvars;
3363    if( nquadelems != NULL )
3364       *nquadelems = nlrow->nquadelems;
3365    if( quadelems  != NULL )
3366       *quadelems  = nlrow->quadelems;
3367 }
3368 
3369 /** gets expression tree */
SCIPnlrowGetExprtree(SCIP_NLROW * nlrow)3370 SCIP_EXPRTREE* SCIPnlrowGetExprtree(
3371    SCIP_NLROW*           nlrow               /**< NLP row */
3372    )
3373 {
3374    assert(nlrow != NULL);
3375 
3376    return nlrow->exprtree;
3377 }
3378 
3379 /** returns the left hand side of a nonlinear row */
SCIPnlrowGetLhs(SCIP_NLROW * nlrow)3380 SCIP_Real SCIPnlrowGetLhs(
3381    SCIP_NLROW*           nlrow               /**< NLP row */
3382    )
3383 {
3384    assert(nlrow != NULL);
3385 
3386    return nlrow->lhs;
3387 }
3388 
3389 /** returns the right hand side of a nonlinear row */
SCIPnlrowGetRhs(SCIP_NLROW * nlrow)3390 SCIP_Real SCIPnlrowGetRhs(
3391    SCIP_NLROW*           nlrow               /**< NLP row */
3392    )
3393 {
3394    assert(nlrow != NULL);
3395 
3396    return nlrow->rhs;
3397 }
3398 
3399 /** returns the curvature of a nonlinear row */
SCIPnlrowGetCurvature(SCIP_NLROW * nlrow)3400 SCIP_EXPRCURV SCIPnlrowGetCurvature(
3401    SCIP_NLROW*           nlrow               /**< NLP row */
3402    )
3403 {
3404    assert(nlrow != NULL);
3405    return nlrow->curvature;
3406 }
3407 
3408 /** sets the curvature of a nonlinear row */
SCIPnlrowSetCurvature(SCIP_NLROW * nlrow,SCIP_EXPRCURV curvature)3409 void SCIPnlrowSetCurvature(
3410    SCIP_NLROW*           nlrow,              /**< NLP row */
3411    SCIP_EXPRCURV         curvature           /**< curvature of NLP row */
3412    )
3413 {
3414    assert(nlrow != NULL);
3415    nlrow->curvature = curvature;
3416 }
3417 
3418 /** returns the name of a nonlinear row */
SCIPnlrowGetName(SCIP_NLROW * nlrow)3419 const char* SCIPnlrowGetName(
3420    SCIP_NLROW*           nlrow               /**< NLP row */
3421    )
3422 {
3423    assert(nlrow != NULL);
3424 
3425    return nlrow->name;
3426 }
3427 
3428 /** gets position of a nonlinear row in current NLP, or -1 if not in NLP */
SCIPnlrowGetNLPPos(SCIP_NLROW * nlrow)3429 int SCIPnlrowGetNLPPos(
3430    SCIP_NLROW*           nlrow               /**< NLP row */
3431    )
3432 {
3433    assert(nlrow != NULL);
3434 
3435    return nlrow->nlpindex;
3436 }
3437 
3438 /** returns TRUE iff row is member of current NLP */
SCIPnlrowIsInNLP(SCIP_NLROW * nlrow)3439 SCIP_Bool SCIPnlrowIsInNLP(
3440    SCIP_NLROW*           nlrow               /**< NLP row */
3441    )
3442 {
3443    assert(nlrow != NULL);
3444 
3445    return nlrow->nlpindex != -1;
3446 }
3447 
3448 /** gets the dual NLP solution of a nlrow
3449  * for a ranged constraint, the dual value is positive if the right hand side is active and negative if the left hand side is active
3450  */
SCIPnlrowGetDualsol(SCIP_NLROW * nlrow)3451 SCIP_Real SCIPnlrowGetDualsol(
3452    SCIP_NLROW*           nlrow               /**< NLP row */
3453    )
3454 {
3455    assert(nlrow != NULL);
3456 
3457    return nlrow->nlpiindex >= 0 ? nlrow->dualsol : 0.0;
3458 }
3459 
3460 /*
3461  * private NLP methods
3462  */
3463 
3464 /** announces, that a row of the NLP was modified
3465  * adjusts status of current solution
3466  * calling method has to ensure that change is passed to the NLPI!
3467  */
3468 static
nlpRowChanged(SCIP_NLP * nlp,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLROW * nlrow)3469 SCIP_RETCODE nlpRowChanged(
3470    SCIP_NLP*             nlp,                /**< current NLP data */
3471    SCIP_SET*             set,                /**< global SCIP settings */
3472    SCIP_STAT*            stat,               /**< problem statistics data */
3473    SCIP_NLROW*           nlrow               /**< nonlinear row which was changed */
3474    )
3475 {
3476    assert(nlp != NULL);
3477    assert(nlrow != NULL);
3478    assert(!nlp->indiving);
3479    assert(nlrow->nlpindex >= 0);
3480 
3481    /* nlrow is a row in the NLP, so changes effect feasibility */
3482    /* if we have a feasible NLP solution and it satisfies the modified row, then it is still feasible
3483     * if the NLP was globally or locally infeasible or unbounded, then this may not be the case anymore
3484     */
3485    if( nlp->solstat <= SCIP_NLPSOLSTAT_FEASIBLE )
3486    {
3487       SCIP_Real feasibility;
3488       SCIP_CALL( SCIPnlrowGetNLPFeasibility(nlrow, set, stat, nlp, &feasibility) );
3489       if( !SCIPsetIsFeasNegative(set, feasibility) )
3490          nlp->solstat = SCIP_NLPSOLSTAT_FEASIBLE;
3491       else
3492          nlp->solstat = SCIP_NLPSOLSTAT_LOCINFEASIBLE;
3493    }
3494    else
3495    {
3496       nlp->solstat = SCIP_NLPSOLSTAT_UNKNOWN;
3497    }
3498 
3499    return SCIP_OKAY;
3500 }
3501 
3502 /** adds a set of nonlinear rows to the NLP and captures them */
3503 static
nlpAddNlRows(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,int nnlrows,SCIP_NLROW ** nlrows)3504 SCIP_RETCODE nlpAddNlRows(
3505    SCIP_NLP*             nlp,                /**< NLP data */
3506    BMS_BLKMEM*           blkmem,             /**< block memory */
3507    SCIP_SET*             set,                /**< global SCIP settings */
3508    SCIP_STAT*            stat,               /**< problem statistics data */
3509    int                   nnlrows,            /**< number of nonlinear rows to add */
3510    SCIP_NLROW**          nlrows              /**< nonlinear rows to add */
3511    )
3512 {
3513 #ifndef NDEBUG
3514    int i;
3515 #endif
3516    int j;
3517    SCIP_NLROW* nlrow;
3518 
3519    assert(nlp    != NULL);
3520    assert(blkmem != NULL);
3521    assert(set    != NULL);
3522    assert(nlrows != NULL || nnlrows == 0);
3523    assert(!nlp->indiving);
3524 
3525    SCIP_CALL( SCIPnlpEnsureNlRowsSize(nlp, blkmem, set, nlp->nnlrows + nnlrows) );
3526 
3527    for( j = 0; j < nnlrows; ++j )
3528    {
3529       nlrow = nlrows[j];  /*lint !e613*/
3530 
3531       /* assert that row is not in NLP (or even NLPI) yet */
3532       assert(nlrow->nlpindex == -1);
3533       assert(nlrow->nlpiindex == -1);
3534 
3535       /* make sure there are only active variables in row */
3536       SCIP_CALL( SCIPnlrowRemoveFixedVars(nlrow, blkmem, set, stat, nlp) );
3537 
3538 #ifndef NDEBUG
3539       /* assert that variables of row are in NLP */
3540       for( i = 0; i < nlrow->nlinvars; ++i )
3541          assert(SCIPhashmapExists(nlp->varhash, nlrow->linvars[i]));
3542 
3543       for( i = 0; i < nlrow->nquadvars; ++i )
3544          assert(SCIPhashmapExists(nlp->varhash, nlrow->quadvars[i]));
3545 
3546       if( nlrow->exprtree )
3547       {
3548          int n;
3549 
3550          n = SCIPexprtreeGetNVars(nlrow->exprtree);
3551          assert(SCIPexprtreeGetVars(nlrow->exprtree) != NULL || n == 0);
3552 
3553          for( i = 0; i < n; ++i )
3554             assert(SCIPhashmapExists(nlp->varhash, SCIPexprtreeGetVars(nlrow->exprtree)[i]));
3555       }
3556 #endif
3557 
3558       /* add row to NLP and capture it */
3559       nlp->nlrows[nlp->nnlrows + j] = nlrow;
3560       nlrow->nlpindex = nlp->nnlrows + j;
3561 
3562       SCIPnlrowCapture(nlrow);
3563 
3564       /* if we have a feasible NLP solution and it satisfies the new solution, then it is still feasible
3565        * if the NLP was globally or locally infeasible, then it stays that way
3566        * if the NLP was unbounded, then this may not be the case anymore
3567        */
3568       if( nlp->solstat <= SCIP_NLPSOLSTAT_FEASIBLE )
3569       {
3570          SCIP_Real feasibility;
3571          SCIP_CALL( SCIPnlrowGetNLPFeasibility(nlrow, set, stat, nlp, &feasibility) );
3572          if( !SCIPsetIsFeasNegative(set, feasibility) )
3573             nlp->solstat = SCIP_NLPSOLSTAT_FEASIBLE;
3574          else
3575             nlp->solstat = SCIP_NLPSOLSTAT_LOCINFEASIBLE;
3576       }
3577       else if( nlp->solstat == SCIP_NLPSOLSTAT_UNBOUNDED )
3578       {
3579          nlp->solstat = SCIP_NLPSOLSTAT_UNKNOWN;
3580       }
3581    }
3582 
3583    nlp->nnlrows += nnlrows;
3584    nlp->nunflushednlrowadd += nnlrows;
3585 
3586    return SCIP_OKAY;
3587 }
3588 
3589 /** moves a nonlinear row to a different place, and updates all corresponding data structures */
3590 static
nlpMoveNlrow(SCIP_NLP * nlp,int oldpos,int newpos)3591 void nlpMoveNlrow(
3592    SCIP_NLP*             nlp,                /**< NLP data structure */
3593    int                   oldpos,             /**< old position of nonlinear row */
3594    int                   newpos              /**< new position of nonlinear row */
3595    )
3596 {
3597    assert(nlp != NULL);
3598    assert(0 <= oldpos && oldpos < nlp->nnlrows);
3599    assert(0 <= newpos && newpos < nlp->nnlrows);
3600    assert(nlp->nlrows[oldpos] != NULL);
3601 
3602    if( oldpos == newpos )
3603       return;
3604 
3605    nlp->nlrows[newpos] = nlp->nlrows[oldpos];
3606    nlp->nlrows[newpos]->nlpindex = newpos;
3607 
3608    /* update nlpi to nlp row index mapping */
3609    if( nlp->nlrows[newpos]->nlpiindex >= 0 )
3610    {
3611       assert(nlp->nlrowmap_nlpi2nlp != NULL);
3612       assert(nlp->nlrows[newpos]->nlpiindex < nlp->sizenlrows_solver);
3613       nlp->nlrowmap_nlpi2nlp[nlp->nlrows[newpos]->nlpiindex] = newpos;
3614    }
3615 }
3616 
3617 /** deletes nonlinear row with given position from NLP */
3618 static
nlpDelNlRowPos(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set,int pos)3619 SCIP_RETCODE nlpDelNlRowPos(
3620    SCIP_NLP*             nlp,                /**< NLP data structure */
3621    BMS_BLKMEM*           blkmem,             /**< block memory */
3622    SCIP_SET*             set,                /**< global SCIP settings */
3623    int                   pos                 /**< position of nonlinear row that is to be removed */
3624    )
3625 {
3626    SCIP_NLROW* nlrow;
3627 
3628    assert(nlp != NULL);
3629    assert(blkmem != NULL);
3630    assert(set != NULL);
3631    assert(pos >= 0);
3632    assert(pos < nlp->nnlrows);
3633    assert(!nlp->indiving);
3634    assert(nlp->nlrows != NULL);
3635 
3636    nlrow = nlp->nlrows[pos];
3637    assert(nlrow != NULL);
3638    assert(nlrow->nlpindex == pos);
3639 
3640    /* if row is in NLPI, then mark that it has to be removed in the next flush
3641     * if row was not in NLPI yet, then we have one unflushed nlrow addition less */
3642    if( nlrow->nlpiindex >= 0 )
3643    {
3644       assert(nlrow->nlpiindex < nlp->nnlrows_solver);
3645       nlp->nlrowmap_nlpi2nlp[nlrow->nlpiindex] = -1;
3646       nlrow->nlpiindex = -1;
3647       ++nlp->nunflushednlrowdel;
3648    }
3649    else
3650    {
3651       assert(nlrow->nlpiindex == -1);
3652       --nlp->nunflushednlrowadd;
3653    }
3654 
3655    /* move NLP row from the end to pos and mark nlrow to be not in NLP anymore */
3656    nlpMoveNlrow(nlp, nlp->nnlrows-1, pos);
3657    nlrow->nlpindex = -1;
3658 
3659    /* forget about restriction */
3660    SCIP_CALL( SCIPnlrowRelease(&nlrow, blkmem, set) );
3661    --nlp->nnlrows;
3662 
3663    if( nlp->solstat < SCIP_NLPSOLSTAT_LOCOPT )
3664       nlp->solstat = SCIP_NLPSOLSTAT_FEASIBLE;
3665    else if( nlp->solstat == SCIP_NLPSOLSTAT_GLOBINFEASIBLE )
3666       nlp->solstat = SCIP_NLPSOLSTAT_LOCINFEASIBLE;
3667 
3668    return SCIP_OKAY; /*lint !e438*/
3669 }
3670 
3671 /** updates bounds on a variable in the NLPI problem */
3672 static
nlpUpdateVarBounds(SCIP_NLP * nlp,SCIP_SET * set,SCIP_VAR * var,SCIP_Bool tightened)3673 SCIP_RETCODE nlpUpdateVarBounds(
3674    SCIP_NLP*             nlp,                /**< NLP data */
3675    SCIP_SET*             set,                /**< global SCIP settings */
3676    SCIP_VAR*             var,                /**< variable which bounds have changed */
3677    SCIP_Bool             tightened           /**< whether the bound change was a bound tightening */
3678    )
3679 {
3680    int pos;
3681    SCIP_Real lb;
3682    SCIP_Real ub;
3683 
3684    assert(nlp != NULL);
3685    assert(var != NULL);
3686    assert(SCIPhashmapExists(nlp->varhash, var));
3687 
3688    /* original variable bounds are ignored during diving
3689     * (all variable bounds are reset to their current value in exitDiving) */
3690    if( nlp->indiving )
3691       return SCIP_OKAY;
3692 
3693    /* get position of variable in NLP */
3694    pos = SCIPhashmapGetImageInt(nlp->varhash, var);
3695 
3696    /* if variable not in NLPI yet, nothing to do */
3697    if( nlp->varmap_nlp2nlpi[pos] == -1 )
3698       return SCIP_OKAY;
3699 
3700    /* update bounds in NLPI problem */
3701    assert(nlp->solver != NULL);
3702    assert(nlp->problem != NULL);
3703 
3704    pos = nlp->varmap_nlp2nlpi[pos];
3705    lb = SCIPvarGetLbLocal(var);
3706    ub = SCIPvarGetUbLocal(var);
3707    SCIP_CALL( SCIPnlpiChgVarBounds(nlp->solver, nlp->problem, 1, &pos, &lb, &ub) );
3708 
3709    /* if we have a feasible NLP solution and it satisfies the new bounds, then it is still feasible
3710     * if the NLP was globally or locally infeasible and we tightened a bound, then it stays that way
3711     * if the NLP was unbounded and we tightened a bound, then this may not be the case anymore
3712     */
3713    if( nlp->solstat <= SCIP_NLPSOLSTAT_FEASIBLE )
3714    {
3715       if( !tightened ||
3716          ((SCIPsetIsInfinity(set, -lb) || SCIPsetIsFeasLE(set, lb, SCIPvarGetNLPSol(var))) &&
3717           (SCIPsetIsInfinity(set,  ub) || SCIPsetIsFeasGE(set, ub, SCIPvarGetNLPSol(var)))) )
3718          nlp->solstat = SCIP_NLPSOLSTAT_FEASIBLE;
3719       else
3720          nlp->solstat = SCIP_NLPSOLSTAT_LOCINFEASIBLE;
3721    }
3722    else if( !tightened || nlp->solstat == SCIP_NLPSOLSTAT_UNBOUNDED )
3723    {
3724       nlp->solstat = SCIP_NLPSOLSTAT_UNKNOWN;
3725    }
3726 
3727    return SCIP_OKAY;
3728 }
3729 
3730 /** updates coefficient of a variable in the objective */
3731 static
nlpUpdateObjCoef(SCIP_NLP * nlp,SCIP_VAR * var)3732 SCIP_RETCODE nlpUpdateObjCoef(
3733    SCIP_NLP*             nlp,                /**< NLP data */
3734    SCIP_VAR*             var                 /**< variable which bounds have changed */
3735    )
3736 {
3737    int pos;
3738    int objidx;
3739    SCIP_Real coef;
3740 
3741    assert(nlp != NULL);
3742    assert(var != NULL);
3743    assert(SCIPhashmapExists(nlp->varhash, var));
3744 
3745    /* if the objective in the NLPI is not up to date, then we do not need to do something here */
3746    if( !nlp->objflushed )
3747       return SCIP_OKAY;
3748 
3749    /* original objective is ignored during diving
3750     * we just need to remember that at end of diving we have to flush the objective */
3751    if( nlp->indiving )
3752    {
3753       nlp->objflushed = FALSE;
3754       return SCIP_OKAY;
3755    }
3756 
3757    /* get position of variable in NLP and objective coefficient */
3758    pos  = SCIPhashmapGetImageInt(nlp->varhash, var);
3759    assert(nlp->varmap_nlp2nlpi[pos] == -1 || nlp->solver != NULL);
3760 
3761    /* actually we only need to remember flushing the objective if we also have an NLPI */
3762    if( nlp->solver == NULL )
3763       return SCIP_OKAY;
3764 
3765    coef = SCIPvarGetObj(var);
3766 
3767    /* if variable not in NLPI yet, then we only need to remember to update the objective after variable additions were flushed */
3768    if( nlp->varmap_nlp2nlpi[pos] == -1 && coef != 0.0 )
3769    {
3770       nlp->objflushed = FALSE;
3771 
3772       return SCIP_OKAY;
3773    }
3774 
3775    /* if we are here, then the objective in the NLPI is up to date,
3776     * we keep it this way by changing the coefficient of var in the NLPI problem objective */
3777    assert(nlp->solver != NULL);
3778    assert(nlp->problem != NULL);
3779 
3780    pos = nlp->varmap_nlp2nlpi[pos];
3781    objidx = -1;
3782    SCIP_CALL( SCIPnlpiChgLinearCoefs(nlp->solver, nlp->problem, objidx, 1, &pos, &coef) );
3783 
3784    /* if we had a solution and it was locally (or globally) optimal, then now we can only be sure that it is still feasible */
3785    if( nlp->solstat < SCIP_NLPSOLSTAT_FEASIBLE )
3786       nlp->solstat = SCIP_NLPSOLSTAT_FEASIBLE;
3787 
3788    return SCIP_OKAY;
3789 }
3790 
3791 /** adds new variables to the NLP */
3792 static
nlpAddVars(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set,int nvars,SCIP_VAR ** vars)3793 SCIP_RETCODE nlpAddVars(
3794    SCIP_NLP*             nlp,                /**< NLP data structure */
3795    BMS_BLKMEM*           blkmem,             /**< block memory */
3796    SCIP_SET*             set,                /**< global SCIP settings */
3797    int                   nvars,              /**< number of variables to add */
3798    SCIP_VAR**            vars                /**< variable to add to NLP */
3799    )
3800 {
3801    int i;
3802    SCIP_VAR* var;
3803 
3804    assert(nlp    != NULL);
3805    assert(blkmem != NULL);
3806    assert(set    != NULL);
3807    assert(vars   != NULL || nvars == 0);
3808    assert(!nlp->indiving || nvars == 0);
3809 
3810    if( nvars == 0 )
3811       return SCIP_OKAY;
3812 
3813    SCIP_CALL( SCIPnlpEnsureVarsSize(nlp, blkmem, set, nlp->nvars + nvars) );
3814    assert(nlp->sizevars >= nlp->nvars + nvars);
3815 
3816    for( i = 0; i < nvars; ++i )
3817    {
3818       var = vars[i];  /*lint !e613*/
3819 
3820       assert(SCIPvarIsTransformed(var));
3821       assert(SCIPvarIsActive(var));
3822       assert(!SCIPhashmapExists(nlp->varhash, var));
3823 
3824       SCIPvarCapture(var);
3825 
3826       nlp->vars[nlp->nvars+i]            = var;
3827       nlp->varmap_nlp2nlpi[nlp->nvars+i] = -1;
3828       SCIP_CALL( SCIPhashmapInsertInt(nlp->varhash, var, nlp->nvars+i) );
3829 
3830       nlp->varlbdualvals[nlp->nvars+i]   = 0.0;
3831       nlp->varubdualvals[nlp->nvars+i]   = 0.0;
3832 
3833       /* update objective, if necessary (new variables have coefficient 0.0 anyway) */
3834       if( SCIPvarGetObj(var) != 0.0 )
3835       {
3836          SCIP_CALL( nlpUpdateObjCoef(nlp, var) );
3837       }
3838 
3839       /* let's keep the previous initial guess and set it for the new variable to the best bound
3840        * (since there can be no row that uses this variable yet, this seems a good guess) */
3841       if( nlp->haveinitguess )
3842       {
3843          assert(nlp->initialguess != NULL);
3844 
3845          nlp->initialguess[nlp->nvars+i] = SCIPvarGetBestBoundLocal(var);
3846       }
3847 
3848       /* if we have a feasible NLP solution, then it remains feasible
3849        * but we have to update the objective function
3850        */
3851       if( nlp->solstat <= SCIP_NLPSOLSTAT_FEASIBLE )
3852       {
3853          SCIP_CALL( SCIPvarSetNLPSol(var, set, SCIPvarGetBestBoundLocal(var)) );
3854          nlp->primalsolobjval += SCIPvarGetObj(var) * SCIPvarGetBestBoundLocal(var);
3855          nlp->solstat = SCIP_NLPSOLSTAT_FEASIBLE;
3856       }
3857 
3858       /* catch events on variable */
3859       SCIP_CALL( SCIPvarCatchEvent(var, blkmem, set, \
3860             SCIP_EVENTTYPE_VARFIXED | SCIP_EVENTTYPE_BOUNDCHANGED | SCIP_EVENTTYPE_OBJCHANGED, \
3861             nlp->eventhdlr, (SCIP_EVENTDATA*)nlp, NULL) ); /* @todo should store event filter position in nlp? */
3862    }
3863 
3864    nlp->nvars += nvars;
3865    nlp->nunflushedvaradd += nvars;
3866 
3867    return SCIP_OKAY;
3868 }
3869 
3870 /** moves a variable to a different place, and updates all corresponding data structures */
3871 static
nlpMoveVar(SCIP_NLP * nlp,int oldpos,int newpos)3872 SCIP_RETCODE nlpMoveVar(
3873    SCIP_NLP*             nlp,                /**< NLP data structure */
3874    int                   oldpos,             /**< old position of variable */
3875    int                   newpos              /**< new position of variable */
3876    )
3877 {
3878    int nlpipos;
3879 
3880    assert(nlp != NULL);
3881    assert(0 <= oldpos && oldpos < nlp->nvars);
3882    assert(0 <= newpos && newpos < nlp->nvars);
3883    assert(nlp->vars[oldpos] != NULL);
3884 
3885    if( oldpos == newpos )
3886       return SCIP_OKAY;
3887 
3888    SCIP_CALL( SCIPhashmapSetImageInt(nlp->varhash, nlp->vars[oldpos], newpos) );
3889    nlp->vars[newpos]            = nlp->vars[oldpos];
3890    nlp->varmap_nlp2nlpi[newpos] = nlp->varmap_nlp2nlpi[oldpos];
3891    nlp->varlbdualvals[newpos]   = nlp->varlbdualvals[oldpos];
3892    nlp->varubdualvals[newpos]   = nlp->varubdualvals[oldpos];
3893    if( nlp->initialguess != NULL )
3894       nlp->initialguess[newpos] = nlp->initialguess[oldpos];
3895 
3896    nlpipos = nlp->varmap_nlp2nlpi[newpos];
3897    if( nlpipos > 0 )
3898       nlp->varmap_nlpi2nlp[nlpipos] = newpos;
3899 
3900    return SCIP_OKAY;
3901 }
3902 
3903 /** deletes variable with given position from NLP */
3904 static
nlpDelVarPos(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_EVENTQUEUE * eventqueue,SCIP_LP * lp,int pos)3905 SCIP_RETCODE nlpDelVarPos(
3906    SCIP_NLP*             nlp,                /**< NLP data structure */
3907    BMS_BLKMEM*           blkmem,             /**< block memory */
3908    SCIP_SET*             set,                /**< global SCIP settings */
3909    SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
3910    SCIP_LP*              lp,                 /**< SCIP LP, needed if a column-variable is freed */
3911    int                   pos                 /**< position of nonlinear row that is to be removed */
3912    )
3913 {
3914    SCIP_VAR* var;
3915 #ifndef NDEBUG
3916    int i;
3917 #endif
3918    int nlpipos;
3919 
3920    assert(nlp    != NULL);
3921    assert(blkmem != NULL);
3922    assert(set    != NULL);
3923    assert(pos >= 0);
3924    assert(pos < nlp->nvars);
3925    assert(!nlp->indiving);
3926 
3927    var = nlp->vars[pos];
3928    assert(var != NULL);
3929 
3930 #ifndef NDEBUG
3931    /* assert that variable is not used by any nonlinear row */
3932    for( i = 0; i < nlp->nnlrows; ++i )
3933    {
3934       int j;
3935       SCIP_NLROW* nlrow;
3936 
3937       nlrow = nlp->nlrows[i];
3938       assert(nlrow != NULL);
3939 
3940       /* use nlrowSearchLinearCoef only if already sorted, since otherwise we may change the solving process slightly */
3941       if( nlrow->linvarssorted )
3942          assert( nlrowSearchLinearCoef(nlrow, var) == -1 );
3943       else
3944          for( j = 0; j < nlrow->nlinvars; ++j )
3945             assert( nlrow->linvars[j] != var );
3946 
3947       assert( SCIPnlrowSearchQuadVar(nlrow, var) == -1);
3948 
3949       assert(nlrow->exprtree == NULL || SCIPexprtreeFindVar(nlrow->exprtree, var) == -1);
3950    }
3951 #endif
3952 
3953    /* if we had a feasible solution, then adjust objective function value
3954     * if NLP was unbounded before, then maybe it is not anymore */
3955    if( nlp->solstat <= SCIP_NLPSOLSTAT_FEASIBLE )
3956       nlp->primalsolobjval -= SCIPvarGetObj(var) * SCIPvarGetNLPSol(var);
3957    else if( nlp->solstat == SCIP_NLPSOLSTAT_UNBOUNDED )
3958       nlp->solstat = SCIP_NLPSOLSTAT_UNKNOWN;
3959 
3960    /* if variable is in NLPI problem, mark that we have to remember to delete it there
3961     * if it was not in the NLPI yet, then we have one unflushed var addition less now */
3962    nlpipos = nlp->varmap_nlp2nlpi[pos];
3963    if( nlpipos >= 0 )
3964    {
3965       assert(nlpipos < nlp->nvars_solver);
3966 
3967       nlp->varmap_nlpi2nlp[nlpipos] = -1;
3968       ++nlp->nunflushedvardel;
3969    }
3970    else
3971       --nlp->nunflushedvaradd;
3972 
3973    /* drop events on variable */
3974    SCIP_CALL( SCIPvarDropEvent(var, blkmem, set, \
3975          SCIP_EVENTTYPE_VARFIXED | SCIP_EVENTTYPE_BOUNDCHANGED | SCIP_EVENTTYPE_OBJCHANGED, \
3976          nlp->eventhdlr, (SCIP_EVENTDATA*)nlp, -1) );
3977 
3978    /* move variable from end to pos */
3979    SCIP_CALL( nlpMoveVar(nlp, nlp->nvars-1, pos) );
3980 
3981    /* forget about variable */
3982    SCIP_CALL( SCIPhashmapRemove(nlp->varhash, var) );
3983    SCIP_CALL( SCIPvarRelease(&var, blkmem, set, eventqueue, lp) );
3984    --nlp->nvars;
3985 
3986    return SCIP_OKAY;
3987 }
3988 
3989 /** notifies NLP that a variable was fixed, so it is removed from objective, all rows, and the NLP variables */
3990 static
nlpRemoveFixedVar(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_EVENTQUEUE * eventqueue,SCIP_LP * lp,SCIP_VAR * var)3991 SCIP_RETCODE nlpRemoveFixedVar(
3992    SCIP_NLP*             nlp,                /**< NLP data */
3993    BMS_BLKMEM*           blkmem,             /**< block memory */
3994    SCIP_SET*             set,                /**< global SCIP settings */
3995    SCIP_STAT*            stat,               /**< problem statistics data */
3996    SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
3997    SCIP_LP*              lp,                 /**< SCIP LP, needed to release variable */
3998    SCIP_VAR*             var                 /**< variable that has been fixed */
3999    )
4000 {
4001    int i;
4002 
4003    assert(nlp != NULL);
4004    assert(var != NULL);
4005    assert(!SCIPvarIsActive(var));
4006    assert(!nlp->indiving);
4007    assert(SCIPhashmapExists(nlp->varhash, var));
4008 
4009    /* remove var from all rows */
4010    for( i = 0; i < nlp->nnlrows; ++i )
4011    {
4012       SCIP_CALL( nlrowRemoveFixedVar(nlp->nlrows[i], blkmem, set, stat, nlp, var) );
4013    }
4014 
4015    /* remove variable from NLP */
4016    SCIP_CALL( SCIPnlpDelVar(nlp, blkmem, set, eventqueue, lp, var) );
4017 
4018    return SCIP_OKAY;
4019 }
4020 
4021 /** creates arrays with NLPI variable indices of variables in a nonlinear row */
4022 static
nlpSetupNlpiIndices(SCIP_NLP * nlp,SCIP_SET * set,SCIP_NLROW * nlrow,int ** linidxs,SCIP_QUADELEM ** quadelems,int ** nlinidxs)4023 SCIP_RETCODE nlpSetupNlpiIndices(
4024    SCIP_NLP*             nlp,                /**< NLP data */
4025    SCIP_SET*             set,                /**< global SCIP settings */
4026    SCIP_NLROW*           nlrow,              /**< nonlinear row */
4027    int**                 linidxs,            /**< buffer to store pointer to NLPI indices of linear variables */
4028    SCIP_QUADELEM**       quadelems,          /**< buffer to store pointer to quadratic elements w.r.t. NLPI indices */
4029    int**                 nlinidxs            /**< buffer to store pointer to NLPI indices of nonlinear variables */
4030    )
4031 {
4032    int i;
4033    SCIP_VAR* var;
4034 
4035    assert(nlp    != NULL);
4036    assert(set    != NULL);
4037    assert(nlrow  != NULL);
4038    assert(linidxs   != NULL);
4039    assert(quadelems != NULL);
4040    assert(nlinidxs  != NULL);
4041 
4042    /* get indices of variables in linear part of row */
4043    if( nlrow->nlinvars > 0 )
4044    {
4045       assert(nlrow->linvars  != NULL);
4046       assert(nlrow->lincoefs != NULL);
4047 
4048       SCIP_CALL( SCIPsetAllocBufferArray(set, linidxs, nlrow->nlinvars) );
4049 
4050       for( i = 0; i < nlrow->nlinvars; ++i )
4051       {
4052          var = nlrow->linvars[i];
4053          assert(var != NULL);
4054          assert(SCIPvarIsActive(var)); /* at this point, there should be only active variables in the row */
4055 
4056          assert(SCIPhashmapExists(nlp->varhash, var));
4057          (*linidxs)[i] = nlp->varmap_nlp2nlpi[SCIPhashmapGetImageInt(nlp->varhash, var)];
4058          assert((*linidxs)[i] >= 0);
4059       }
4060    }
4061    else
4062       *linidxs = NULL;
4063 
4064    /* get indices of variables in quadratic part of row */
4065    if( nlrow->nquadvars > 0 )
4066    {
4067       int* quadvarsidx;
4068 
4069       assert(nlrow->quadvars    != NULL);
4070       assert(nlrow->nquadelems  > 0);
4071       assert(nlrow->quadelems   != NULL);
4072 
4073       /* allocate memory */
4074       SCIP_CALL( SCIPsetAllocBufferArray(set, quadelems, nlrow->nquadelems) );
4075       SCIP_CALL( SCIPsetAllocBufferArray(set, &quadvarsidx, nlrow->nquadvars) );
4076 
4077       /* compute mapping of variable indices quadratic term -> NLPI */
4078       for( i = 0; i < nlrow->nquadvars; ++i )
4079       {
4080          var = nlrow->quadvars[i];
4081          assert(var != NULL);
4082          assert(SCIPvarIsActive(var)); /* at this point, there should be only active variables in the row */
4083 
4084          assert(SCIPhashmapExists(nlp->varhash, var));
4085          quadvarsidx[i] = nlp->varmap_nlp2nlpi[SCIPhashmapGetImageInt(nlp->varhash, var)];
4086       }
4087 
4088       /* compute quad elements using NLPI indices */
4089       for( i = 0; i < nlrow->nquadelems; ++i )
4090       {
4091          assert(nlrow->quadelems[i].idx1 >= 0);
4092          assert(nlrow->quadelems[i].idx1 < nlrow->nquadvars);
4093          assert(nlrow->quadelems[i].idx2 >= 0);
4094          assert(nlrow->quadelems[i].idx2 < nlrow->nquadvars);
4095 
4096          (*quadelems)[i].idx1 = quadvarsidx[nlrow->quadelems[i].idx1];
4097          (*quadelems)[i].idx2 = quadvarsidx[nlrow->quadelems[i].idx2];
4098          if( (*quadelems)[i].idx1 > (*quadelems)[i].idx2 )
4099          {
4100             int tmp = (*quadelems)[i].idx1;
4101             (*quadelems)[i].idx1 = (*quadelems)[i].idx2;
4102             (*quadelems)[i].idx2 = tmp;
4103          }
4104          (*quadelems)[i].coef = nlrow->quadelems[i].coef;
4105       }
4106 
4107       SCIPsetFreeBufferArray(set, &quadvarsidx);
4108    }
4109    else
4110       *quadelems = NULL;
4111 
4112    /* get indices of variables in expression tree part of row */
4113    if( nlrow->exprtree != NULL )
4114    {
4115       int n;
4116 
4117       n = SCIPexprtreeGetNVars(nlrow->exprtree);
4118       assert(n == 0 || SCIPexprtreeGetVars(nlrow->exprtree) != NULL);
4119 
4120       SCIP_CALL( SCIPsetAllocBufferArray(set, nlinidxs, n) );
4121 
4122       for( i = 0; i < n; ++i )
4123       {
4124          var = SCIPexprtreeGetVars(nlrow->exprtree)[i];
4125          assert(var != NULL);
4126          assert(SCIPvarIsActive(var)); /* at this point, there should be only active variables in the row */
4127 
4128          assert(SCIPhashmapExists(nlp->varhash, var));
4129          (*nlinidxs)[i] = nlp->varmap_nlp2nlpi[SCIPhashmapGetImageInt(nlp->varhash, var)];
4130       }
4131    }
4132    else
4133       *nlinidxs = NULL;
4134 
4135    return SCIP_OKAY;
4136 }
4137 
4138 /** ensures, that NLPI variables array of NLP can store at least num entries */
4139 static
nlpEnsureVarsSolverSize(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set,int num)4140 SCIP_RETCODE nlpEnsureVarsSolverSize(
4141    SCIP_NLP*             nlp,                /**< NLP data */
4142    BMS_BLKMEM*           blkmem,             /**< block memory */
4143    SCIP_SET*             set,                /**< global SCIP settings */
4144    int                   num                 /**< minimum number of entries to store */
4145    )
4146 {
4147    assert(nlp    != NULL);
4148    assert(blkmem != NULL);
4149    assert(set    != NULL);
4150    assert(nlp->nvars_solver <= nlp->sizevars_solver);
4151 
4152    if( num > nlp->sizevars_solver )
4153    {
4154       int newsize;
4155 
4156       newsize = SCIPsetCalcMemGrowSize(set, num);
4157       SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &nlp->varmap_nlpi2nlp, nlp->sizevars_solver, newsize) );
4158 
4159       nlp->sizevars_solver = newsize;
4160    }
4161    assert(num <= nlp->sizevars_solver);
4162 
4163    return SCIP_OKAY;
4164 }
4165 
4166 /** ensures, that NLPI nonlinear rows array of NLP can store at least num entries */
4167 static
nlpEnsureNlRowsSolverSize(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set,int num)4168 SCIP_RETCODE nlpEnsureNlRowsSolverSize(
4169    SCIP_NLP*             nlp,                /**< NLP data */
4170    BMS_BLKMEM*           blkmem,             /**< block memory */
4171    SCIP_SET*             set,                /**< global SCIP settings */
4172    int                   num                 /**< minimum number of entries to store */
4173    )
4174 {
4175    assert(nlp    != NULL);
4176    assert(blkmem != NULL);
4177    assert(set    != NULL);
4178    assert(nlp->nnlrows_solver <= nlp->sizenlrows_solver);
4179 
4180    if( num > nlp->sizenlrows_solver )
4181    {
4182       int newsize;
4183 
4184       newsize = SCIPsetCalcMemGrowSize(set, num);
4185       SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &nlp->nlrowmap_nlpi2nlp, nlp->sizenlrows_solver, newsize) );
4186 
4187       nlp->sizenlrows_solver = newsize;
4188    }
4189    assert(num <= nlp->sizenlrows_solver);
4190 
4191    return SCIP_OKAY;
4192 }
4193 
4194 /** deletes rows from the NLPI problem that have been marked as to remove */
4195 static
nlpFlushNlRowDeletions(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set)4196 SCIP_RETCODE nlpFlushNlRowDeletions(
4197    SCIP_NLP*             nlp,                /**< NLP data */
4198    BMS_BLKMEM*           blkmem,             /**< block memory */
4199    SCIP_SET*             set                 /**< global SCIP settings */
4200    )
4201 {
4202    int         j;
4203    int         c;      /* counts the number of rows to delete */
4204    int*        rowset; /* marks which rows to delete and stores new indices */
4205    SCIP_NLROW* nlrow;
4206 
4207    assert(nlp    != NULL);
4208    assert(blkmem != NULL);
4209    assert(set    != NULL);
4210    assert(nlp->nunflushednlrowdel >= 0);
4211    assert(!nlp->indiving);
4212 
4213    if( nlp->nunflushednlrowdel == 0 )
4214    {
4215 #ifndef NDEBUG
4216       /* check that there are really no pending removals of nonlinear rows */
4217       for( j = 0; j < nlp->nnlrows_solver; ++j )
4218          assert(nlp->nlrowmap_nlpi2nlp[j] >= 0);
4219 #endif
4220       return SCIP_OKAY;
4221    }
4222 
4223    assert(nlp->solver != NULL);
4224    assert(nlp->problem != NULL);
4225 
4226    /* create marker which rows have to be deleted */
4227    SCIP_CALL( SCIPsetAllocBufferArray(set, &rowset, nlp->nnlrows_solver) );
4228    c = 0;
4229    for( j = 0; j < nlp->nnlrows_solver; ++j )
4230    {
4231       if( nlp->nlrowmap_nlpi2nlp[j] == -1 )
4232       {
4233          rowset[j] = 1;
4234          ++c;
4235       }
4236       else
4237          rowset[j] = 0;
4238    }
4239    assert(c == nlp->nunflushednlrowdel);
4240 
4241    /* remove rows from NLPI problem */
4242    SCIP_CALL( SCIPnlpiDelConsSet(nlp->solver, nlp->problem, rowset, nlp->nnlrows_solver) );
4243 
4244    /* update NLPI row indices */
4245    for( j = 0; j < nlp->nnlrows_solver; ++j )
4246    {
4247       assert(rowset[j] <= j); /* we assume that the NLP solver did not move a row behind its previous position!! */
4248       if( rowset[j] < 0 )
4249       {
4250          /* assert that row was marked as deleted */
4251          assert(nlp->nlrowmap_nlpi2nlp[j] == -1);
4252       }
4253       else if( rowset[j] < j )
4254       {
4255          /* nlrow at position j moved (forward) to position rowset[j] */
4256          assert(nlp->nlrowmap_nlpi2nlp[j] >= 0);
4257          assert(nlp->nlrowmap_nlpi2nlp[j] < nlp->nnlrows);
4258 
4259          nlrow = nlp->nlrows[nlp->nlrowmap_nlpi2nlp[j]];
4260          assert(nlrow->nlpiindex == j);
4261 
4262          /* there should be no row at the new position already */
4263          assert(nlp->nlrowmap_nlpi2nlp[rowset[j]] == -1);
4264 
4265          nlrow->nlpiindex = rowset[j];
4266          nlp->nlrowmap_nlpi2nlp[rowset[j]] = nlrow->nlpindex;
4267       }
4268       else
4269       {
4270          /* row j stays at position j */
4271          assert(nlp->nlrowmap_nlpi2nlp[j] >= 0);
4272          assert(nlp->nlrowmap_nlpi2nlp[j] < nlp->nnlrows);
4273          assert(nlp->nlrows[nlp->nlrowmap_nlpi2nlp[j]]->nlpiindex == j);
4274       }
4275    }
4276    nlp->nnlrows_solver -= c;
4277    nlp->nunflushednlrowdel = 0;
4278 
4279    /* cleanup */
4280    SCIPsetFreeBufferArray(set, &rowset);
4281 
4282    return SCIP_OKAY;
4283 }
4284 
4285 /** deletes variables from the NLPI problem that have been marked as to remove
4286  * assumes that there are no pending row deletions (nlpFlushNlRowDeletions should be called first)
4287  */
4288 static
nlpFlushVarDeletions(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set)4289 SCIP_RETCODE nlpFlushVarDeletions(
4290    SCIP_NLP*             nlp,                /**< NLP data */
4291    BMS_BLKMEM*           blkmem,             /**< block memory */
4292    SCIP_SET*             set                 /**< global SCIP settings */
4293    )
4294 {
4295    int  i;
4296    int  c;      /* counter on number of variables to remove in solver */
4297    int* colset; /* marks which variables to delete and stores new indices */
4298 
4299    assert(nlp    != NULL);
4300    assert(blkmem != NULL);
4301    assert(set    != NULL);
4302    assert(nlp->nunflushedvardel >= 0);
4303    assert(nlp->nunflushednlrowdel == 0);
4304    assert(!nlp->indiving);
4305 
4306    if( nlp->nunflushedvardel == 0 )
4307    {
4308 #ifndef NDEBUG
4309       /* check that there are really no pending removals of variables */
4310       for( i = 0; i < nlp->nvars_solver; ++i )
4311          assert(nlp->varmap_nlpi2nlp[i] >= 0);
4312 #endif
4313       return SCIP_OKAY;
4314    }
4315 
4316    assert(nlp->solver != NULL);
4317    assert(nlp->problem != NULL);
4318 
4319    /* create marker which variables have to be deleted */
4320    SCIP_CALL( SCIPsetAllocBufferArray(set, &colset, nlp->nvars_solver) );
4321    c = 0;
4322    for( i = 0; i < nlp->nvars_solver; ++i )
4323    {
4324       if( nlp->varmap_nlpi2nlp[i] == -1 )
4325       {
4326          colset[i] = 1;
4327          ++c;
4328       }
4329       else
4330          colset[i] = 0;
4331    }
4332    assert(c == nlp->nunflushedvardel);
4333 
4334    /* delete variables from NLPI problem */
4335    SCIP_CALL( SCIPnlpiDelVarSet(nlp->solver, nlp->problem, colset, nlp->nvars_solver) );
4336 
4337    /* update NLPI variable indices */
4338    for( i = 0; i < nlp->nvars_solver; ++i )
4339    {
4340       assert(colset[i] <= i); /* we assume that the NLP solver did not move a variable behind its previous position!! */
4341       if( colset[i] < 0 )
4342       {
4343          /* assert that variable was marked as deleted */
4344          assert(nlp->varmap_nlpi2nlp[i] == -1);
4345       }
4346       else if( colset[i] < i)
4347       {
4348          /* variable at position i moved (forward) to position colset[i] */
4349          int varpos;
4350 
4351          varpos = nlp->varmap_nlpi2nlp[i]; /* position of variable i in NLP */
4352          assert(varpos >= 0);
4353          assert(varpos < nlp->nvars);
4354          assert(nlp->varmap_nlp2nlpi[varpos] == i);
4355 
4356          /* there should be no variable at the new position already */
4357          assert(nlp->varmap_nlpi2nlp[colset[i]] == -1);
4358 
4359          nlp->varmap_nlp2nlpi[varpos] = colset[i];
4360          nlp->varmap_nlpi2nlp[colset[i]] = varpos;
4361       }
4362       else
4363       {
4364          /* variable i stays at position i */
4365          assert(nlp->varmap_nlpi2nlp[i] >= 0);
4366          assert(nlp->varmap_nlpi2nlp[i] < nlp->nvars);
4367          assert(nlp->varmap_nlp2nlpi[nlp->varmap_nlpi2nlp[i]] == i);
4368       }
4369    }
4370 
4371    nlp->nvars_solver -= c;
4372    nlp->nunflushedvardel = 0;
4373 
4374    /* cleanup */
4375    SCIPsetFreeBufferArray(set, &colset);
4376 
4377    return SCIP_OKAY;
4378 }
4379 
4380 /** adds nonlinear rows to NLPI problem that have been added to NLP before
4381  * assumes that there are no pending variable additions or deletions (nlpFlushVarDeletions and nlpFlushVarAdditions should be called first) */
4382 static
nlpFlushNlRowAdditions(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set)4383 SCIP_RETCODE nlpFlushNlRowAdditions(
4384    SCIP_NLP*             nlp,                /**< NLP data */
4385    BMS_BLKMEM*           blkmem,             /**< block memory */
4386    SCIP_SET*             set                 /**< global SCIP settings */
4387    )
4388 {
4389    int c, i;
4390    SCIP_NLROW* nlrow;
4391    SCIP_Real*  lhss;
4392    SCIP_Real*  rhss;
4393    int*        nlinvars;
4394    int**       linidxs;
4395    SCIP_Real** lincoefs;
4396    int*        nquadelems;
4397    SCIP_QUADELEM** quadelems;
4398    int**       nlidxs;
4399    SCIP_EXPRTREE** exprtrees;
4400    const char** names;
4401 
4402    assert(nlp    != NULL);
4403    assert(blkmem != NULL);
4404    assert(set    != NULL);
4405    assert(nlp->nunflushednlrowadd >= 0);
4406    assert(nlp->nunflushedvaradd == 0);
4407    assert(nlp->nunflushedvardel == 0);
4408    assert(!nlp->indiving);
4409 
4410    if( nlp->nunflushednlrowadd == 0 )
4411    {
4412 #ifndef NDEBUG
4413       /* check that there are really no pending additions of variables */
4414       for( i = 0; i < nlp->nnlrows; ++i )
4415          assert(nlp->nlrows[i]->nlpiindex >= 0);
4416 #endif
4417       return SCIP_OKAY;
4418    }
4419 
4420    assert(nlp->solver != NULL);
4421    assert(nlp->problem != NULL);
4422 
4423    SCIP_CALL( nlpEnsureNlRowsSolverSize(nlp, blkmem, set, nlp->nnlrows_solver + nlp->nunflushednlrowadd) );
4424 
4425    SCIP_CALL( SCIPsetAllocBufferArray(set, &lhss,        nlp->nunflushednlrowadd) );
4426    SCIP_CALL( SCIPsetAllocBufferArray(set, &rhss,        nlp->nunflushednlrowadd) );
4427    SCIP_CALL( SCIPsetAllocBufferArray(set, &nlinvars,    nlp->nunflushednlrowadd) );
4428    SCIP_CALL( SCIPsetAllocBufferArray(set, &linidxs,     nlp->nunflushednlrowadd) );
4429    SCIP_CALL( SCIPsetAllocBufferArray(set, &lincoefs,    nlp->nunflushednlrowadd) );
4430    SCIP_CALL( SCIPsetAllocBufferArray(set, &nquadelems,  nlp->nunflushednlrowadd) );
4431    SCIP_CALL( SCIPsetAllocBufferArray(set, &quadelems,   nlp->nunflushednlrowadd) );
4432    SCIP_CALL( SCIPsetAllocBufferArray(set, &nlidxs,      nlp->nunflushednlrowadd) );
4433    SCIP_CALL( SCIPsetAllocBufferArray(set, &exprtrees,   nlp->nunflushednlrowadd) );
4434 #if ADDNAMESTONLPI
4435    SCIP_CALL( SCIPsetAllocBufferArray(set, &names,       nlp->nunflushednlrowadd) );
4436 #else
4437    names = NULL;
4438 #endif
4439 
4440    c = 0;
4441    for( i = 0; i < nlp->nnlrows; ++i )
4442    {
4443       nlrow = nlp->nlrows[i];
4444       assert(nlrow != NULL);
4445 
4446       /* skip nonlinear rows already in NLPI problem */
4447       if( nlrow->nlpiindex >= 0 )
4448          continue;
4449       assert(c < nlp->nunflushednlrowadd);
4450 
4451       /* get indices in NLPI */
4452       SCIP_CALL( nlpSetupNlpiIndices(nlp, set, nlrow, &linidxs[c], &quadelems[c], &nlidxs[c]) );
4453       assert(linidxs[c]   != NULL || nlrow->nlinvars  == 0);
4454       assert(quadelems[c] != NULL || nlrow->nquadvars == 0);
4455       assert(nlidxs[c]    != NULL || nlrow->exprtree  == NULL);
4456 
4457       nlp->nlrowmap_nlpi2nlp[nlp->nnlrows_solver+c] = i;
4458       nlrow->nlpiindex = nlp->nnlrows_solver+c;
4459 
4460       lhss[c] = nlrow->lhs;
4461       rhss[c] = nlrow->rhs;
4462       if( nlrow->constant != 0.0 )
4463       {
4464          if( !SCIPsetIsInfinity(set, -nlrow->lhs) )
4465             lhss[c] -= nlrow->constant;
4466          if( !SCIPsetIsInfinity(set,  nlrow->rhs) )
4467             rhss[c] -= nlrow->constant;
4468       }
4469       if( rhss[c] < lhss[c] )
4470       {
4471          assert(SCIPsetIsEQ(set, lhss[c], rhss[c]));
4472          rhss[c] = lhss[c];
4473       }
4474 
4475       nlinvars[c] = nlrow->nlinvars;
4476       lincoefs[c] = nlrow->lincoefs;
4477 
4478       nquadelems[c] = nlrow->nquadelems;
4479 
4480       exprtrees[c]  = nlrow->exprtree;
4481 
4482 #if ADDNAMESTONLPI
4483       names[c]      = nlrow->name;
4484 #endif
4485 
4486       ++c;
4487 
4488 #ifdef NDEBUG
4489       /* have c vars to add already, there can be no more */
4490       if( c == nlp->nunflushednlrowadd )
4491          break;
4492 #endif
4493    }
4494    assert(c == nlp->nunflushednlrowadd);
4495 
4496    nlp->nnlrows_solver += c;
4497 
4498    SCIP_CALL( SCIPnlpiAddConstraints(nlp->solver, nlp->problem, c, lhss, rhss,
4499          nlinvars, linidxs, lincoefs,
4500          nquadelems, quadelems,
4501          nlidxs, exprtrees,
4502          names) );
4503 
4504    for( c = nlp->nunflushednlrowadd - 1; c >= 0 ; --c )
4505    {
4506       if( nlidxs[c] != NULL )
4507          SCIPsetFreeBufferArray(set, &nlidxs[c]);
4508       if( quadelems[c] != NULL )
4509          SCIPsetFreeBufferArray(set, &quadelems[c]);
4510       if( linidxs[c] != NULL )
4511          SCIPsetFreeBufferArray(set, &linidxs[c]);
4512    }
4513 
4514 #if ADDNAMESTONLPI
4515    SCIPsetFreeBufferArray(set, &names);
4516 #endif
4517    SCIPsetFreeBufferArray(set, &exprtrees);
4518    SCIPsetFreeBufferArray(set, &nlidxs);
4519    SCIPsetFreeBufferArray(set, &quadelems);
4520    SCIPsetFreeBufferArray(set, &nquadelems);
4521    SCIPsetFreeBufferArray(set, &lincoefs);
4522    SCIPsetFreeBufferArray(set, &linidxs);
4523    SCIPsetFreeBufferArray(set, &nlinvars);
4524    SCIPsetFreeBufferArray(set, &rhss);
4525    SCIPsetFreeBufferArray(set, &lhss);
4526 
4527    nlp->nunflushednlrowadd = 0;
4528 
4529    return SCIP_OKAY;
4530 }
4531 
4532 
4533 /** adds variables to NLPI problem that have been added to NLP before
4534  * may set nlp->objflushed to FALSE if a variable with nonzero obj.coefficient is added to the NLPI problem */
4535 static
nlpFlushVarAdditions(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set)4536 SCIP_RETCODE nlpFlushVarAdditions(
4537    SCIP_NLP*             nlp,                /**< NLP data */
4538    BMS_BLKMEM*           blkmem,             /**< block memory */
4539    SCIP_SET*             set                 /**< global SCIP settings */
4540    )
4541 {
4542    int i, c;
4543    SCIP_Real*  lbs;
4544    SCIP_Real*  ubs;
4545    const char** names;
4546 
4547    assert(nlp    != NULL);
4548    assert(blkmem != NULL);
4549    assert(set    != NULL);
4550    assert(nlp->nunflushedvaradd >= 0);
4551    assert(!nlp->indiving);
4552 
4553    if( nlp->nunflushedvaradd == 0 )
4554    {
4555 #ifndef NDEBUG
4556       /* check that there are really no pending additions of variables */
4557       for( i = 0; i < nlp->nvars; ++i )
4558          assert(nlp->varmap_nlp2nlpi[i] >= 0);
4559 #endif
4560       return SCIP_OKAY;
4561    }
4562 
4563    assert(nlp->solver != NULL);
4564    assert(nlp->problem != NULL);
4565 
4566    SCIP_CALL( nlpEnsureVarsSolverSize(nlp, blkmem, set, nlp->nvars_solver + nlp->nunflushedvaradd) );
4567 
4568    SCIP_CALL( SCIPsetAllocBufferArray(set, &lbs,   nlp->nunflushedvaradd) );
4569    SCIP_CALL( SCIPsetAllocBufferArray(set, &ubs,   nlp->nunflushedvaradd) );
4570 #if ADDNAMESTONLPI
4571    SCIP_CALL( SCIPsetAllocBufferArray(set, &names, nlp->nunflushedvaradd) );
4572 #else
4573    names = NULL;
4574 #endif
4575 
4576    c = 0;
4577    for( i = 0; i < nlp->nvars; ++i )
4578    {
4579       /* skip variables already in NLPI problem */
4580       if( nlp->varmap_nlp2nlpi[i] >= 0 )
4581          continue;
4582       assert(c < nlp->nunflushedvaradd);
4583 
4584       nlp->varmap_nlpi2nlp[nlp->nvars_solver+c] = i;
4585       nlp->varmap_nlp2nlpi[i] = nlp->nvars_solver+c;
4586       lbs[c]   = SCIPvarGetLbLocal(nlp->vars[i]);
4587       ubs[c]   = SCIPvarGetUbLocal(nlp->vars[i]);
4588 #if ADDNAMESTONLPI
4589       names[c] = SCIPvarGetName(nlp->vars[i]);
4590 #endif
4591       ++c;
4592 
4593       /* if the new variable has a nonzero objective coefficient, then the objective need to be updated */
4594       if( !SCIPsetIsZero(set, SCIPvarGetObj(nlp->vars[i])) )
4595          nlp->objflushed = FALSE;
4596 
4597 #ifdef NDEBUG
4598       /* have c vars to add already, there can be no more */
4599       if( c == nlp->nunflushedvaradd )
4600          break;
4601 #endif
4602    }
4603    assert(c == nlp->nunflushedvaradd);
4604 
4605    nlp->nvars_solver += c;
4606 
4607    SCIP_CALL( SCIPnlpiAddVars(nlp->solver, nlp->problem, c, lbs, ubs, names) );
4608 
4609 #if ADDNAMESTONLPI
4610    SCIPsetFreeBufferArray(set, &names);
4611 #endif
4612    SCIPsetFreeBufferArray(set, &ubs);
4613    SCIPsetFreeBufferArray(set, &lbs);
4614 
4615    nlp->nunflushedvaradd = 0;
4616 
4617    return SCIP_OKAY;
4618 }
4619 
4620 /** updates the objective in the NLPI problem, if necessary
4621  * assumes that there are no unflushed variable additions or deletions (nlpFlushVarDeletions and nlpFlushVarAdditions should be called first)
4622  */
4623 static
nlpFlushObjective(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set)4624 SCIP_RETCODE nlpFlushObjective(
4625    SCIP_NLP*             nlp,                /**< NLP data */
4626    BMS_BLKMEM*           blkmem,             /**< block memory */
4627    SCIP_SET*             set                 /**< global SCIP settings */
4628    )
4629 {
4630    int*       linindices;
4631    SCIP_Real* lincoefs;
4632    SCIP_Real  coef;
4633    int        i;
4634    int        nz;
4635 
4636    assert(nlp    != NULL);
4637    assert(blkmem != NULL);
4638    assert(set    != NULL);
4639    assert(nlp->nunflushedvaradd == 0);
4640    assert(nlp->nunflushedvardel == 0);
4641    assert(!nlp->indiving);
4642 
4643    if( nlp->objflushed )
4644       return SCIP_OKAY;
4645 
4646    assert(nlp->solver != NULL);
4647    assert(nlp->problem != NULL);
4648 
4649    /* assemble coefficients */
4650    SCIP_CALL( SCIPsetAllocBufferArray(set, &linindices, nlp->nvars_solver) );
4651    SCIP_CALL( SCIPsetAllocBufferArray(set, &lincoefs,   nlp->nvars_solver) );
4652 
4653    nz = 0;
4654    for( i = 0; i < nlp->nvars_solver; ++i )
4655    {
4656       assert(nlp->varmap_nlpi2nlp[i] >= 0); /* there should be no variable deletions pending */
4657 
4658       coef = SCIPvarGetObj(nlp->vars[nlp->varmap_nlpi2nlp[i]]);
4659       if( SCIPsetIsZero(set, coef) )
4660          continue;
4661 
4662       linindices[nz] = i;
4663       lincoefs[nz]   = coef;
4664       ++nz;
4665    }
4666 
4667    SCIP_CALL( SCIPnlpiSetObjective(nlp->solver, nlp->problem,
4668          nz, linindices, lincoefs,
4669          0, NULL,
4670          NULL, NULL,
4671          0.0) );
4672 
4673    SCIPsetFreeBufferArray(set, &lincoefs);
4674    SCIPsetFreeBufferArray(set, &linindices);
4675 
4676    nlp->objflushed = TRUE;
4677 
4678    return SCIP_OKAY;
4679 }
4680 
4681 /** solves the NLP, assuming it has been flushed already
4682  *
4683  *  is used also to solve diving NLP
4684  */
4685 static
nlpSolve(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_MESSAGEHDLR * messagehdlr,SCIP_STAT * stat)4686 SCIP_RETCODE nlpSolve(
4687    SCIP_NLP*             nlp,                /**< NLP data */
4688    BMS_BLKMEM*           blkmem,             /**< block memory buffers */
4689    SCIP_SET*             set,                /**< global SCIP settings */
4690    SCIP_MESSAGEHDLR*     messagehdlr,        /**< message handler */
4691    SCIP_STAT*            stat                /**< problem statistics */
4692    )
4693 {
4694    SCIP_Real sciptimelimit;
4695    SCIP_Real timeleft;
4696    int i;
4697 
4698    assert(nlp    != NULL);
4699    assert(blkmem != NULL);
4700    assert(set    != NULL);
4701    assert(stat   != NULL);
4702 
4703    if( nlp->solver == NULL )
4704    {
4705       SCIPmessagePrintWarning(messagehdlr, "Attempted to solve NLP, but no solver available.\n");
4706 
4707       nlp->solstat  = SCIP_NLPSOLSTAT_UNKNOWN;
4708       nlp->termstat = SCIP_NLPTERMSTAT_OTHER;
4709 
4710       return SCIP_OKAY;
4711    }
4712 
4713    assert(nlp->solver != NULL);
4714    assert(nlp->problem != NULL);
4715 
4716    /* set initial guess, if available */
4717    if( nlp->haveinitguess )
4718    {
4719       /* @todo should we not set it if we had set it already? (initguessflushed...) */
4720       SCIP_Real* initialguess_solver;
4721       int nlpidx;
4722 
4723       assert(nlp->initialguess != NULL);
4724 
4725       SCIP_CALL( SCIPsetAllocBufferArray(set, &initialguess_solver, nlp->nvars_solver) );
4726 
4727       for( i = 0; i < nlp->nvars_solver; ++i )
4728       {
4729          nlpidx = nlp->varmap_nlpi2nlp[i];
4730          assert(nlpidx >= 0);
4731          assert(nlpidx < nlp->nvars);
4732 
4733          initialguess_solver[i] = nlp->initialguess[nlpidx];
4734       }
4735       SCIP_CALL( SCIPnlpiSetInitialGuess(nlp->solver, nlp->problem, initialguess_solver, NULL, NULL, NULL) );
4736 
4737       SCIPsetFreeBufferArray(set, &initialguess_solver);
4738    }
4739 
4740    /* set NLP tolerances to current SCIP primal and dual feasibility tolerance */
4741    SCIP_CALL( SCIPnlpiSetRealPar(nlp->solver, nlp->problem, SCIP_NLPPAR_FEASTOL, SCIPsetFeastol(set)) );
4742    SCIP_CALL( SCIPnlpiSetRealPar(nlp->solver, nlp->problem, SCIP_NLPPAR_RELOBJTOL, SCIPsetDualfeastol(set)) );
4743 
4744    /* set the NLP timelimit to the remaining time */
4745    SCIP_CALL( SCIPsetGetRealParam(set, "limits/time", &sciptimelimit) );
4746    timeleft = sciptimelimit - SCIPclockGetTime(stat->solvingtime);
4747    SCIP_CALL( SCIPnlpiSetRealPar(nlp->solver, nlp->problem, SCIP_NLPPAR_TILIM, MAX(0.0, timeleft)) );
4748 
4749    /* let NLP solver do his work */
4750    SCIPclockStart(stat->nlpsoltime, set);
4751 
4752    SCIP_CALL( SCIPnlpiSolve(nlp->solver, nlp->problem) );
4753 
4754    SCIPclockStop(stat->nlpsoltime, set);
4755    ++stat->nnlps;
4756 
4757    nlp->termstat = SCIPnlpiGetTermstat(nlp->solver, nlp->problem);
4758    nlp->solstat  = SCIPnlpiGetSolstat(nlp->solver, nlp->problem);
4759    switch( nlp->solstat )
4760    {
4761    case SCIP_NLPSOLSTAT_GLOBOPT:
4762    case SCIP_NLPSOLSTAT_LOCOPT:
4763    case SCIP_NLPSOLSTAT_FEASIBLE:
4764    case SCIP_NLPSOLSTAT_LOCINFEASIBLE:
4765    {
4766       SCIP_Real* primalvals;
4767       SCIP_Real* nlrowdualvals;
4768       SCIP_Real* varlbdualvals;
4769       SCIP_Real* varubdualvals;
4770 
4771       primalvals    = NULL;
4772       nlrowdualvals = NULL;
4773       varlbdualvals = NULL;
4774       varubdualvals = NULL;
4775 
4776       /* get NLP solution */
4777       SCIP_CALL( SCIPnlpiGetSolution(nlp->solver, nlp->problem, &primalvals, &nlrowdualvals, &varlbdualvals, &varubdualvals, NULL) );
4778       assert(primalvals != NULL || nlp->nvars == 0);
4779       assert((varlbdualvals != NULL) == (varubdualvals != NULL)); /* if there are duals for one bound, then there should also be duals for the other bound */
4780 
4781       /* store solution primal values in variable and evaluate objective function */
4782       if( nlp->indiving && nlp->divingobj != NULL )
4783       {
4784          for( i = 0; i < nlp->nvars; ++i )
4785          {
4786             SCIP_CALL( SCIPvarSetNLPSol(nlp->vars[i], set, primalvals[nlp->varmap_nlp2nlpi[i]]) );  /*lint !e613 */
4787          }
4788 
4789          /* evaluate modified diving objective */
4790          SCIP_CALL( SCIPnlrowGetNLPActivity(nlp->divingobj, set, stat, nlp, &nlp->primalsolobjval) );
4791       }
4792       else
4793       {
4794          /* evaluate SCIP objective function */
4795          nlp->primalsolobjval = 0.0;
4796          for( i = 0; i < nlp->nvars; ++i )
4797          {
4798             SCIP_Real solval = primalvals[nlp->varmap_nlp2nlpi[i]];  /*lint !e613 */
4799 
4800             /* do a quick assert that variable bounds are satisfied, if feasibility is claimed */
4801             assert(SCIPsetIsInfinity(set, -SCIPvarGetLbLocal(nlp->vars[i])) ||
4802                SCIPsetIsFeasGE(set, solval, SCIPvarGetLbLocal(nlp->vars[i])) || nlp->solstat > SCIP_NLPSOLSTAT_FEASIBLE);
4803             assert(SCIPsetIsInfinity(set, SCIPvarGetUbLocal(nlp->vars[i])) ||
4804                SCIPsetIsFeasLE(set, solval, SCIPvarGetUbLocal(nlp->vars[i])) || nlp->solstat > SCIP_NLPSOLSTAT_FEASIBLE);
4805 
4806             SCIP_CALL( SCIPvarSetNLPSol(nlp->vars[i], set, solval) );  /*lint !e613 */
4807             nlp->primalsolobjval += SCIPvarGetObj(nlp->vars[i]) * solval;  /*lint !e613 */
4808          }
4809       }
4810 
4811       /* store solution dual values in nlrows and variables */
4812       for( i = 0; i < nlp->nnlrows; ++i )
4813       {
4814          assert(nlp->nlrows[i]->nlpiindex >= 0); /* NLP was flushed before solve, so all nlrows should be in there */
4815 
4816          nlp->nlrows[i]->dualsol = nlrowdualvals != NULL ? nlrowdualvals[nlp->nlrows[i]->nlpiindex] : 0.0;
4817 
4818          /* SCIPsetDebugMsg(set, "dual of nlrow <%s> = %g\n", nlp->nlrows[i]->name, nlp->nlrows[i]->dualsol); */
4819       }
4820       assert(nlp->varlbdualvals != NULL || nlp->nvars == 0);
4821       assert(nlp->varubdualvals != NULL || nlp->nvars == 0);
4822       if( varlbdualvals != NULL )
4823       {
4824          for( i = 0; i < nlp->nvars; ++i )
4825          {
4826             assert(nlp->varmap_nlp2nlpi[i] >= 0); /* NLP was flushed before solve, so all vars should be in there */
4827 
4828             nlp->varlbdualvals[i] = varlbdualvals[nlp->varmap_nlp2nlpi[i]];
4829             nlp->varubdualvals[i] = varubdualvals[nlp->varmap_nlp2nlpi[i]];
4830 
4831             /* SCIPsetDebugMsg(set, "duals of var <%s> = %g %g\n", SCIPvarGetName(nlp->vars[i]), nlp->varlbdualvals[i], nlp->varubdualvals[i]); */
4832          }
4833       }
4834       else if( nlp->nvars > 0 )
4835       {
4836          BMSclearMemoryArray(nlp->varlbdualvals, nlp->nvars);
4837          BMSclearMemoryArray(nlp->varubdualvals, nlp->nvars);
4838       }
4839 
4840       break;
4841    }
4842    default:
4843       nlp->primalsolobjval = SCIP_INVALID;
4844       break;
4845    } /*lint !e788*/
4846 
4847    return SCIP_OKAY;
4848 }
4849 
4850 /** assembles list of fractional variables in last NLP solution */
4851 static
nlpCalcFracVars(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat)4852 SCIP_RETCODE nlpCalcFracVars(
4853    SCIP_NLP*             nlp,                /**< NLP data */
4854    BMS_BLKMEM*           blkmem,             /**< block memory buffers */
4855    SCIP_SET*             set,                /**< global SCIP settings */
4856    SCIP_STAT*            stat                /**< problem statistics */
4857    )
4858 {
4859    assert(nlp != NULL);
4860    assert(blkmem != NULL);
4861    assert(set != NULL);
4862    assert(stat != NULL);
4863    assert(nlp->validfracvars <= stat->nnlps);
4864    assert(SCIPnlpHasSolution(nlp));
4865 
4866    SCIPsetDebugMsg(set, "calculating NLP fractional variables: validfracvars=%" SCIP_LONGINT_FORMAT ", nnlps=%" SCIP_LONGINT_FORMAT "\n", nlp->validfracvars, stat->nnlps);
4867 
4868    if( nlp->solstat > SCIP_NLPSOLSTAT_LOCINFEASIBLE )
4869    {
4870       nlp->nfracvars     = 0;
4871       nlp->npriofracvars = 0;
4872       nlp->validfracvars = stat->nnlps;
4873 
4874       SCIPsetDebugMsg(set, "NLP globally infeasible, unbounded, or worse -> no solution values -> no fractional variables\n");
4875       return SCIP_OKAY;
4876    }
4877 
4878    /* check, if the current NLP fractional variables array is invalid */
4879    if( nlp->validfracvars < stat->nnlps )
4880    {
4881       SCIP_VAR* var;
4882       SCIP_Real primsol;
4883       SCIP_Real frac;
4884       int branchpriority;
4885       int insertpos;
4886       int maxpriority;
4887       int i;
4888 
4889       SCIPsetDebugMsg(set, " -> recalculating NLP fractional variables\n");
4890 
4891       if( nlp->fracvarssize == 0 )
4892       {
4893          assert(nlp->fracvars     == NULL);
4894          assert(nlp->fracvarssol  == NULL);
4895          assert(nlp->fracvarsfrac == NULL);
4896          nlp->fracvarssize = 5;
4897          SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &nlp->fracvars,     nlp->fracvarssize) );
4898          SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &nlp->fracvarssol,  nlp->fracvarssize) );
4899          SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &nlp->fracvarsfrac, nlp->fracvarssize) );
4900       }
4901 
4902       maxpriority = INT_MIN;
4903       nlp->nfracvars = 0;
4904       nlp->npriofracvars = 0;
4905       for( i = 0; i < nlp->nvars; ++i )
4906       {
4907          var = nlp->vars[i];
4908          assert(var != NULL);
4909 
4910          primsol = SCIPvarGetNLPSol(var);
4911          assert(primsol < SCIP_INVALID);
4912 
4913          /* consider only binary and integer variables */
4914          if( SCIPvarGetType(var) != SCIP_VARTYPE_BINARY && SCIPvarGetType(var) != SCIP_VARTYPE_INTEGER )
4915             continue;
4916 
4917          /* ignore fixed variables (due to numerics, it is possible, that the NLP solution of a fixed integer variable
4918           * (with large fixed value) is fractional in terms of absolute feasibility measure)
4919           */
4920          if( SCIPvarGetLbLocal(var) >= SCIPvarGetUbLocal(var) - 0.5 )
4921             continue;
4922 
4923          /* check, if the LP solution value is fractional */
4924          frac = SCIPsetFeasFrac(set, primsol);
4925 
4926          /* The fractionality should not be smaller than -feastol, however, if the primsol is large enough
4927           * and close to an integer, fixed precision floating point arithmetic might give us values slightly
4928           * smaller than -feastol. Originally, the "frac >= -feastol"-check was within SCIPsetIsFeasFracIntegral(),
4929           * however, we relaxed it to "frac >= -2*feastol" and have the stricter check here for small-enough primsols.
4930           */
4931          assert(SCIPsetIsGE(set, frac, -SCIPsetFeastol(set)) || (primsol > 1e14 * SCIPsetFeastol(set)));
4932 
4933          if( SCIPsetIsFeasFracIntegral(set, frac) )
4934             continue;
4935 
4936          /* ensure enough space in fracvars arrays */
4937          if( nlp->fracvarssize <= nlp->nfracvars )
4938          {
4939             int newsize;
4940 
4941             newsize = SCIPsetCalcMemGrowSize(set, nlp->nfracvars + 1);
4942             SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &nlp->fracvars,     nlp->fracvarssize, newsize) );
4943             SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &nlp->fracvarssol,  nlp->fracvarssize, newsize) );
4944             SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &nlp->fracvarsfrac, nlp->fracvarssize, newsize) );
4945             nlp->fracvarssize = newsize;
4946          }
4947          assert(nlp->nfracvars < nlp->fracvarssize);
4948          assert(nlp->fracvars     != NULL);
4949          assert(nlp->fracvarssol  != NULL);
4950          assert(nlp->fracvarsfrac != NULL);
4951 
4952          /* insert candidate in candidate list */
4953          branchpriority = SCIPvarGetBranchPriority(var);
4954          insertpos = nlp->nfracvars;
4955          nlp->nfracvars++;
4956          if( branchpriority > maxpriority )
4957          {
4958             /* candidate has higher priority than the current maximum:
4959              * move it to the front and declare it to be the single best candidate
4960              */
4961             if( insertpos != 0 )
4962             {
4963                nlp->fracvars[insertpos]     = nlp->fracvars[0];
4964                nlp->fracvarssol[insertpos]  = nlp->fracvarssol[0];
4965                nlp->fracvarsfrac[insertpos] = nlp->fracvarsfrac[0];
4966                insertpos = 0;
4967             }
4968             nlp->npriofracvars = 1;
4969             maxpriority = branchpriority;
4970          }
4971          else if( branchpriority == maxpriority )
4972          {
4973             /* candidate has equal priority as the current maximum:
4974              * move away the first non-maximal priority candidate, move the current candidate to the correct
4975              * slot (binaries first) and increase the number of maximal priority candidates
4976              */
4977             if( insertpos != nlp->npriofracvars )
4978             {
4979                nlp->fracvars[insertpos]     = nlp->fracvars[nlp->npriofracvars];
4980                nlp->fracvarssol[insertpos]  = nlp->fracvarssol[nlp->npriofracvars];
4981                nlp->fracvarsfrac[insertpos] = nlp->fracvarsfrac[nlp->npriofracvars];
4982                insertpos = nlp->npriofracvars;
4983             }
4984             ++nlp->npriofracvars;
4985          }
4986          nlp->fracvars[insertpos]     = var;
4987          nlp->fracvarssol[insertpos]  = primsol;
4988          nlp->fracvarsfrac[insertpos] = frac;
4989 
4990          SCIPsetDebugMsg(set, " -> candidate %d: var=<%s>, sol=%g, frac=%g, prio=%d (max: %d) -> pos %d\n",
4991             nlp->nfracvars, SCIPvarGetName(var), primsol, frac, branchpriority, maxpriority, insertpos);
4992       }
4993 
4994       nlp->validfracvars = stat->nnlps;
4995    }
4996    assert(0 <= nlp->npriofracvars);
4997    assert(nlp->npriofracvars <= nlp->nfracvars);
4998 
4999    SCIPsetDebugMsg(set, " -> %d fractional variables (%d of maximal priority)\n", nlp->nfracvars, nlp->npriofracvars);
5000 
5001    return SCIP_OKAY;
5002 }
5003 
5004 /** event handling for variable events */
5005 static
SCIP_DECL_EVENTEXEC(eventExecNlp)5006 SCIP_DECL_EVENTEXEC(eventExecNlp)
5007 {
5008    SCIP_EVENTTYPE etype;
5009    SCIP_VAR*      var;
5010 
5011    assert(scip      != NULL);
5012    assert(eventhdlr != NULL);
5013    assert(event     != NULL);
5014    assert(eventdata != NULL);
5015 
5016    assert((SCIP_NLP*)eventdata == scip->nlp);
5017 
5018    etype = SCIPeventGetType(event);
5019    var   = SCIPeventGetVar(event);
5020 
5021    if( SCIP_EVENTTYPE_VARADDED & etype )
5022    {
5023       SCIPdebugMessage("-> handling varadd event, variable <%s>\n", SCIPvarGetName(var) );
5024       SCIP_CALL( SCIPnlpAddVar(scip->nlp, SCIPblkmem(scip), scip->set, var) );
5025    }
5026    else if( SCIP_EVENTTYPE_VARDELETED & etype )
5027    {
5028       SCIPdebugMessage("-> handling vardel event, variable <%s>\n", SCIPvarGetName(var) );
5029       SCIP_CALL( SCIPnlpDelVar(scip->nlp, SCIPblkmem(scip), scip->set, scip->eventqueue, scip->lp, var) );
5030    }
5031    else if( SCIP_EVENTTYPE_VARFIXED & etype )
5032    {
5033       /* variable was fixed, aggregated, or multi-aggregated */
5034       SCIPdebugMessage("-> handling variable fixation event, variable <%s>\n", SCIPvarGetName(var) );
5035       SCIP_CALL( nlpRemoveFixedVar(scip->nlp, SCIPblkmem(scip), scip->set, scip->stat, scip->eventqueue, scip->lp, var) );
5036    }
5037    else if( SCIP_EVENTTYPE_BOUNDCHANGED & etype )
5038    {
5039       SCIPdebugMessage("-> handling bound changed event %" SCIP_EVENTTYPE_FORMAT ", variable <%s>\n", etype, SCIPvarGetName(var) );
5040       SCIP_CALL( nlpUpdateVarBounds(scip->nlp, scip->set, var, (SCIP_Bool)(SCIP_EVENTTYPE_BOUNDTIGHTENED & etype)) );
5041    }
5042    else if( SCIP_EVENTTYPE_OBJCHANGED & etype )
5043    {
5044       SCIPdebugMessage("-> handling objchg event, variable <%s>\n", SCIPvarGetName(var) );
5045       SCIP_CALL( nlpUpdateObjCoef(scip->nlp, var) );
5046    }
5047    else
5048    {
5049       SCIPerrorMessage("unexpected event %ld on variable <%s>\n", etype, SCIPvarGetName(var) );
5050       return SCIP_ERROR;
5051    }
5052 
5053    return SCIP_OKAY;
5054 }
5055 
5056 
5057 /*
5058  * public NLP methods
5059  */
5060 
5061 /** includes NLP specific plugins (e.g., event handler) and parameters */
SCIPnlpInclude(SCIP_SET * set,BMS_BLKMEM * blkmem)5062 SCIP_RETCODE SCIPnlpInclude(
5063    SCIP_SET*             set,                /**< global SCIP settings */
5064    BMS_BLKMEM*           blkmem              /**< block memory */
5065    )
5066 {
5067    SCIP_EVENTHDLR* eventhdlr;
5068 
5069    assert(set != NULL);
5070    assert(blkmem != NULL);
5071    assert(set->stage == SCIP_STAGE_INIT);
5072 
5073    /* check whether event handler is already present */
5074    if( SCIPsetFindEventhdlr(set, EVENTHDLR_NAME) != NULL )
5075    {
5076       SCIPerrorMessage("event handler <" EVENTHDLR_NAME "> already included.\n");
5077       return SCIP_INVALIDDATA;
5078    }
5079 
5080    SCIP_CALL( SCIPeventhdlrCreate(&eventhdlr, set, EVENTHDLR_NAME, EVENTHDLR_DESC,
5081          NULL, NULL, NULL, NULL, NULL, NULL, NULL, eventExecNlp, NULL) );
5082    SCIP_CALL( SCIPsetIncludeEventhdlr(set, eventhdlr) );
5083 
5084    return SCIP_OKAY;
5085 } /*lint !e715*/
5086 
5087 /** construct a new empty NLP */
SCIPnlpCreate(SCIP_NLP ** nlp,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,const char * name,int nvars_estimate)5088 SCIP_RETCODE SCIPnlpCreate(
5089    SCIP_NLP**            nlp,                /**< NLP handler, call by reference */
5090    BMS_BLKMEM*           blkmem,             /**< block memory */
5091    SCIP_SET*             set,                /**< global SCIP settings */
5092    SCIP_STAT*            stat,               /**< problem statistics */
5093    const char*           name,               /**< problem name */
5094    int                   nvars_estimate      /**< an estimate on the number of variables that may be added to the NLP later */
5095    )
5096 {
5097    assert(nlp  != NULL);
5098    assert(blkmem != NULL);
5099    assert(set  != NULL);
5100    assert(stat != NULL);
5101    assert(name != NULL);
5102 
5103    SCIP_ALLOC( BMSallocMemory(nlp) );
5104 
5105    /* select NLP solver (if any available) and setup problem */
5106    if( set->nnlpis > 0 )
5107    {
5108       assert(set->nlp_solver != NULL);
5109       if( set->nlp_solver[0] == '\0' )
5110       { /* take solver with highest priority */
5111          assert(set->nlpis != NULL);
5112 
5113          /* sort the NLPIs if necessary */
5114          if( !set->nlpissorted )
5115             SCIPsetSortNlpis(set);
5116 
5117          (*nlp)->solver = set->nlpis[0];
5118       }
5119       else
5120       { /* find user specified NLP solver */
5121          (*nlp)->solver = SCIPsetFindNlpi(set, set->nlp_solver);
5122          if( (*nlp)->solver == NULL )
5123          {
5124             SCIPerrorMessage("Selected NLP solver <%s> not available.\n", set->nlp_solver);
5125             return SCIP_PLUGINNOTFOUND;
5126          }
5127       }
5128       assert((*nlp)->solver != NULL);
5129       SCIP_CALL( SCIPnlpiCreateProblem((*nlp)->solver, &(*nlp)->problem, "scip_nlp") );
5130    }
5131    else
5132    {
5133       /* maybe someone wanna use the NLP just to collect nonlinearities, but is not necessarily interesting on solving
5134        * so we allow this and just continue */
5135       (*nlp)->solver = NULL;
5136       (*nlp)->problem = NULL;
5137    }
5138 
5139    /* status */
5140    (*nlp)->nunflushedvaradd   = 0;
5141    (*nlp)->nunflushedvardel   = 0;
5142    (*nlp)->nunflushednlrowadd = 0;
5143    (*nlp)->nunflushednlrowdel = 0;
5144    (*nlp)->isrelax    = TRUE;
5145    (*nlp)->indiving   = FALSE;
5146 
5147    /* variables in problem and NLPI problem */
5148    (*nlp)->nvars = 0;
5149    (*nlp)->sizevars = 0;
5150    (*nlp)->vars = NULL;
5151    SCIP_CALL( SCIPhashmapCreate(&(*nlp)->varhash, blkmem, nvars_estimate) );
5152 
5153    (*nlp)->nvars_solver = 0;
5154    (*nlp)->sizevars_solver = 0;
5155    (*nlp)->varmap_nlp2nlpi = NULL;
5156    (*nlp)->varmap_nlpi2nlp = NULL;
5157 
5158    /* nonlinear rows in problem and NLPI problem */
5159    (*nlp)->nnlrows = 0;
5160    (*nlp)->sizenlrows = 0;
5161    (*nlp)->nlrows = NULL;
5162 
5163    (*nlp)->nnlrows_solver = 0;
5164    (*nlp)->sizenlrows_solver = 0;
5165    (*nlp)->nlrowmap_nlpi2nlp = NULL;
5166 
5167    /* objective function */
5168    (*nlp)->objflushed = TRUE;
5169    (*nlp)->divingobj = NULL;
5170 
5171    /* initial guess */
5172    (*nlp)->haveinitguess = FALSE;
5173    (*nlp)->initialguess = NULL;
5174 
5175    /* solution of NLP */
5176    (*nlp)->primalsolobjval = SCIP_INVALID;
5177    (*nlp)->solstat         = SCIP_NLPSOLSTAT_UNKNOWN;
5178    (*nlp)->termstat        = SCIP_NLPTERMSTAT_OTHER;
5179    (*nlp)->varlbdualvals   = NULL;
5180    (*nlp)->varubdualvals   = NULL;
5181 
5182    /* event handling: catch variable addition and deletion events */
5183    (*nlp)->eventhdlr = SCIPsetFindEventhdlr(set, EVENTHDLR_NAME);
5184    if( (*nlp)->eventhdlr == NULL )
5185    {
5186       SCIPerrorMessage("NLP eventhandler <" EVENTHDLR_NAME "> not found.\n");
5187       return SCIP_PLUGINNOTFOUND;
5188    }
5189    SCIP_CALL( SCIPeventfilterAdd(set->scip->eventfilter, blkmem, set,
5190          SCIP_EVENTTYPE_VARADDED | SCIP_EVENTTYPE_VARDELETED,
5191          (*nlp)->eventhdlr, (SCIP_EVENTDATA*)(*nlp), &(*nlp)->globalfilterpos) );
5192 
5193    /* fractional variables in last NLP solution */
5194    (*nlp)->fracvars     = NULL;
5195    (*nlp)->fracvarssol  = NULL;
5196    (*nlp)->fracvarsfrac = NULL;
5197    (*nlp)->nfracvars     = 0;
5198    (*nlp)->npriofracvars = 0;
5199    (*nlp)->fracvarssize  = 0;
5200    (*nlp)->validfracvars = -1;
5201 
5202    /* miscellaneous */
5203    SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &(*nlp)->name, name, strlen(name)+1) );
5204 
5205    return SCIP_OKAY;
5206 }
5207 
5208 /** frees NLP data object */
SCIPnlpFree(SCIP_NLP ** nlp,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_EVENTQUEUE * eventqueue,SCIP_LP * lp)5209 SCIP_RETCODE SCIPnlpFree(
5210    SCIP_NLP**            nlp,                /**< pointer to NLP data object */
5211    BMS_BLKMEM*           blkmem,             /**< block memory */
5212    SCIP_SET*             set,                /**< global SCIP settings */
5213    SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
5214    SCIP_LP*              lp                  /**< SCIP LP, needed for releasing variables */
5215    )
5216 {
5217    assert(nlp    != NULL);
5218    assert(*nlp   != NULL);
5219    assert(blkmem != NULL);
5220    assert(set    != NULL);
5221 
5222    /* drop fractional variables */
5223    BMSfreeBlockMemoryArrayNull(blkmem, &(*nlp)->fracvars,     (*nlp)->fracvarssize);
5224    BMSfreeBlockMemoryArrayNull(blkmem, &(*nlp)->fracvarssol,  (*nlp)->fracvarssize);
5225    BMSfreeBlockMemoryArrayNull(blkmem, &(*nlp)->fracvarsfrac, (*nlp)->fracvarssize);
5226 
5227    /* drop global events (variable addition and deletion) */
5228    SCIP_CALL( SCIPeventfilterDel(set->scip->eventfilter, blkmem, set,
5229          SCIP_EVENTTYPE_VARADDED | SCIP_EVENTTYPE_VARDELETED,
5230          (*nlp)->eventhdlr, (SCIP_EVENTDATA*)(*nlp), (*nlp)->globalfilterpos) );
5231 
5232    SCIP_CALL( SCIPnlpReset(*nlp, blkmem, set, eventqueue, lp) );
5233    assert((*nlp)->nnlrows == 0);
5234    assert((*nlp)->nnlrows_solver == 0);
5235    assert((*nlp)->nvars == 0);
5236    assert((*nlp)->nvars_solver == 0);
5237    assert((*nlp)->initialguess == NULL);
5238 
5239    BMSfreeBlockMemoryArray(blkmem, &(*nlp)->name, strlen((*nlp)->name)+1);
5240 
5241    /* free nonlinear rows arrays */
5242    BMSfreeBlockMemoryArrayNull(blkmem, &(*nlp)->nlrowmap_nlpi2nlp, (*nlp)->sizenlrows_solver);
5243    BMSfreeBlockMemoryArrayNull(blkmem, &(*nlp)->nlrows, (*nlp)->sizenlrows);
5244 
5245    /* free variables arrays */
5246    BMSfreeBlockMemoryArrayNull(blkmem, &(*nlp)->varmap_nlp2nlpi, (*nlp)->sizevars);
5247    BMSfreeBlockMemoryArrayNull(blkmem, &(*nlp)->varmap_nlpi2nlp, (*nlp)->sizevars_solver);
5248    SCIPhashmapFree(&(*nlp)->varhash);
5249    BMSfreeBlockMemoryArrayNull(blkmem, &(*nlp)->vars, (*nlp)->sizevars);
5250    BMSfreeBlockMemoryArrayNull(blkmem, &(*nlp)->varlbdualvals, (*nlp)->sizevars);
5251    BMSfreeBlockMemoryArrayNull(blkmem, &(*nlp)->varubdualvals, (*nlp)->sizevars);
5252 
5253    /* free NLPI problem */
5254    if( (*nlp)->problem != NULL )
5255    {
5256       SCIP_CALL( SCIPnlpiFreeProblem((*nlp)->solver, &(*nlp)->problem) );
5257    }
5258 
5259    /* free NLP data structure */
5260    BMSfreeMemory(nlp);
5261 
5262    return SCIP_OKAY;
5263 }
5264 
5265 /** resets the NLP to the empty NLP by removing all variables and rows from NLP,
5266  *  releasing all rows, and flushing the changes to the NLP solver
5267  */
SCIPnlpReset(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_EVENTQUEUE * eventqueue,SCIP_LP * lp)5268 SCIP_RETCODE SCIPnlpReset(
5269    SCIP_NLP*             nlp,                /**< NLP data */
5270    BMS_BLKMEM*           blkmem,             /**< block memory */
5271    SCIP_SET*             set,                /**< global SCIP settings */
5272    SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
5273    SCIP_LP*              lp                  /**< SCIP LP, needed for releasing variables */
5274    )
5275 {
5276    int i;
5277 
5278    assert(nlp    != NULL);
5279    assert(blkmem != NULL);
5280    assert(set    != NULL);
5281 
5282    if( nlp->indiving )
5283    {
5284       SCIP_CALL( SCIPnlpEndDive(nlp, blkmem, set) );
5285    }
5286 
5287    nlp->solstat  = SCIP_NLPSOLSTAT_UNKNOWN;
5288    nlp->termstat = SCIP_NLPTERMSTAT_OTHER;
5289 
5290    BMSfreeBlockMemoryArrayNull(blkmem, &nlp->initialguess, nlp->sizevars);
5291    nlp->haveinitguess = FALSE;
5292 
5293    for(i = nlp->nnlrows - 1; i >= 0; --i)
5294    {
5295       SCIP_CALL( nlpDelNlRowPos(nlp, blkmem, set, i) );
5296    }
5297 
5298    for(i = nlp->nvars - 1; i >= 0; --i)
5299    {
5300       SCIP_CALL( nlpDelVarPos(nlp, blkmem, set, eventqueue, lp, i) );
5301    }
5302 
5303    SCIP_CALL( SCIPnlpFlush(nlp, blkmem, set) );
5304 
5305    return SCIP_OKAY;
5306 }
5307 
5308 /** currently a dummy function that always returns TRUE */
SCIPnlpHasCurrentNodeNLP(SCIP_NLP * nlp)5309 SCIP_Bool SCIPnlpHasCurrentNodeNLP(
5310    SCIP_NLP*             nlp                 /**< NLP data */
5311    )
5312 {
5313    assert(nlp != NULL);
5314    return TRUE;
5315 } /*lint !e715*/
5316 
5317 /** ensures, that variables array of NLP can store at least num entries */
SCIPnlpEnsureVarsSize(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set,int num)5318 SCIP_RETCODE SCIPnlpEnsureVarsSize(
5319    SCIP_NLP*             nlp,                /**< NLP data */
5320    BMS_BLKMEM*           blkmem,             /**< block memory */
5321    SCIP_SET*             set,                /**< global SCIP settings */
5322    int                   num                 /**< minimum number of entries to store */
5323    )
5324 {
5325    assert(nlp    != NULL);
5326    assert(blkmem != NULL);
5327    assert(set    != NULL);
5328    assert(nlp->nvars <= nlp->sizevars);
5329 
5330    if( num > nlp->sizevars )
5331    {
5332       int newsize;
5333 
5334       newsize = SCIPsetCalcMemGrowSize(set, num);
5335       SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &nlp->vars,            nlp->sizevars, newsize) );
5336       SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &nlp->varmap_nlp2nlpi, nlp->sizevars, newsize) );
5337       SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &nlp->varlbdualvals,   nlp->sizevars, newsize) );
5338       SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &nlp->varubdualvals,   nlp->sizevars, newsize) );
5339       if( nlp->initialguess != NULL )
5340       {
5341          SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &nlp->initialguess, nlp->sizevars, newsize) );
5342       }
5343 
5344       nlp->sizevars = newsize;
5345    }
5346    assert(num <= nlp->sizevars);
5347 
5348    return SCIP_OKAY;
5349 }
5350 
5351 /** adds a variable to the NLP and captures the variable */
SCIPnlpAddVar(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_VAR * var)5352 SCIP_RETCODE SCIPnlpAddVar(
5353    SCIP_NLP*             nlp,                /**< NLP data */
5354    BMS_BLKMEM*           blkmem,             /**< block memory */
5355    SCIP_SET*             set,                /**< global SCIP settings */
5356    SCIP_VAR*             var                 /**< variable */
5357    )
5358 {
5359    assert(nlp != NULL);
5360    assert(blkmem != NULL);
5361    assert(set != NULL);
5362    assert(var != NULL);
5363    assert(SCIPvarIsTransformed(var));
5364    assert(!SCIPhashmapExists(nlp->varhash, var));
5365 
5366    if( nlp->indiving )
5367    {
5368       SCIPerrorMessage("cannot add variable during NLP diving\n");
5369       return SCIP_ERROR;
5370    }
5371 
5372    SCIP_CALL( nlpAddVars(nlp, blkmem, set, 1, &var) );
5373 
5374    return SCIP_OKAY;
5375 }
5376 
5377 /** adds a set of variables to the NLP and captures the variables */
SCIPnlpAddVars(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set,int nvars,SCIP_VAR ** vars)5378 SCIP_RETCODE SCIPnlpAddVars(
5379    SCIP_NLP*             nlp,                /**< NLP data */
5380    BMS_BLKMEM*           blkmem,             /**< block memory */
5381    SCIP_SET*             set,                /**< global SCIP settings */
5382    int                   nvars,              /**< number of variables to add */
5383    SCIP_VAR**            vars                /**< variables to add */
5384    )
5385 {
5386    assert(nlp != NULL);
5387    assert(blkmem != NULL);
5388    assert(set != NULL);
5389    assert(vars != NULL || nvars == 0);
5390 
5391    if( nlp->indiving && nvars > 0)
5392    {
5393       SCIPerrorMessage("cannot add variables during NLP diving\n");
5394       return SCIP_ERROR;
5395    }
5396 
5397    SCIP_CALL( nlpAddVars(nlp, blkmem, set, nvars, vars) );
5398 
5399    return SCIP_OKAY;
5400 }
5401 
5402 /** deletes a variable from the NLP and releases the variable */
SCIPnlpDelVar(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_EVENTQUEUE * eventqueue,SCIP_LP * lp,SCIP_VAR * var)5403 SCIP_RETCODE SCIPnlpDelVar(
5404    SCIP_NLP*             nlp,                /**< NLP data */
5405    BMS_BLKMEM*           blkmem,             /**< block memory */
5406    SCIP_SET*             set,                /**< global SCIP settings */
5407    SCIP_EVENTQUEUE*      eventqueue,         /**< event queue */
5408    SCIP_LP*              lp,                 /**< SCIP LP, needed to release variable */
5409    SCIP_VAR*             var                 /**< variable */
5410    )
5411 {
5412    int varpos;
5413 
5414    assert(nlp    != NULL);
5415    assert(blkmem != NULL);
5416    assert(set    != NULL);
5417    assert(var    != NULL);
5418 
5419    if( !SCIPhashmapExists(nlp->varhash, var) )
5420    {
5421       SCIPerrorMessage("variable <%s> not found in NLP, cannot delete\n", SCIPvarGetName(var));
5422       return SCIP_ERROR;
5423    }
5424 
5425    if( nlp->indiving )
5426    {
5427       SCIPerrorMessage("cannot delete variable during NLP diving\n");
5428       return SCIP_ERROR;
5429    }
5430 
5431    varpos = SCIPhashmapGetImageInt(nlp->varhash, var);
5432 
5433    SCIP_CALL( nlpDelVarPos(nlp, blkmem, set, eventqueue, lp, varpos) );
5434 
5435    return SCIP_OKAY;
5436 }
5437 
5438 /** ensures, that nonlinear rows array of NLP can store at least num entries */
SCIPnlpEnsureNlRowsSize(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set,int num)5439 SCIP_RETCODE SCIPnlpEnsureNlRowsSize(
5440    SCIP_NLP*             nlp,                /**< NLP data */
5441    BMS_BLKMEM*           blkmem,             /**< block memory */
5442    SCIP_SET*             set,                /**< global SCIP settings */
5443    int                   num                 /**< minimum number of entries to store */
5444    )
5445 {
5446    assert(nlp    != NULL);
5447    assert(blkmem != NULL);
5448    assert(set    != NULL);
5449    assert(nlp->nnlrows <= nlp->sizenlrows);
5450 
5451    if( num > nlp->sizenlrows )
5452    {
5453       int newsize;
5454 
5455       newsize = SCIPsetCalcMemGrowSize(set, num);
5456       SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &nlp->nlrows, nlp->sizenlrows, newsize) );
5457 
5458       nlp->sizenlrows = newsize;
5459    }
5460    assert(num <= nlp->sizenlrows);
5461 
5462    return SCIP_OKAY;
5463 }
5464 
5465 /** adds a nonlinear row to the NLP and captures it
5466  * all variables of the row need to be present in the NLP */
SCIPnlpAddNlRow(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_NLROW * nlrow)5467 SCIP_RETCODE SCIPnlpAddNlRow(
5468    SCIP_NLP*             nlp,                /**< NLP data */
5469    BMS_BLKMEM*           blkmem,             /**< block memory */
5470    SCIP_SET*             set,                /**< global SCIP settings */
5471    SCIP_STAT*            stat,               /**< problem statistics data */
5472    SCIP_NLROW*           nlrow               /**< nonlinear row */
5473    )
5474 {
5475    assert(nlp   != NULL);
5476    assert(nlrow != NULL);
5477 
5478    if( nlp->indiving )
5479    {
5480       SCIPerrorMessage("cannot add row during NLP diving\n");
5481       return SCIP_ERROR;
5482    }
5483 
5484    SCIP_CALL( nlpAddNlRows(nlp, blkmem, set, stat, 1, &nlrow) );
5485 
5486    return SCIP_OKAY;
5487 }
5488 
5489 /** adds nonlinear rows to the NLP and captures them
5490  * all variables of the row need to be present in the NLP */
SCIPnlpAddNlRows(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,int nnlrows,SCIP_NLROW ** nlrows)5491 SCIP_RETCODE SCIPnlpAddNlRows(
5492    SCIP_NLP*             nlp,                /**< NLP data */
5493    BMS_BLKMEM*           blkmem,             /**< block memory */
5494    SCIP_SET*             set,                /**< global SCIP settings */
5495    SCIP_STAT*            stat,               /**< problem statistics data */
5496    int                   nnlrows,            /**< number of rows to add */
5497    SCIP_NLROW**          nlrows              /**< rows to add */
5498    )
5499 {
5500    assert(nlp    != NULL);
5501    assert(nlrows != NULL || nnlrows == 0);
5502 
5503    if( nnlrows == 0 )
5504       return SCIP_OKAY;
5505 
5506    if( nlp->indiving )
5507    {
5508       SCIPerrorMessage("cannot add rows during NLP diving\n");
5509       return SCIP_ERROR;
5510    }
5511 
5512    SCIP_CALL( nlpAddNlRows(nlp, blkmem, set, stat, nnlrows, nlrows) );
5513 
5514    return SCIP_OKAY;
5515 }
5516 
5517 /** deletes a nonlinear row from the NLP
5518  * does nothing if nonlinear row is not in NLP */
SCIPnlpDelNlRow(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_NLROW * nlrow)5519 SCIP_RETCODE SCIPnlpDelNlRow(
5520    SCIP_NLP*             nlp,                /**< NLP data */
5521    BMS_BLKMEM*           blkmem,             /**< block memory */
5522    SCIP_SET*             set,                /**< global SCIP settings */
5523    SCIP_NLROW*           nlrow               /**< nonlinear row */
5524    )
5525 {
5526    assert(nlp    != NULL);
5527    assert(blkmem != NULL);
5528    assert(set    != NULL);
5529    assert(nlrow  != NULL);
5530 
5531    /* if row not in NLP, nothing to do */
5532    if( nlrow->nlpindex == -1 )
5533       return SCIP_OKAY;
5534 
5535    assert(nlrow->nlpindex >= 0);
5536    assert(nlrow->nlpindex < nlp->nnlrows);
5537 
5538    if( nlp->indiving )
5539    {
5540       SCIPerrorMessage("cannot delete row during NLP diving\n");
5541       return SCIP_ERROR;
5542    }
5543 
5544    SCIP_CALL( nlpDelNlRowPos(nlp, blkmem, set, nlrow->nlpindex) );
5545 
5546    return SCIP_OKAY;
5547 }
5548 
5549 /** applies all cached changes to the NLP solver */
SCIPnlpFlush(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set)5550 SCIP_RETCODE SCIPnlpFlush(
5551    SCIP_NLP*             nlp,                /**< current NLP data */
5552    BMS_BLKMEM*           blkmem,             /**< block memory */
5553    SCIP_SET*             set                 /**< global SCIP settings */
5554    )
5555 {
5556    assert(nlp    != NULL);
5557    assert(blkmem != NULL);
5558    assert(set    != NULL);
5559 
5560    if( nlp->indiving )
5561    {
5562       SCIPerrorMessage("cannot flush NLP during NLP diving\n");
5563       return SCIP_ERROR;
5564    }
5565 
5566    /* flush removals of nonlinear rows and variables */
5567    SCIP_CALL( nlpFlushNlRowDeletions(nlp, blkmem, set) );
5568    SCIP_CALL( nlpFlushVarDeletions(nlp, blkmem, set) );
5569    assert(nlp->nunflushednlrowdel == 0);
5570    assert(nlp->nunflushedvardel   == 0);
5571 
5572    /* flush addition of variables, objective, and addition of rows */
5573    SCIP_CALL( nlpFlushVarAdditions(nlp, blkmem, set) );
5574    SCIP_CALL( nlpFlushObjective(nlp, blkmem, set) );
5575    SCIP_CALL( nlpFlushNlRowAdditions(nlp, blkmem, set) );
5576    assert(nlp->nunflushedvaradd == 0);
5577    assert(nlp->objflushed == TRUE);
5578    assert(nlp->nunflushednlrowadd == 0);
5579 
5580    assert(nlp->nvars   == nlp->nvars_solver);
5581    assert(nlp->nnlrows == nlp->nnlrows_solver);
5582 
5583    return SCIP_OKAY;
5584 }
5585 
5586 /** solves the NLP */
SCIPnlpSolve(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_MESSAGEHDLR * messagehdlr,SCIP_STAT * stat)5587 SCIP_RETCODE SCIPnlpSolve(
5588    SCIP_NLP*             nlp,                /**< NLP data */
5589    BMS_BLKMEM*           blkmem,             /**< block memory buffers */
5590    SCIP_SET*             set,                /**< global SCIP settings */
5591    SCIP_MESSAGEHDLR*     messagehdlr,        /**< message handler */
5592    SCIP_STAT*            stat                /**< problem statistics */
5593    )
5594 {
5595    assert(nlp    != NULL);
5596    assert(blkmem != NULL);
5597    assert(set    != NULL);
5598    assert(stat   != NULL);
5599 
5600    if( nlp->indiving )
5601    {
5602       SCIPerrorMessage("cannot solve NLP during NLP diving (use SCIPsolveDiveNLP)\n");
5603       return SCIP_ERROR;
5604    }
5605 
5606    SCIP_CALL( SCIPnlpFlush(nlp, blkmem, set) );
5607 
5608    SCIP_CALL( nlpSolve(nlp, blkmem, set, messagehdlr, stat) );
5609 
5610    return SCIP_OKAY;
5611 }
5612 
5613 /** gets objective value of current NLP */
SCIPnlpGetObjval(SCIP_NLP * nlp)5614 SCIP_Real SCIPnlpGetObjval(
5615    SCIP_NLP*             nlp                 /**< current NLP data */
5616    )
5617 {
5618    assert(nlp != NULL);
5619 
5620    return nlp->primalsolobjval;
5621 }
5622 
5623 /** gives current pseudo objective value */
SCIPnlpGetPseudoObjval(SCIP_NLP * nlp,SCIP_SET * set,SCIP_STAT * stat,SCIP_Real * pseudoobjval)5624 SCIP_RETCODE SCIPnlpGetPseudoObjval(
5625    SCIP_NLP*             nlp,                /**< current NLP data */
5626    SCIP_SET*             set,                /**< global SCIP settings */
5627    SCIP_STAT*            stat,               /**< problem statistics */
5628    SCIP_Real*            pseudoobjval        /**< buffer to store pseudo objective value */
5629    )
5630 {
5631    assert(nlp != NULL);
5632    assert(pseudoobjval != NULL);
5633 
5634    if( nlp->divingobj != NULL )
5635    {
5636       assert(nlp->indiving);
5637       SCIP_CALL( SCIPnlrowGetPseudoActivity(nlp->divingobj, set, stat, pseudoobjval) );
5638    }
5639    else
5640    {
5641       int i;
5642 
5643       *pseudoobjval = 0.0;
5644       for( i = 0; i < nlp->nvars; ++i )
5645          *pseudoobjval += SCIPvarGetObj(nlp->vars[i]) * SCIPvarGetBestBoundLocal(nlp->vars[i]);
5646    }
5647 
5648    return SCIP_OKAY;
5649 }
5650 
5651 /** gets fractional variables of last NLP solution along with solution values and fractionalities
5652  */
SCIPnlpGetFracVars(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_VAR *** fracvars,SCIP_Real ** fracvarssol,SCIP_Real ** fracvarsfrac,int * nfracvars,int * npriofracvars)5653 SCIP_RETCODE SCIPnlpGetFracVars(
5654    SCIP_NLP*             nlp,                /**< NLP data structure */
5655    BMS_BLKMEM*           blkmem,             /**< block memory */
5656    SCIP_SET*             set,                /**< global SCIP settings */
5657    SCIP_STAT*            stat,               /**< problem statistics */
5658    SCIP_VAR***           fracvars,           /**< pointer to store the array of NLP fractional variables, or NULL */
5659    SCIP_Real**           fracvarssol,        /**< pointer to store the array of NLP fractional variables solution values, or NULL */
5660    SCIP_Real**           fracvarsfrac,       /**< pointer to store the array of NLP fractional variables fractionalities, or NULL */
5661    int*                  nfracvars,          /**< pointer to store the number of NLP fractional variables , or NULL */
5662    int*                  npriofracvars       /**< pointer to store the number of NLP fractional variables with maximal branching priority, or NULL */
5663    )
5664 {
5665    assert(nlp != NULL);
5666 
5667    SCIP_CALL( nlpCalcFracVars(nlp, blkmem, set, stat) );
5668    assert(nlp->fracvars     != NULL);
5669    assert(nlp->fracvarssol  != NULL);
5670    assert(nlp->fracvarsfrac != NULL);
5671 
5672    if( fracvars != NULL )
5673       *fracvars = nlp->fracvars;
5674    if( fracvarssol != NULL )
5675       *fracvarssol = nlp->fracvarssol;
5676    if( fracvarsfrac != NULL )
5677       *fracvarsfrac = nlp->fracvarsfrac;
5678    if( nfracvars != NULL )
5679       *nfracvars = nlp->nfracvars;
5680    if( npriofracvars != NULL )
5681       *npriofracvars = nlp->npriofracvars;
5682 
5683    return SCIP_OKAY;
5684 }
5685 
5686 /** removes all redundant nonlinear rows */
SCIPnlpRemoveRedundantNlRows(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat)5687 SCIP_RETCODE SCIPnlpRemoveRedundantNlRows(
5688    SCIP_NLP*             nlp,                /**< current NLP data */
5689    BMS_BLKMEM*           blkmem,             /**< block memory buffers */
5690    SCIP_SET*             set,                /**< global SCIP settings */
5691    SCIP_STAT*            stat                /**< problem statistics */
5692    )
5693 {
5694    SCIP_NLPSOLSTAT solstatus;
5695    SCIP_Bool isredundant;
5696    int i;
5697 
5698    assert(nlp    != NULL);
5699    assert(blkmem != NULL);
5700    assert(set    != NULL);
5701    assert(stat   != NULL);
5702 
5703    if( nlp->nnlrows == 0 )
5704       return SCIP_OKAY;
5705 
5706    if( nlp->indiving )
5707    {
5708       SCIPerrorMessage("cannot remove redundant rows during NLP diving\n");
5709       return SCIP_ERROR;
5710    }
5711 
5712    /* removing redundant rows should not change the solution status, so we reset it at the end */
5713    solstatus = nlp->solstat;
5714 
5715    for( i = 0; i < nlp->nnlrows; ++i )
5716    {
5717       SCIP_CALL( SCIPnlrowIsRedundant(nlp->nlrows[i], set, stat, &isredundant) );
5718       if( isredundant )
5719       {
5720          SCIP_CALL( nlpDelNlRowPos(nlp, blkmem, set, i) );
5721       }
5722    }
5723 
5724    nlp->solstat = solstatus;
5725 
5726    return SCIP_OKAY;
5727 }
5728 
5729 /** set initial guess (approximate primal solution) for next solve
5730  *
5731  *  array initguess must be NULL or have length at least SCIPnlpGetNVars()
5732  */
SCIPnlpSetInitialGuess(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_Real * initguess)5733 SCIP_RETCODE SCIPnlpSetInitialGuess(
5734    SCIP_NLP*             nlp,                /**< current NLP data */
5735    BMS_BLKMEM*           blkmem,             /**< block memory buffers */
5736    SCIP_Real*            initguess           /**< new initial guess, or NULL to clear previous one */
5737    )
5738 {
5739    assert(nlp    != NULL);
5740    assert(blkmem != NULL);
5741    assert(nlp->solver  != NULL);
5742    assert(nlp->problem != NULL);
5743 
5744    /* if user wants to let NLP solver choose start point, then invalidate current initial guess both in NLP and in NLPI */
5745    if( initguess == NULL )
5746    {
5747       nlp->haveinitguess = FALSE;
5748       SCIP_CALL( SCIPnlpiSetInitialGuess(nlp->solver, nlp->problem, NULL, NULL, NULL, NULL) );
5749       return SCIP_OKAY;
5750    }
5751 
5752    if( nlp->initialguess != NULL )
5753    {
5754       BMScopyMemoryArray(nlp->initialguess, initguess, nlp->nvars);
5755    }
5756    else
5757    {
5758       assert( nlp->sizevars >= nlp->nvars );
5759       SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &nlp->initialguess, nlp->sizevars) );
5760       BMScopyMemoryArray(nlp->initialguess, initguess, nlp->nvars);
5761    }
5762    nlp->haveinitguess = TRUE;
5763 
5764    return SCIP_OKAY;
5765 }
5766 
5767 /** writes NLP to a file */
SCIPnlpWrite(SCIP_NLP * nlp,SCIP_SET * set,SCIP_MESSAGEHDLR * messagehdlr,const char * fname)5768 SCIP_RETCODE SCIPnlpWrite(
5769    SCIP_NLP*             nlp,                /**< current NLP data */
5770    SCIP_SET*             set,                /**< global SCIP settings */
5771    SCIP_MESSAGEHDLR*     messagehdlr,        /**< message handler */
5772    const char*           fname               /**< file name */
5773    )
5774 {
5775    FILE* file;
5776    int i;
5777 
5778    assert(nlp != NULL);
5779 
5780    if( fname != NULL )
5781    {
5782       file = fopen(fname, "w");
5783       if( file == NULL )
5784       {
5785          SCIPerrorMessage("could not open file <%s> for writing\n", fname);
5786          return SCIP_FILECREATEERROR;
5787       }
5788    }
5789    else
5790       file = stdout;
5791 
5792    SCIPmessageFPrintInfo(messagehdlr, file, "STATISTICS\n");
5793    SCIPmessageFPrintInfo(messagehdlr, file, "  NLP name: %s\n", nlp->name);
5794    SCIPmessageFPrintInfo(messagehdlr, file, "  Variables: %d\n", nlp->nvars);
5795    SCIPmessageFPrintInfo(messagehdlr, file, "  Rows: %d\n", nlp->nnlrows);
5796 
5797    SCIPmessageFPrintInfo(messagehdlr, file, "VARIABLES\n");
5798    for( i = 0; i < nlp->nvars; ++i )
5799    {
5800       SCIP_CALL( SCIPvarPrint(nlp->vars[i], set, messagehdlr, file) );
5801    }
5802 
5803    SCIPmessageFPrintInfo(messagehdlr, file, "NONLINEAR ROWS\n");
5804    for( i = 0; i < nlp->nnlrows; ++i )
5805    {
5806       SCIPmessageFPrintInfo(messagehdlr, file, "  ");
5807       SCIP_CALL( SCIPnlrowPrint(nlp->nlrows[i], messagehdlr, file) );
5808    }
5809 
5810    if( fname != NULL )
5811    {
5812       fclose(file);
5813    }
5814 
5815    return SCIP_OKAY;
5816 }
5817 
5818 /** gets array with variables of the NLP */
SCIPnlpGetVars(SCIP_NLP * nlp)5819 SCIP_VAR** SCIPnlpGetVars(
5820    SCIP_NLP*             nlp                 /**< current NLP data */
5821    )
5822 {
5823    assert(nlp != NULL);
5824 
5825    return nlp->vars;
5826 }
5827 
5828 /** gets current number of variables in NLP */
SCIPnlpGetNVars(SCIP_NLP * nlp)5829 int SCIPnlpGetNVars(
5830    SCIP_NLP*             nlp                 /**< current NLP data */
5831    )
5832 {
5833    assert(nlp != NULL);
5834 
5835    return nlp->nvars;
5836 }
5837 
5838 /** computes for each variables the number of NLP rows in which the variable appears in a nonlinear var */
SCIPnlpGetVarsNonlinearity(SCIP_NLP * nlp,int * nlcount)5839 SCIP_RETCODE SCIPnlpGetVarsNonlinearity(
5840    SCIP_NLP*             nlp,                /**< current NLP data */
5841    int*                  nlcount             /**< an array of length at least SCIPnlpGetNVars() to store nonlinearity counts of variables */
5842    )
5843 {
5844    SCIP_NLROW* nlrow;
5845    int varidx;
5846    int i;
5847    int c;
5848 
5849    assert(nlp != NULL);
5850    assert(nlcount != NULL || nlp->nvars == 0);
5851 
5852    BMSclearMemoryArray(nlcount, nlp->nvars);
5853 
5854    for( c = 0; c < nlp->nnlrows; ++c )
5855    {
5856       nlrow = nlp->nlrows[c];
5857       assert(nlrow != NULL);
5858 
5859       for( i = 0; i < nlrow->nquadvars; ++i )
5860       {
5861          assert(SCIPhashmapExists(nlp->varhash, (void*)nlrow->quadvars[i]));
5862          varidx = SCIPhashmapGetImageInt(nlp->varhash, (void*)nlrow->quadvars[i]);
5863          assert(varidx < nlp->nvars);
5864          assert(nlcount != NULL);
5865          ++nlcount[varidx];  /*lint !e613 */
5866       }
5867 
5868       if( nlrow->exprtree != NULL )
5869       {
5870          SCIP_VAR** exprtreevars;
5871          int nexprtreevars;
5872 
5873          exprtreevars = SCIPexprtreeGetVars(nlrow->exprtree);
5874          nexprtreevars = SCIPexprtreeGetNVars(nlrow->exprtree);
5875          assert(exprtreevars != NULL || nexprtreevars == 0);
5876          for( i = 0; i < nexprtreevars; ++i )
5877          {
5878             assert(SCIPhashmapExists(nlp->varhash, (void*)exprtreevars[i]));  /*lint !e613 */
5879 
5880             /* skip variables that also appear in quadratic part, so they are not counted twice */
5881             if( nlrow->quadvarshash != NULL && SCIPhashmapExists(nlrow->quadvarshash, (void*)exprtreevars[i]) )  /*lint !e613 */
5882                continue;
5883 
5884             varidx = SCIPhashmapGetImageInt(nlp->varhash, (void*)exprtreevars[i]);  /*lint !e613 */
5885             assert(varidx < nlp->nvars);
5886             assert(nlcount != NULL);
5887             ++nlcount[varidx];  /*lint !e613 */
5888          }
5889       }
5890    }
5891 
5892    return SCIP_OKAY;
5893 }
5894 
5895 
5896 /** indicates whether there exists a row that contains a continuous variable in a nonlinear term
5897  *
5898  * @note The method may have to touch every row and nonlinear term to compute its result.
5899  */
SCIPnlpHasContinuousNonlinearity(SCIP_NLP * nlp)5900 SCIP_Bool SCIPnlpHasContinuousNonlinearity(
5901    SCIP_NLP*             nlp                 /**< current NLP data */
5902    )
5903 {
5904    SCIP_NLROW* nlrow;
5905    int c;
5906    int i;
5907 
5908    assert(nlp != NULL);
5909 
5910    for( c = 0; c < nlp->nnlrows; ++c )
5911    {
5912       nlrow = nlp->nlrows[c];
5913       assert(nlrow != NULL);
5914 
5915       for( i = 0; i < nlrow->nquadvars; ++i )
5916          if( SCIPvarGetType(nlrow->quadvars[i]) == SCIP_VARTYPE_CONTINUOUS )
5917             return TRUE;
5918 
5919       if( nlrow->exprtree != NULL )
5920       {
5921          SCIP_VAR** exprtreevars;
5922          int nexprtreevars;
5923 
5924          exprtreevars = SCIPexprtreeGetVars(nlrow->exprtree);
5925          nexprtreevars = SCIPexprtreeGetNVars(nlrow->exprtree);
5926          assert(exprtreevars != NULL || nexprtreevars == 0);
5927 
5928          for( i = 0; i < nexprtreevars; ++i )
5929             if( SCIPvarGetType(exprtreevars[i]) == SCIP_VARTYPE_CONTINUOUS ) /*lint !e613*/
5930                return TRUE;
5931       }
5932    }
5933 
5934    return FALSE;
5935 }
5936 
5937 /** gives dual solution values associated with lower bounds of NLP variables */
SCIPnlpGetVarsLbDualsol(SCIP_NLP * nlp)5938 SCIP_Real* SCIPnlpGetVarsLbDualsol(
5939    SCIP_NLP*             nlp                 /**< current NLP data */
5940    )
5941 {
5942    assert(nlp != NULL);
5943 
5944    return nlp->varlbdualvals;
5945 }
5946 
5947 /** gives dual solution values associated with upper bounds of NLP variables */
SCIPnlpGetVarsUbDualsol(SCIP_NLP * nlp)5948 SCIP_Real* SCIPnlpGetVarsUbDualsol(
5949    SCIP_NLP*             nlp                 /**< current NLP data */
5950    )
5951 {
5952    assert(nlp != NULL);
5953 
5954    return nlp->varubdualvals;
5955 }
5956 
5957 /** gets array with nonlinear rows of the NLP */
SCIPnlpGetNlRows(SCIP_NLP * nlp)5958 SCIP_NLROW** SCIPnlpGetNlRows(
5959    SCIP_NLP*             nlp                 /**< current NLP data */
5960    )
5961 {
5962    assert(nlp != NULL);
5963 
5964    return nlp->nlrows;
5965 }
5966 
5967 /** gets current number of nonlinear rows in NLP */
SCIPnlpGetNNlRows(SCIP_NLP * nlp)5968 int SCIPnlpGetNNlRows(
5969    SCIP_NLP*             nlp                 /**< current NLP data */
5970    )
5971 {
5972    assert(nlp != NULL);
5973 
5974    return nlp->nnlrows;
5975 }
5976 
5977 /** gets the NLP solver interface */
SCIPnlpGetNLPI(SCIP_NLP * nlp)5978 SCIP_NLPI* SCIPnlpGetNLPI(
5979    SCIP_NLP*             nlp                 /**< current NLP data */
5980    )
5981 {
5982    assert(nlp != NULL);
5983 
5984    return nlp->solver;
5985 }
5986 
5987 /** gets the NLP problem in the solver interface */
SCIPnlpGetNLPIProblem(SCIP_NLP * nlp)5988 SCIP_NLPIPROBLEM* SCIPnlpGetNLPIProblem(
5989    SCIP_NLP*             nlp                 /**< current NLP data */
5990    )
5991 {
5992    assert(nlp != NULL);
5993 
5994    return nlp->problem;
5995 }
5996 
5997 /** indicates whether NLP is currently in diving mode */
SCIPnlpIsDiving(SCIP_NLP * nlp)5998 SCIP_Bool SCIPnlpIsDiving(
5999    SCIP_NLP*             nlp                 /**< current NLP data */
6000    )
6001 {
6002    assert(nlp != NULL);
6003 
6004    return nlp->indiving;
6005 }
6006 
6007 /** gets solution status of current NLP */
SCIPnlpGetSolstat(SCIP_NLP * nlp)6008 SCIP_NLPSOLSTAT SCIPnlpGetSolstat(
6009    SCIP_NLP*             nlp                 /**< current NLP data */
6010    )
6011 {
6012    assert(nlp != NULL);
6013 
6014    return nlp->solstat;
6015 }
6016 
6017 /** gets termination status of last NLP solve */
SCIPnlpGetTermstat(SCIP_NLP * nlp)6018 SCIP_NLPTERMSTAT SCIPnlpGetTermstat(
6019    SCIP_NLP*             nlp                 /**< current NLP data */
6020    )
6021 {
6022    assert(nlp != NULL);
6023 
6024    return nlp->termstat;
6025 }
6026 
6027 /** gives statistics (number of iterations, solving time, ...) of last NLP solve */
SCIPnlpGetStatistics(SCIP_NLP * nlp,SCIP_NLPSTATISTICS * statistics)6028 SCIP_RETCODE SCIPnlpGetStatistics(
6029    SCIP_NLP*             nlp,                /**< pointer to NLP datastructure */
6030    SCIP_NLPSTATISTICS*   statistics          /**< pointer to store statistics */
6031    )
6032 {
6033    assert(nlp != NULL);
6034    assert(nlp->solver != NULL);
6035    assert(nlp->problem != NULL);
6036    assert(statistics != NULL);
6037 
6038    SCIP_CALL( SCIPnlpiGetStatistics(nlp->solver, nlp->problem, statistics) );
6039 
6040    return SCIP_OKAY;
6041 }
6042 
6043 /** indicates whether a feasible solution for the current NLP is available
6044  * thus, returns whether the solution status <= feasible  */
SCIPnlpHasSolution(SCIP_NLP * nlp)6045 SCIP_Bool SCIPnlpHasSolution(
6046    SCIP_NLP*             nlp                 /**< current NLP data */
6047    )
6048 {
6049    assert(nlp != NULL);
6050 
6051    return nlp->solstat <= SCIP_NLPSOLSTAT_FEASIBLE;
6052 }
6053 
6054 /** gets integer parameter of NLP */
SCIPnlpGetIntPar(SCIP_NLP * nlp,SCIP_NLPPARAM type,int * ival)6055 SCIP_RETCODE SCIPnlpGetIntPar(
6056    SCIP_NLP*             nlp,                /**< pointer to NLP datastructure */
6057    SCIP_NLPPARAM         type,               /**< parameter number */
6058    int*                  ival                /**< pointer to store the parameter value */
6059    )
6060 {
6061    assert(nlp  != NULL);
6062    assert(nlp->solver  != NULL);
6063    assert(nlp->problem != NULL);
6064    assert(ival != NULL);
6065 
6066    SCIP_CALL( SCIPnlpiGetIntPar(nlp->solver, nlp->problem, type, ival) );
6067 
6068    return SCIP_OKAY;
6069 }
6070 
6071 /** sets integer parameter of NLP */
SCIPnlpSetIntPar(SCIP_NLP * nlp,SCIP_NLPPARAM type,int ival)6072 SCIP_RETCODE SCIPnlpSetIntPar(
6073    SCIP_NLP*             nlp,                /**< pointer to NLP datastructure */
6074    SCIP_NLPPARAM         type,               /**< parameter number */
6075    int                   ival                /**< parameter value */
6076    )
6077 {
6078    assert(nlp  != NULL);
6079    assert(nlp->solver  != NULL);
6080    assert(nlp->problem != NULL);
6081 
6082    SCIP_CALL( SCIPnlpiSetIntPar(nlp->solver, nlp->problem, type, ival) );
6083 
6084    return SCIP_OKAY;
6085 }
6086 
6087 /** gets floating point parameter of NLP */
SCIPnlpGetRealPar(SCIP_NLP * nlp,SCIP_NLPPARAM type,SCIP_Real * dval)6088 SCIP_RETCODE SCIPnlpGetRealPar(
6089    SCIP_NLP*             nlp,                /**< pointer to NLP datastructure */
6090    SCIP_NLPPARAM         type,               /**< parameter number */
6091    SCIP_Real*            dval                /**< pointer to store the parameter value */
6092    )
6093 {
6094    assert(nlp  != NULL);
6095    assert(nlp->solver  != NULL);
6096    assert(nlp->problem != NULL);
6097    assert(dval != NULL);
6098 
6099    SCIP_CALL( SCIPnlpiGetRealPar(nlp->solver, nlp->problem, type, dval) );
6100 
6101    return SCIP_OKAY;
6102 }
6103 
6104 /** sets floating point parameter of NLP */
SCIPnlpSetRealPar(SCIP_NLP * nlp,SCIP_NLPPARAM type,SCIP_Real dval)6105 SCIP_RETCODE SCIPnlpSetRealPar(
6106    SCIP_NLP*             nlp,                /**< pointer to NLP datastructure */
6107    SCIP_NLPPARAM         type,               /**< parameter number */
6108    SCIP_Real             dval                /**< parameter value */
6109    )
6110 {
6111    assert(nlp  != NULL);
6112    assert(nlp->solver  != NULL);
6113    assert(nlp->problem != NULL);
6114 
6115    SCIP_CALL( SCIPnlpiSetRealPar(nlp->solver, nlp->problem, type, dval) );
6116 
6117    return SCIP_OKAY;
6118 }
6119 
6120 /** gets string parameter of NLP */
SCIPnlpGetStringPar(SCIP_NLP * nlp,SCIP_NLPPARAM type,const char ** sval)6121 SCIP_RETCODE SCIPnlpGetStringPar(
6122    SCIP_NLP*             nlp,                /**< pointer to NLP datastructure */
6123    SCIP_NLPPARAM         type,               /**< parameter number */
6124    const char**          sval                /**< pointer to store the parameter value */
6125    )
6126 {
6127    assert(nlp  != NULL);
6128    assert(nlp->solver  != NULL);
6129    assert(nlp->problem != NULL);
6130    assert(sval != NULL);
6131 
6132    SCIP_CALL( SCIPnlpiGetStringPar(nlp->solver, nlp->problem, type, sval) );
6133 
6134    return SCIP_OKAY;
6135 }
6136 
6137 /** sets string parameter of NLP */
SCIPnlpSetStringPar(SCIP_NLP * nlp,SCIP_NLPPARAM type,const char * sval)6138 SCIP_RETCODE SCIPnlpSetStringPar(
6139    SCIP_NLP*             nlp,                /**< pointer to NLP datastructure */
6140    SCIP_NLPPARAM         type,               /**< parameter number */
6141    const char*           sval                /**< parameter value */
6142    )
6143 {
6144    assert(nlp  != NULL);
6145    assert(nlp->solver  != NULL);
6146    assert(nlp->problem != NULL);
6147 
6148    SCIP_CALL( SCIPnlpiSetStringPar(nlp->solver, nlp->problem, type, sval) );
6149 
6150    return SCIP_OKAY;
6151 }
6152 
6153 /*
6154  * NLP diving methods
6155  */
6156 
6157 /** signals start of diving */
SCIPnlpStartDive(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set)6158 SCIP_RETCODE SCIPnlpStartDive(
6159    SCIP_NLP*             nlp,                /**< current NLP data */
6160    BMS_BLKMEM*           blkmem,             /**< block memory buffers */
6161    SCIP_SET*             set                 /**< global SCIP settings */
6162    )
6163 {
6164    assert(nlp != NULL);
6165 
6166    if( nlp->indiving )
6167    {
6168       SCIPerrorMessage("NLP is already in diving mode\n");
6169       return SCIP_ERROR;
6170    }
6171 
6172    if( nlp->solver == NULL )
6173    {
6174       /* In diving mode we do not cache changes but put them directly in the NLPI problem, which does not exist if there is no solver.
6175        * So we forbid diving of no solver is available. */
6176       SCIPerrorMessage("Cannot start diving if no NLP solver is available\n");
6177       return SCIP_ERROR;
6178    }
6179 
6180    SCIP_CALL( SCIPnlpFlush(nlp, blkmem, set) );
6181 
6182    nlp->indiving = TRUE;
6183 
6184    return SCIP_OKAY;
6185 }
6186 
6187 /** resets the bound and objective changes made during diving and disables diving mode */
SCIPnlpEndDive(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set)6188 SCIP_RETCODE SCIPnlpEndDive(
6189    SCIP_NLP*             nlp,                /**< current NLP data */
6190    BMS_BLKMEM*           blkmem,             /**< block memory */
6191    SCIP_SET*             set                 /**< global SCIP settings */
6192    )
6193 {
6194    int i;
6195    int* varidx;
6196    SCIP_Real* varlb;
6197    SCIP_Real* varub;
6198 
6199    assert(nlp != NULL);
6200    assert(set != NULL);
6201    assert(nlp->nvars == nlp->nvars_solver);
6202 
6203    if( !nlp->indiving )
6204    {
6205       SCIPerrorMessage("NLP not in diving mode, cannot end dive\n");
6206       return SCIP_ERROR;
6207    }
6208 
6209    assert(nlp->solver != NULL);
6210    assert(nlp->problem != NULL);
6211 
6212    /* reset variable bounds in NLPI problem to their current values */
6213    SCIP_CALL( SCIPsetAllocBufferArray(set, &varidx, nlp->nvars) );
6214    SCIP_CALL( SCIPsetAllocBufferArray(set, &varlb,  nlp->nvars) );
6215    SCIP_CALL( SCIPsetAllocBufferArray(set, &varub,  nlp->nvars) );
6216    for( i = 0; i < nlp->nvars; ++i )
6217    {
6218       varidx[i] = i;
6219       varlb[i] = SCIPvarGetLbLocal(nlp->vars[nlp->varmap_nlpi2nlp[i]]);
6220       varub[i] = SCIPvarGetUbLocal(nlp->vars[nlp->varmap_nlpi2nlp[i]]);
6221    }
6222 
6223    SCIP_CALL( SCIPnlpiChgVarBounds(nlp->solver, nlp->problem, nlp->nvars, varidx, varlb, varub) );
6224 
6225    SCIPsetFreeBufferArray(set, &varidx);
6226    SCIPsetFreeBufferArray(set, &varlb);
6227    SCIPsetFreeBufferArray(set, &varub);
6228 
6229    /* clear diving objective, if one was used (i.e., if SCIPnlpChgVarObjDive had been called)
6230     * the objective in the NLPI will be reset in the next flush */
6231    if( nlp->divingobj != NULL )
6232    {
6233       SCIP_CALL( SCIPnlrowRelease(&nlp->divingobj, blkmem, set) );
6234       assert(nlp->divingobj == NULL);
6235       assert(nlp->objflushed == FALSE);
6236    }
6237 
6238    /* we do not have a valid solution anymore */
6239    nlp->solstat  = SCIP_NLPSOLSTAT_UNKNOWN;
6240    nlp->termstat = SCIP_NLPTERMSTAT_OTHER;
6241    nlp->primalsolobjval = SCIP_INVALID;
6242 
6243    nlp->indiving = FALSE;
6244 
6245    return SCIP_OKAY;
6246 }
6247 
6248 /** changes coefficient of variable in diving NLP */
SCIPnlpChgVarObjDive(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_STAT * stat,SCIP_VAR * var,SCIP_Real coef)6249 SCIP_RETCODE SCIPnlpChgVarObjDive(
6250    SCIP_NLP*             nlp,                /**< current NLP data */
6251    BMS_BLKMEM*           blkmem,             /**< block memory */
6252    SCIP_SET*             set,                /**< global SCIP settings */
6253    SCIP_STAT*            stat,               /**< problem statistics data */
6254    SCIP_VAR*             var,                /**< variable which coefficient to change */
6255    SCIP_Real             coef                /**< new linear coefficient of variable in objective */
6256    )
6257 {
6258    int pos;
6259    int objidx;
6260 
6261    assert(nlp != NULL);
6262    assert(var != NULL);
6263    assert(SCIPhashmapExists(nlp->varhash, var));
6264    assert(nlp->indiving);
6265    assert(nlp->solver != NULL);
6266    assert(nlp->problem != NULL);
6267 
6268    /* get position of variable in NLPI problem */
6269    pos = SCIPhashmapGetImageInt(nlp->varhash, var);
6270    pos = nlp->varmap_nlp2nlpi[pos];
6271    assert(pos >= 0);
6272 
6273    /* set coefficient in NLPI problem objective */
6274    objidx = -1;
6275    SCIP_CALL( SCIPnlpiChgLinearCoefs(nlp->solver, nlp->problem, objidx, 1, &pos, &coef) );
6276 
6277    /* create an nlrow that holds the diving objective, if not done yet */
6278    if( nlp->divingobj == NULL )
6279    {
6280       SCIP_Real* coefs;
6281       int        i;
6282 
6283       SCIP_CALL( SCIPsetAllocBufferArray(set, &coefs, nlp->nvars) );
6284       for( i = 0; i < nlp->nvars; ++i )
6285          coefs[i] = SCIPvarGetObj(nlp->vars[i]);
6286 
6287       SCIP_CALL( SCIPnlrowCreate(&nlp->divingobj, blkmem, set, "divingobj",
6288             0.0,
6289             nlp->nvars, nlp->vars, coefs,
6290             0, NULL, 0, NULL,
6291             NULL,
6292             -SCIPsetInfinity(set), SCIPsetInfinity(set),
6293             SCIP_EXPRCURV_LINEAR) );
6294 
6295       SCIPsetFreeBufferArray(set, &coefs);
6296    }
6297    assert(nlp->divingobj != NULL);
6298 
6299    /* modify coefficient in diving objective */
6300    SCIP_CALL( SCIPnlrowChgLinearCoef(nlp->divingobj, blkmem, set, stat, nlp, var, coef) );
6301 
6302    /* remember that we have to store objective after diving ended */
6303    nlp->objflushed = FALSE;
6304 
6305    return SCIP_OKAY;
6306 }
6307 
6308 /** changes bounds of variable in diving NLP */
SCIPnlpChgVarBoundsDive(SCIP_NLP * nlp,SCIP_VAR * var,SCIP_Real lb,SCIP_Real ub)6309 SCIP_RETCODE SCIPnlpChgVarBoundsDive(
6310    SCIP_NLP*             nlp,                /**< current NLP data */
6311    SCIP_VAR*             var,                /**< variable which coefficient to change */
6312    SCIP_Real             lb,                 /**< new lower bound of variable */
6313    SCIP_Real             ub                  /**< new upper bound of variable */
6314    )
6315 {
6316    int pos;
6317 
6318    assert(nlp != NULL);
6319    assert(var != NULL);
6320    assert(SCIPhashmapExists(nlp->varhash, var));
6321    assert(nlp->indiving);
6322    assert(nlp->solver != NULL);
6323    assert(nlp->problem != NULL);
6324 
6325    /* get position of variable in NLPI problem */
6326    pos = SCIPhashmapGetImageInt(nlp->varhash, var);
6327    pos = nlp->varmap_nlp2nlpi[pos];
6328    assert(pos >= 0);
6329 
6330    /* set new bounds in NLPI */
6331    SCIP_CALL( SCIPnlpiChgVarBounds(nlp->solver, nlp->problem, 1, &pos, &lb, &ub) );
6332 
6333    return SCIP_OKAY;
6334 }
6335 
6336 /** changes bounds of a set of variables in diving NLP */
SCIPnlpChgVarsBoundsDive(SCIP_NLP * nlp,SCIP_SET * set,int nvars,SCIP_VAR ** vars,SCIP_Real * lbs,SCIP_Real * ubs)6337 SCIP_RETCODE SCIPnlpChgVarsBoundsDive(
6338    SCIP_NLP*             nlp,                /**< current NLP data */
6339    SCIP_SET*             set,                /**< global SCIP settings */
6340    int                   nvars,              /**< number of variables which bounds to change */
6341    SCIP_VAR**            vars,               /**< variables which bounds to change */
6342    SCIP_Real*            lbs,                /**< new lower bounds of variables */
6343    SCIP_Real*            ubs                 /**< new upper bounds of variables */
6344    )
6345 {
6346    int i;
6347    int* poss;
6348 
6349    assert(nlp  != NULL);
6350    assert(vars != NULL || nvars == 0);
6351    assert(nlp->indiving);
6352    assert(lbs  != NULL || nvars == 0);
6353    assert(ubs  != NULL || nvars == 0);
6354    assert(nlp->solver != NULL);
6355    assert(nlp->problem != NULL);
6356 
6357    if( nvars == 0 )
6358       return SCIP_OKAY;
6359 
6360    SCIP_CALL( SCIPsetAllocBufferArray(set, &poss, nvars) );
6361 
6362    for( i = 0; i < nvars; ++i )
6363    {
6364       assert(SCIPhashmapExists(nlp->varhash, vars[i]));  /*lint !e613*/
6365 
6366       /* get position of variable in NLPI problem */
6367       poss[i] = SCIPhashmapGetImageInt(nlp->varhash, vars[i]);   /*lint !e613*/
6368       poss[i] = nlp->varmap_nlp2nlpi[poss[i]];
6369       assert(poss[i] >= 0);
6370    }
6371 
6372    /* set new bounds in NLPI */
6373    SCIP_CALL( SCIPnlpiChgVarBounds(nlp->solver, nlp->problem, nvars, poss, lbs, ubs) );
6374 
6375    SCIPsetFreeBufferArray(set, &poss);
6376 
6377    return SCIP_OKAY;
6378 }
6379 
6380 /** returns whether the objective function has been changed during diving */
SCIPnlpIsDivingObjChanged(SCIP_NLP * nlp)6381 SCIP_Bool SCIPnlpIsDivingObjChanged(
6382    SCIP_NLP*             nlp                 /**< current NLP data */
6383    )
6384 {
6385    return nlp->divingobj != NULL;
6386 }
6387 
6388 /** solves diving NLP */
SCIPnlpSolveDive(SCIP_NLP * nlp,BMS_BLKMEM * blkmem,SCIP_SET * set,SCIP_MESSAGEHDLR * messagehdlr,SCIP_STAT * stat)6389 SCIP_RETCODE SCIPnlpSolveDive(
6390    SCIP_NLP*             nlp,                /**< current NLP data */
6391    BMS_BLKMEM*           blkmem,             /**< block memory buffers */
6392    SCIP_SET*             set,                /**< global SCIP settings */
6393    SCIP_MESSAGEHDLR*     messagehdlr,        /**< message handler */
6394    SCIP_STAT*            stat                /**< problem statistics */
6395    )
6396 {
6397    SCIP_CALL( nlpSolve(nlp, blkmem, set, messagehdlr, stat) );
6398 
6399    return SCIP_OKAY;
6400 }
6401 
6402