Scippy

SCIP

Solving Constraint Integer Programs

cons_sos2.c
Go to the documentation of this file.
1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program and library */
4 /* SCIP --- Solving Constraint Integer Programs */
5 /* */
6 /* Copyright (C) 2002-2014 Konrad-Zuse-Zentrum */
7 /* fuer Informationstechnik Berlin */
8 /* */
9 /* SCIP is distributed under the terms of the ZIB Academic License. */
10 /* */
11 /* You should have received a copy of the ZIB Academic License */
12 /* along with SCIP; see the file COPYING. If not email to scip@zib.de. */
13 /* */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15 
16 /**@file cons_sos2.c
17  * @brief constraint handler for SOS type 2 constraints
18  * @author Marc Pfetsch
19  *
20  * A specially ordered set of type 2 (SOS2) is a sequence of variables such that at most two
21  * variables are nonzero and if two variables are nonzero they must be adjacent in the specified
22  * sequence. Note that it is in principle allowed that a variable appears twice, but it then can be
23  * fixed to 0 if it is at least two apart in the sequence.
24  *
25  * This constraint is useful when considering a piecewise affine approximation of a univariate
26  * (nonlinear) function \f$: [a,b] \rightarrow R\f$: Let \f$x_1 < \ldots < x_n\f$ be points in
27  * \f$[a,b]\f$ and introduce variables \f$\lambda_1, \ldots, \lambda_n\f$. To evaluate \f$f(x')\f$
28  * at some point \f$x' \in [a,b]\f$ one can use the following constraints:
29  * \f[
30  * \lambda_1 + \cdots + \lambda_n = 1,\quad x' = x_1 \lambda_1 + \cdots + x_n \lambda_n.
31  * \f]
32  * The value of \f$f(x')\f$ can the be approximated as
33  * \f[
34  * f(x_1) \lambda_1 + \cdots + f(x_n) \lambda_n.
35  * \f]
36  * To get a valid piecewise affine approximation, \f$\lambda_1, \ldots, \lambda_n\f$ have to obey an
37  * SOS constraint of type 2.
38  *
39  * This implementation of this constraint handler is based on classical ideas, see e.g.@n
40  * "Special Facilities in General Mathematical Programming System for
41  * Non-Convex Problems Using Ordered Sets of Variables"@n
42  * E. Beale and J. Tomlin, Proc. 5th IFORS Conference, 447-454 (1970)
43  *
44  * The order of the variables is determined as follows:
45  *
46  * - If the constraint is created with SCIPcreateConsSOS2() and weights are given, the weights
47  * determine the order (decreasing weights). Additional variables can be added with
48  * SCIPaddVarSOS2(), which adds a variable with given weight.
49  *
50  * - If an empty constraint is created and then variables are added with SCIPaddVarSOS2(), weights
51  * are needed and stored.
52  *
53  * - All other calls ignore the weights, i.e., if a nonempty constraint is created or variables are
54  * added with SCIPappendVarSOS2().
55  *
56  * @todo Allow to adapt the order of the constraints, e.g. by priorities. This for instance
57  * determines the branching order.
58  * @todo Separate the following cuts for each pair of variables x, y of at least distance 2 in the
59  * SOS2 constraint: \f$ \min \{l_x, l_y\} \leq x + y \leq \max \{u_x, u_y\}\f$, where \f$l_x, u_x,
60  * l_y, u_y\f$ are the lower and upper bounds of x and y, respectively.
61  * @todo Possibly allow to generate local cuts via strengthened local cuts (would affect lhs/rhs of rows)
62  */
63 
64 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
65 
66 #include <assert.h>
67 
68 #include "scip/cons_sos2.h"
69 #include "scip/cons_linear.h"
70 #include "scip/pub_misc.h"
71 #include <string.h>
72 #include <ctype.h>
73 
74 
75 /* constraint handler properties */
76 #define CONSHDLR_NAME "SOS2"
77 #define CONSHDLR_DESC "SOS2 constraint handler"
78 #define CONSHDLR_SEPAPRIORITY 10 /**< priority of the constraint handler for separation */
79 #define CONSHDLR_ENFOPRIORITY 100 /**< priority of the constraint handler for constraint enforcing */
80 #define CONSHDLR_CHECKPRIORITY -10 /**< priority of the constraint handler for checking feasibility */
81 #define CONSHDLR_SEPAFREQ 0 /**< frequency for separating cuts; zero means to separate only in the root node */
82 #define CONSHDLR_PROPFREQ 1 /**< frequency for propagating domains; zero means only preprocessing propagation */
83 #define CONSHDLR_EAGERFREQ 100 /**< frequency for using all instead of only the useful constraints in separation,
84  * propagation and enforcement, -1 for no eager evaluations, 0 for first only */
85 #define CONSHDLR_MAXPREROUNDS -1 /**< maximal number of presolving rounds the constraint handler participates in (-1: no limit) */
86 #define CONSHDLR_DELAYSEPA FALSE /**< should separation method be delayed, if other separators found cuts? */
87 #define CONSHDLR_DELAYPROP FALSE /**< should propagation method be delayed, if other propagators found reductions? */
88 #define CONSHDLR_DELAYPRESOL FALSE /**< should presolving method be delayed, if other presolvers found reductions? */
89 #define CONSHDLR_NEEDSCONS TRUE /**< should the constraint handler be skipped, if no constraints are available? */
90 
91 #define CONSHDLR_PROP_TIMING SCIP_PROPTIMING_BEFORELP
92 
93 /* event handler properties */
94 #define EVENTHDLR_NAME "SOS2"
95 #define EVENTHDLR_DESC "bound change event handler for SOS2 constraints"
96 
97 
98 /** constraint data for SOS2 constraints */
99 struct SCIP_ConsData
100 {
101  int nvars; /**< number of variables in the constraint */
102  int maxvars; /**< maximal number of variables (= size of storage) */
103  int nfixednonzeros; /**< number of variables fixed to be nonzero */
104  SCIP_VAR** vars; /**< variables in constraint */
105  SCIP_ROW* row; /**< row corresponding to upper and lower bound inequalities, or NULL if not yet created */
106  SCIP_Real* weights; /**< weights determining the order (ascending), or NULL if not used */
107 };
108 
109 /** SOS2 constraint handler data */
110 struct SCIP_ConshdlrData
111 {
112  SCIP_EVENTHDLR* eventhdlr; /**< event handler for bound change events */
113 };
114 
115 
116 /** fix variable in given node to 0 or add constraint if variable is multi-aggregated */
117 static
119  SCIP* scip, /**< SCIP pointer */
120  SCIP_VAR* var, /**< variable to be fixed to 0*/
121  SCIP_NODE* node, /**< node */
122  SCIP_Bool* infeasible /**< if fixing is infeasible */
123  )
124 {
125  /* if variable cannot be nonzero */
126  *infeasible = FALSE;
128  {
129  *infeasible = TRUE;
130  return SCIP_OKAY;
131  }
132 
133  /* if variable is multi-aggregated */
135  {
136  SCIP_CONS* cons;
137  SCIP_Real val;
138 
139  val = 1.0;
140 
141  if ( ! SCIPisFeasZero(scip, SCIPvarGetLbLocal(var)) || ! SCIPisFeasZero(scip, SCIPvarGetUbLocal(var)) )
142  {
143  SCIPdebugMessage("creating constraint to force multi-aggregated variable <%s> to 0.\n", SCIPvarGetName(var));
144  /* we have to insert a local constraint var = 0 */
145  SCIP_CALL( SCIPcreateConsLinear(scip, &cons, "branch", 1, &var, &val, 0.0, 0.0, TRUE, TRUE, TRUE, TRUE, TRUE,
146  TRUE, FALSE, FALSE, FALSE, FALSE) );
147  SCIP_CALL( SCIPaddConsNode(scip, node, cons, NULL) );
148  SCIP_CALL( SCIPreleaseCons(scip, &cons) );
149  }
150  }
151  else
152  {
153  if ( ! SCIPisFeasZero(scip, SCIPvarGetLbLocal(var)) )
154  SCIP_CALL( SCIPchgVarLbNode(scip, node, var, 0.0) );
155  if ( ! SCIPisFeasZero(scip, SCIPvarGetUbLocal(var)) )
156  SCIP_CALL( SCIPchgVarUbNode(scip, node, var, 0.0) );
157  }
158 
159  return SCIP_OKAY;
160 }
161 
162 
163 /** fix variable in local node to 0, and return whether the operation was feasible
164  *
165  * @note We do not add a linear constraint if the variable is multi-aggregated as in
166  * fixVariableZeroNode(), since this would be too time consuming.
167  */
168 static
170  SCIP* scip, /**< SCIP pointer */
171  SCIP_VAR* var, /**< variable to be fixed to 0*/
172  SCIP_CONS* cons, /**< constraint */
173  int inferinfo, /**< info for reverse prop. */
174  SCIP_Bool* infeasible, /**< if fixing is infeasible */
175  SCIP_Bool* tightened, /**< if fixing was performed */
176  SCIP_Bool* success /**< whether fixing was successful, i.e., variable is not multi-aggregated */
177  )
178 {
179  *infeasible = FALSE;
180  *tightened = FALSE;
181  *success = FALSE;
182 
183  /* if variable cannot be nonzero */
185  {
186  *infeasible = TRUE;
187  return SCIP_OKAY;
188  }
189 
190  /* directly fix variable if it is not multi-aggregated, do nothing otherwise */
192  {
193  SCIP_Bool tighten;
194 
195  /* fix lower bound */
196  SCIP_CALL( SCIPinferVarLbCons(scip, var, 0.0, cons, inferinfo, FALSE, infeasible, &tighten) );
197  *tightened = *tightened || tighten;
198 
199  /* fix upper bound */
200  SCIP_CALL( SCIPinferVarUbCons(scip, var, 0.0, cons, inferinfo, FALSE, infeasible, &tighten) );
201  *tightened = *tightened || tighten;
202 
203  *success = TRUE;
204  }
205 
206  return SCIP_OKAY;
207 }
208 
209 
210 /** add lock on variable */
211 static
213  SCIP* scip, /**< SCIP data structure */
214  SCIP_CONS* cons, /**< constraint */
215  SCIP_VAR* var /**< variable */
216  )
217 {
218  assert( scip != NULL );
219  assert( cons != NULL );
220  assert( var != NULL );
221 
222  /* rounding down == bad if lb < 0, rounding up == bad if ub > 0 */
224 
225  return SCIP_OKAY;
226 }
227 
228 
229 /* remove lock on variable */
230 static
232  SCIP* scip, /**< SCIP data structure */
233  SCIP_CONS* cons, /**< constraint */
234  SCIP_VAR* var /**< variable */
235  )
236 {
237  assert( scip != NULL );
238  assert( cons != NULL );
239  assert( var != NULL );
240 
241  /* rounding down == bad if lb < 0, rounding up == bad if ub > 0 */
243 
244  return SCIP_OKAY;
245 }
246 
247 
248 /** ensures that the vars and weights array can store at least num entries */
249 static
251  SCIP* scip, /**< SCIP data structure */
252  SCIP_CONSDATA* consdata, /**< constraint data */
253  int num, /**< minimum number of entries to store */
254  SCIP_Bool reserveWeights /**< whether the weights array is handled */
255  )
256 {
257  assert( consdata != NULL );
258  assert( consdata->nvars <= consdata->maxvars );
259 
260  if ( num > consdata->maxvars )
261  {
262  int newsize;
263 
264  newsize = SCIPcalcMemGrowSize(scip, num);
265  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->vars, consdata->maxvars, newsize) );
266  if ( reserveWeights )
267  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->weights, consdata->maxvars, newsize) );
268  consdata->maxvars = newsize;
269  }
270  assert( num <= consdata->maxvars );
271 
272  return SCIP_OKAY;
273 }
274 
275 
276 /** handle new variable */
277 static
279  SCIP* scip, /**< SCIP data structure */
280  SCIP_CONS* cons, /**< constraint */
281  SCIP_CONSDATA* consdata, /**< constraint data */
282  SCIP_VAR* var, /**< variable */
283  SCIP_Bool transformed /**< whether original variable was transformed */
284  )
285 {
286  assert( scip != NULL );
287  assert( cons != NULL );
288  assert( consdata != NULL );
289  assert( var != NULL );
290 
291  /* if we are in transformed problem, catch the variable's events */
292  if ( transformed )
293  {
294  SCIP_CONSHDLR* conshdlr;
295  SCIP_CONSHDLRDATA* conshdlrdata;
296 
297  /* get event handler */
298  conshdlr = SCIPconsGetHdlr(cons);
299  conshdlrdata = SCIPconshdlrGetData(conshdlr);
300  assert( conshdlrdata != NULL );
301  assert( conshdlrdata->eventhdlr != NULL );
302 
303  /* catch bound change events of variable */
304  SCIP_CALL( SCIPcatchVarEvent(scip, var, SCIP_EVENTTYPE_BOUNDCHANGED, conshdlrdata->eventhdlr,
305  (SCIP_EVENTDATA*)consdata, NULL) );
306 
307  /* if the variable if fixed to nonzero */
308  assert( consdata->nfixednonzeros >= 0 );
310  ++consdata->nfixednonzeros;
311  }
312 
313  /* install the rounding locks for the new variable */
314  SCIP_CALL( lockVariableSOS2(scip, cons, var) );
315 
316  /* add the new coefficient to the LP row, if necessary */
317  if ( consdata->row != NULL )
318  {
319  /* this is currently dead code, since the constraint is not modifiable */
320  SCIP_CALL( SCIPaddVarToRow(scip, consdata->row, var, 1.0) );
321 
322  /* update lhs and rhs if necessary */
323  if ( SCIPisFeasGT(scip, SCIPvarGetUbLocal(var), SCIProwGetRhs(consdata->row)) )
324  SCIP_CALL( SCIPchgRowRhs(scip, consdata->row, SCIPvarGetUbLocal(var) ) );
325  if ( SCIPisFeasLT(scip, SCIPvarGetLbLocal(var), SCIProwGetLhs(consdata->row)) )
326  SCIP_CALL( SCIPchgRowLhs(scip, consdata->row, SCIPvarGetLbLocal(var) ) );
327  }
328 
329  return SCIP_OKAY;
330 }
331 
332 
333 /** adds a variable to an SOS2 constraint, a position given by weight - ascending order */
334 static
336  SCIP* scip, /**< SCIP data structure */
337  SCIP_CONS* cons, /**< constraint */
338  SCIP_VAR* var, /**< variable to add to the constraint */
339  SCIP_Real weight /**< weight to determine position */
340  )
341 {
342  SCIP_CONSDATA* consdata;
343  SCIP_Bool transformed;
344  int j;
345  int pos;
346 
347  assert( var != NULL );
348  assert( cons != NULL );
349 
350  consdata = SCIPconsGetData(cons);
351  assert( consdata != NULL );
352 
353  if ( consdata->weights == NULL && consdata->maxvars > 0 )
354  {
355  SCIPerrorMessage("cannot add variable to SOS2 constraint <%s> that does not contain weights.\n", SCIPconsGetName(cons));
356  return SCIP_INVALIDCALL;
357  }
358 
359  /* are we in the transformed problem? */
360  transformed = SCIPconsIsTransformed(cons);
361 
362  /* always use transformed variables in transformed constraints */
363  if ( transformed )
364  {
365  SCIP_CALL( SCIPgetTransformedVar(scip, var, &var) );
366  }
367  assert( var != NULL );
368  assert( transformed == SCIPvarIsTransformed(var) );
369 
370  SCIP_CALL( consdataEnsurevarsSizeSOS2(scip, consdata, consdata->nvars + 1, TRUE) );
371  assert( consdata->weights != NULL );
372  assert( consdata->maxvars >= consdata->nvars+1 );
373 
374  /* find variable position */
375  for (pos = 0; pos < consdata->nvars; ++pos)
376  {
377  if ( consdata->weights[pos] > weight )
378  break;
379  }
380  assert( 0 <= pos && pos <= consdata->nvars );
381 
382  /* move other variables, if necessary */
383  for (j = consdata->nvars; j > pos; --j)
384  {
385  consdata->vars[j] = consdata->vars[j-1];
386  consdata->weights[j] = consdata->weights[j-1];
387  }
388 
389  /* insert variable */
390  consdata->vars[pos] = var;
391  consdata->weights[pos] = weight;
392  ++consdata->nvars;
393 
394  /* handle the new variable */
395  SCIP_CALL( handleNewVariableSOS2(scip, cons, consdata, var, transformed) );
396 
397  return SCIP_OKAY;
398 }
399 
400 
401 /** appends a variable to an SOS2 constraint */
402 static
404  SCIP* scip, /**< SCIP data structure */
405  SCIP_CONS* cons, /**< constraint */
406  SCIP_VAR* var /**< variable to add to the constraint */
407  )
408 {
409  SCIP_CONSDATA* consdata;
410  SCIP_Bool transformed;
411 
412  assert( var != NULL );
413  assert( cons != NULL );
414 
415  consdata = SCIPconsGetData(cons);
416  assert( consdata != NULL );
417 
418  /* are we in the transformed problem? */
419  transformed = SCIPconsIsTransformed(cons);
420 
421  /* always use transformed variables in transformed constraints */
422  if ( transformed )
423  {
424  SCIP_CALL( SCIPgetTransformedVar(scip, var, &var) );
425  }
426  assert( var != NULL );
427  assert( transformed == SCIPvarIsTransformed(var) );
428 
429  SCIP_CALL( consdataEnsurevarsSizeSOS2(scip, consdata, consdata->nvars + 1, FALSE) );
430 
431  /* insert variable */
432  consdata->vars[consdata->nvars] = var;
433  assert( consdata->weights != NULL || consdata->nvars > 0 );
434  if ( consdata->weights != NULL && consdata->nvars > 0 )
435  consdata->weights[consdata->nvars] = consdata->weights[consdata->nvars-1] + 1.0;
436  ++consdata->nvars;
437 
438  /* handle the new variable */
439  SCIP_CALL( handleNewVariableSOS2(scip, cons, consdata, var, transformed) );
440 
441  return SCIP_OKAY;
442 }
443 
444 
445 /** deletes a variable of an SOS2 constraint */
446 static
448  SCIP* scip, /**< SCIP data structure */
449  SCIP_CONS* cons, /**< constraint */
450  SCIP_CONSDATA* consdata, /**< constraint data */
451  SCIP_EVENTHDLR* eventhdlr, /**< corresponding event handler */
452  int pos /**< position of variable in array */
453  )
454 {
455  int j;
456 
457  assert( 0 <= pos && pos < consdata->nvars );
458 
459  /* remove lock of variable */
460  SCIP_CALL( unlockVariableSOS2(scip, cons, consdata->vars[pos]) );
461 
462  /* drop events on variable */
463  SCIP_CALL( SCIPdropVarEvent(scip, consdata->vars[pos], SCIP_EVENTTYPE_BOUNDCHANGED, eventhdlr, (SCIP_EVENTDATA*)consdata, -1) );
464 
465  /* delete variable - need to copy since order is important */
466  for (j = pos; j < consdata->nvars-1; ++j)
467  {
468  consdata->vars[j] = consdata->vars[j+1];
469  if ( consdata->weights != NULL )
470  consdata->weights[j] = consdata->weights[j+1];
471  }
472  --consdata->nvars;
473 
474  return SCIP_OKAY;
475 }
476 
477 
478 /** perform one presolving round
479  *
480  * We perform the following presolving steps.
481  *
482  * - If the bounds of one variable force it to be nonzero, we can fix all other variables with distance at least two to
483  * zero. If two variables are certain to be nonzero, we can fix all other variables to 0 and remove the constraint.
484  * - All variables fixed to zero, that are at the beginning or end of the constraint can be removed.
485  * - We substitute appregated variables.
486  * - If a constraint has at most two variables, we delete it.
487  *
488  * We currently do not handle the following:
489  *
490  * - If we have at least two variables fixed to zero next to each-other, that are positioned in the inner part of this
491  * constraint, we can delete all but one of these variables.
492  * - If a variable appears twice not next to each-other, it can be fixed to 0. If one variable appears next to
493  * each-other and is already certain to be nonzero, we can fix all variables.
494  * - If a binary variable and its negation appear in the constraint, we might fix variables to zero or can forbid a zero
495  * value for them.
496  * - When, after removing all zero "border" variables, a constraint with more than two variables has at most two
497  * variables that are not fixed to 0, only one of these can take a nonzero value, because these variables need to be
498  * the "border" variables of this constraint. The same holds if we have exactly three variables in one constraint and
499  * the middle variable is certain to be not zero. In both cases we can upgrade this constraint constraint to an sos1
500  * consisting only of the "border" variables. If these "border" variables are negations of each other, we can delete
501  * this constraint.
502  * - When, after removing all variables fixed to 0, that are possible, in a constraint each even positioned variable is
503  * fixed to 0, we can upgrade this constraint to an sos1 that holds all non-fixed variables.
504  * - Extract cliques for all odd and also for all even positioned binary variables
505  */
506 static
508  SCIP* scip, /**< SCIP pointer */
509  SCIP_CONS* cons, /**< constraint */
510  SCIP_CONSDATA* consdata, /**< constraint data */
511  SCIP_EVENTHDLR* eventhdlr, /**< event handler */
512  SCIP_Bool* cutoff, /**< whether a cutoff happened */
513  SCIP_Bool* success, /**< whether we performed a successful reduction */
514  int* ndelconss, /**< number of deleted constraints */
515  int* nfixedvars, /**< number of fixed variables */
516  int* nremovedvars /**< number of variables removed */
517  )
518 {
519  SCIP_VAR** vars;
520  SCIP_Bool infeasible;
521  SCIP_Bool fixed;
522  int nfixednonzeros;
523  int lastFixedNonzero;
524  int lastzero;
525  int localnremovedvars;
526  int oldnfixedvars;
527  int j;
528 
529  assert( scip != NULL );
530  assert( cons != NULL );
531  assert( consdata != NULL );
532  assert( eventhdlr != NULL );
533  assert( cutoff != NULL );
534  assert( success != NULL );
535  assert( ndelconss != NULL );
536  assert( nfixedvars != NULL );
537  assert( nremovedvars != NULL );
538 
539  *cutoff = FALSE;
540  *success = FALSE;
541 
542  SCIPdebugMessage("Presolving SOS2 constraint <%s>.\n", SCIPconsGetName(cons) );
543 
544  /* if the number of variables is at most 2 */
545  if( consdata->nvars <= 2 )
546  {
547  SCIPdebugMessage("Deleting constraint with <= 2 variables.\n");
548 
549  /* delete constraint */
550  assert( ! SCIPconsIsModifiable(cons) );
551  SCIP_CALL( SCIPdelCons(scip, cons) );
552  ++(*ndelconss);
553  *success = TRUE;
554 
555  return SCIP_OKAY;
556  }
557 
558  nfixednonzeros = 0;
559  lastFixedNonzero = -1;
560  vars = consdata->vars;
561  lastzero = consdata->nvars;
562  localnremovedvars = 0;
563 
564  /* check for variables fixed to 0 and bounds that guarantee a variable to be nonzero; downward loop is important */
565  for( j = consdata->nvars - 1; j >= 0; --j )
566  {
567  SCIP_VAR* var;
568  SCIP_Real lb;
569  SCIP_Real ub;
570  SCIP_Real scalar;
571  SCIP_Real constant;
572 
573  /* check that our vars array is still correct */
574  assert(vars == consdata->vars);
575 
576  scalar = 1.0;
577  constant = 0.0;
578 
579  /* check aggregation: if the constant is zero, the variable is zero iff the aggregated variable is 0 */
580  var = vars[j];
581  SCIP_CALL( SCIPgetProbvarSum(scip, &var, &scalar, &constant) );
582 
583  /* if constant is zero and we get a different variable, substitute variable */
584  if ( SCIPisZero(scip, constant) && ! SCIPisZero(scip, scalar) && var != vars[j] )
585  {
586  SCIPdebugMessage("substituted variable <%s> by <%s>.\n", SCIPvarGetName(vars[j]), SCIPvarGetName(var));
587  SCIP_CALL( SCIPdropVarEvent(scip, consdata->vars[j], SCIP_EVENTTYPE_BOUNDCHANGED, eventhdlr, (SCIP_EVENTDATA*)consdata, -1) );
588  SCIP_CALL( SCIPcatchVarEvent(scip, var, SCIP_EVENTTYPE_BOUNDCHANGED, eventhdlr, (SCIP_EVENTDATA*)consdata, NULL) );
589 
590  /* change the rounding locks */
591  SCIP_CALL( unlockVariableSOS2(scip, cons, consdata->vars[j]) );
592  SCIP_CALL( lockVariableSOS2(scip, cons, var) );
593 
594  vars[j] = var;
595  }
596 
597  /* get bounds */
598  lb = SCIPvarGetLbLocal(vars[j]);
599  ub = SCIPvarGetUbLocal(vars[j]);
600 
601  /* if the variable if fixed to nonzero */
602  if ( SCIPisFeasPositive(scip, lb) || SCIPisFeasNegative(scip, ub) )
603  {
604  ++nfixednonzeros;
605 
606  /* two variables certain to be nonzero which are not next to each other, so we are infeasible */
607  if( lastFixedNonzero != -1 && lastFixedNonzero != j + 1 )
608  {
609  SCIPdebugMessage("The problem is infeasible: two non-consecutive variables have bounds that keep them from being 0.\n");
610  *cutoff = TRUE;
611  return SCIP_OKAY;
612  }
613 
614  /* if more than two variables are fixed to be nonzero, we are infeasible */
615  if( nfixednonzeros > 2 )
616  {
617  SCIPdebugMessage("The problem is infeasible: more than two variables have bounds that keep them from being 0.\n");
618  *cutoff = TRUE;
619  return SCIP_OKAY;
620  }
621 
622  if( lastFixedNonzero == -1)
623  lastFixedNonzero = j;
624  }
625 
626  /* if the variable is fixed to 0 we may delete it from our constraint */
627  if( SCIPisFeasZero(scip, lb) && SCIPisFeasZero(scip, ub) )
628  {
629  /* all rear variables fixed to 0 can be deleted */
630  if( j == consdata->nvars - 1 )
631  {
632  ++(*nremovedvars);
633 
634  SCIPdebugMessage("deleting variable <%s> fixed to 0.\n", SCIPvarGetName(vars[j]));
635  SCIP_CALL( deleteVarSOS2(scip, cons, consdata, eventhdlr, j) );
636 
637  *success = TRUE;
638  }
639  /* remember position of last variable for which all up front and this one are fixed to 0 */
640  else if( lastzero > j + 1 )
641  lastzero = j;
642  }
643  else
644  lastzero = consdata->nvars;
645  }
646 
647  /* check that our vars array is still correct */
648  assert(vars == consdata->vars);
649 
650  /* remove first "lastzero" many variables, that are already fixed to 0 */
651  if( lastzero < consdata->nvars )
652  {
653  assert(lastzero >= 0);
654 
655  for( j = lastzero; j >= 0; --j )
656  {
657  /* the variables should all be fixed to zero */
658  assert(SCIPisFeasZero(scip, SCIPvarGetLbGlobal(vars[j])) && SCIPisFeasZero(scip, SCIPvarGetUbGlobal(vars[j])));
659 
660  SCIPdebugMessage("deleting variable <%s> fixed to 0.\n", SCIPvarGetName(vars[j]));
661  SCIP_CALL( deleteVarSOS2(scip, cons, consdata, eventhdlr, j) );
662  }
663  localnremovedvars += (lastzero + 1);
664  *success = TRUE;
665  }
666 
667  /* check that our variable array is still correct */
668  assert(vars == consdata->vars);
669 
670  *nremovedvars += localnremovedvars;
671 
672  /* we might need to correct the position of the first variable which is certain to be not zero */
673  if( lastFixedNonzero >= 0 )
674  {
675  lastFixedNonzero -= localnremovedvars;
676  assert(0 <= lastFixedNonzero && lastFixedNonzero < consdata->nvars);
677  assert(SCIPisFeasPositive(scip, SCIPvarGetLbGlobal(vars[lastFixedNonzero])) || SCIPisFeasNegative(scip, SCIPvarGetUbGlobal(vars[lastFixedNonzero])));
678  }
679 
680  /* if the number of variables is at most 2 */
681  if( consdata->nvars <= 2 )
682  {
683  SCIPdebugMessage("Deleting constraint with <= 2 variables.\n");
684 
685  /* delete constraint */
686  assert( ! SCIPconsIsModifiable(cons) );
687  SCIP_CALL( SCIPdelCons(scip, cons) );
688  ++(*ndelconss);
689  *success = TRUE;
690 
691  return SCIP_OKAY;
692  }
693 
694  oldnfixedvars = *nfixedvars;
695 
696  /* if there is exactly one fixed nonzero variable */
697  if ( nfixednonzeros == 1 )
698  {
699  assert(0 <= lastFixedNonzero && lastFixedNonzero < consdata->nvars);
700  assert(SCIPisFeasPositive(scip, SCIPvarGetLbGlobal(vars[lastFixedNonzero])) ||
701  SCIPisFeasNegative(scip, SCIPvarGetUbGlobal(vars[lastFixedNonzero])));
702 
703  /* fix all other variables with distance two to zero */
704  for( j = 0; j < lastFixedNonzero - 1; ++j )
705  {
706  SCIPdebugMessage("fixing variable <%s> to 0.\n", SCIPvarGetName(vars[j]));
707  SCIP_CALL( SCIPfixVar(scip, vars[j], 0.0, &infeasible, &fixed) );
708 
709  if( infeasible )
710  {
711  *cutoff = TRUE;
712  return SCIP_OKAY;
713  }
714 
715  if ( fixed )
716  ++(*nfixedvars);
717  }
718  for( j = lastFixedNonzero + 2; j < consdata->nvars; ++j )
719  {
720  SCIPdebugMessage("fixing variable <%s> to 0.\n", SCIPvarGetName(vars[j]));
721  SCIP_CALL( SCIPfixVar(scip, vars[j], 0.0, &infeasible, &fixed) );
722 
723  if( infeasible )
724  {
725  *cutoff = TRUE;
726  return SCIP_OKAY;
727  }
728 
729  if ( fixed )
730  ++(*nfixedvars);
731  }
732 
733  if( *nfixedvars > oldnfixedvars )
734  *success = TRUE;
735  }
736  /* if there are exactly two fixed nonzero variables */
737  else if ( nfixednonzeros == 2 )
738  {
739  assert(0 < lastFixedNonzero && lastFixedNonzero < consdata->nvars);
740  assert(SCIPisFeasPositive(scip, SCIPvarGetLbGlobal(vars[lastFixedNonzero])) ||
741  SCIPisFeasNegative(scip, SCIPvarGetUbGlobal(vars[lastFixedNonzero])));
742  /* the previous variable need also to be nonzero, otherwise the infeasibility should have been detected earlier */
743  assert(SCIPisFeasPositive(scip, SCIPvarGetLbGlobal(vars[lastFixedNonzero - 1])) ||
744  SCIPisFeasNegative(scip, SCIPvarGetUbGlobal(vars[lastFixedNonzero - 1])));
745 
746  /* fix all variables before lastFixedNonzero to zero */
747  for( j = 0; j < lastFixedNonzero - 1; ++j )
748  {
749  SCIPdebugMessage("fixing variable <%s> to 0.\n", SCIPvarGetName(vars[j]));
750  SCIP_CALL( SCIPfixVar(scip, vars[j], 0.0, &infeasible, &fixed) );
751 
752  if( infeasible )
753  {
754  *cutoff = TRUE;
755  return SCIP_OKAY;
756  }
757  if ( fixed )
758  ++(*nfixedvars);
759  }
760  /* fix all variables after lastFixedNonzero + 1 to zero */
761  for( j = lastFixedNonzero + 1; j < consdata->nvars; ++j )
762  {
763  SCIPdebugMessage("fixing variable <%s> to 0.\n", SCIPvarGetName(vars[j]));
764  SCIP_CALL( SCIPfixVar(scip, vars[j], 0.0, &infeasible, &fixed) );
765 
766  if( infeasible )
767  {
768  *cutoff = TRUE;
769  return SCIP_OKAY;
770  }
771  if ( fixed )
772  ++(*nfixedvars);
773  }
774 
775  /* delete constraint */
776  assert( ! SCIPconsIsModifiable(cons) );
777  SCIP_CALL( SCIPdelCons(scip, cons) );
778  ++(*ndelconss);
779  *success = TRUE;
780  }
781 
782  return SCIP_OKAY;
783 }
784 
785 
786 /** propagate variables */
787 static
789  SCIP* scip, /**< SCIP pointer */
790  SCIP_CONS* cons, /**< constraint */
791  SCIP_CONSDATA* consdata, /**< constraint data */
792  SCIP_Bool* cutoff, /**< whether a cutoff happened */
793  int* nGen /**< number of domain changes */
794  )
795 {
796  assert( scip != NULL );
797  assert( cons != NULL );
798  assert( consdata != NULL );
799  assert( cutoff != NULL );
800  assert( nGen != NULL );
801 
802  *cutoff = FALSE;
803 
804  /* if more than two variables are fixed to be nonzero */
805  if ( consdata->nfixednonzeros > 2 )
806  {
807  SCIPdebugMessage("the node is infeasible, more than 2 variables are fixed to be nonzero.\n");
808  SCIP_CALL( SCIPresetConsAge(scip, cons) );
809  *cutoff = TRUE;
810  return SCIP_OKAY;
811  }
812 
813  /* if exactly one variable is fixed to be nonzero */
814  if ( consdata->nfixednonzeros == 1 )
815  {
816  SCIP_VAR** vars;
817  SCIP_Bool infeasible;
818  SCIP_Bool tightened;
819  SCIP_Bool success;
820  int firstFixedNonzero;
821  int nvars;
822  int j;
823 
824  firstFixedNonzero = -1;
825  nvars = consdata->nvars;
826  vars = consdata->vars;
827  assert( vars != NULL );
828 
829  /* search nonzero variable */
830  for (j = 0; j < nvars; ++j)
831  {
832  if ( SCIPisFeasPositive(scip, SCIPvarGetLbLocal(vars[j])) || SCIPisFeasNegative(scip, SCIPvarGetUbLocal(vars[j])) )
833  {
834  firstFixedNonzero = j;
835  break;
836  }
837  }
838  assert( firstFixedNonzero >= 0 );
839 
840  SCIPdebugMessage("variable <%s> is nonzero, fixing variables with distance at least 2 to 0.\n", SCIPvarGetName(vars[firstFixedNonzero]));
841 
842  /* fix variables before firstFixedNonzero-1 to 0 */
843  for (j = 0; j < firstFixedNonzero-1; ++j)
844  {
845  /* fix variable */
846  SCIP_CALL( inferVariableZero(scip, vars[j], cons, firstFixedNonzero, &infeasible, &tightened, &success) );
847  assert( ! infeasible );
848 
849  if ( tightened )
850  ++(*nGen);
851  }
852 
853  /* fix variables after firstFixedNonzero+1 to 0 */
854  for (j = firstFixedNonzero+2; j < nvars; ++j)
855  {
856  /* fix variable */
857  SCIP_CALL( inferVariableZero(scip, vars[j], cons, firstFixedNonzero, &infeasible, &tightened, &success) );
858 
859  /* no variable after firstFixedNonzero+1 should be fixed to be nonzero */
860  if ( infeasible )
861  {
862  assert( SCIPisFeasPositive(scip, SCIPvarGetLbLocal(vars[j])) || SCIPisFeasNegative(scip, SCIPvarGetUbLocal(vars[j])) );
863  SCIPdebugMessage("the node is infeasible: variable <%s> is fixed nonzero and variable <%s> with distance at least 2 as well.\n",
864  SCIPvarGetName(vars[firstFixedNonzero]), SCIPvarGetName(vars[j]));
865  *cutoff = TRUE;
866  return SCIP_OKAY;
867  }
868 
869  if ( tightened )
870  ++(*nGen);
871  }
872  /* cannot locally delete constraint, since position of second entry is not fixed! */
873  }
874  /* if exactly two variables are fixed to be nonzero */
875  else if ( consdata->nfixednonzeros == 2 )
876  {
877  SCIP_VAR** vars;
878  SCIP_Bool infeasible;
879  SCIP_Bool tightened;
880  SCIP_Bool success;
881  SCIP_Bool allVarFixed;
882  int firstFixedNonzero;
883  int nvars;
884  int j;
885 
886  firstFixedNonzero = -1;
887  nvars = consdata->nvars;
888  vars = consdata->vars;
889  assert( vars != NULL );
890 
891  /* search nonzero variable */
892  for (j = 0; j < nvars; ++j)
893  {
894  if ( SCIPisFeasPositive(scip, SCIPvarGetLbLocal(vars[j])) || SCIPisFeasNegative(scip, SCIPvarGetUbLocal(vars[j])) )
895  {
896  firstFixedNonzero = j;
897  break;
898  }
899  }
900  assert( 0 <= firstFixedNonzero && firstFixedNonzero < nvars-1 );
901 
902  SCIPdebugMessage("variables <%s> and <%s> are fixed nonzero, fixing other variables to 0.\n", SCIPvarGetName(vars[firstFixedNonzero]),
903  SCIPvarGetName(vars[firstFixedNonzero+1]));
904 
905  /* fix variables before firstFixedNonzero to 0 */
906  allVarFixed = TRUE;
907  for (j = 0; j < firstFixedNonzero; ++j)
908  {
909  /* fix variable */
910  SCIP_CALL( inferVariableZero(scip, vars[j], cons, firstFixedNonzero+1, &infeasible, &tightened, &success) );
911  assert( ! infeasible );
912  allVarFixed = allVarFixed && success;
913  if ( tightened )
914  ++(*nGen);
915  }
916 
917  /* fix variables after firstFixedNonzero+1 to 0 */
918  for (j = firstFixedNonzero+2; j < nvars; ++j)
919  {
920  /* fix variable */
921  SCIP_CALL( inferVariableZero(scip, vars[j], cons, firstFixedNonzero, &infeasible, &tightened, &success) );
922 
923  /* no variable after firstFixedNonzero+1 should be fixed to be nonzero */
924  if ( infeasible )
925  {
926  assert( SCIPisFeasPositive(scip, SCIPvarGetLbLocal(vars[j])) || SCIPisFeasNegative(scip, SCIPvarGetUbLocal(vars[j])) );
927  SCIPdebugMessage("the node is infeasible: variable <%s> is fixed nonzero and variable <%s> with distance at least 2 as well.\n",
928  SCIPvarGetName(vars[firstFixedNonzero]), SCIPvarGetName(vars[j]));
929  *cutoff = TRUE;
930  return SCIP_OKAY;
931  }
932  allVarFixed = allVarFixed && success;
933 
934  if ( tightened )
935  ++(*nGen);
936  }
937 
938  /* delete constraint locally, since the nonzero positions are fixed */
939  if ( allVarFixed )
940  {
941  SCIPdebugMessage("locally deleting constraint <%s>.\n", SCIPconsGetName(cons));
942  assert( !SCIPconsIsModifiable(cons) );
943  SCIP_CALL( SCIPdelConsLocal(scip, cons) );
944  }
945  }
946 
947  /* reset constraint age counter */
948  if ( *nGen > 0 )
949  SCIP_CALL( SCIPresetConsAge(scip, cons) );
950 
951  return SCIP_OKAY;
952 }
953 
954 
955 /** enforcement method
956  *
957  * We check whether the current solution is feasible, i.e., contains
958  * at most one nonzero variable. If not, we branch along the lines
959  * indicated by Beale and Tomlin:
960  *
961  * We first compute \f$W = \sum_{j=1}^n |x_i|\f$ and \f$w =
962  * \sum_{j=1}^n j\, |x_i|\f$. Then we search for the index \f$k\f$ that
963  * satisfies
964  * \f[
965  * k \leq \frac{w}{W} < k+1.
966  * \f]
967  * The branches are then
968  * \f[
969  * x_1 = 0, \ldots, x_{k-1} = 0 \qquad \mbox{and}\qquad
970  * x_{k+1} = 0, \ldots, x_n = 0.
971  * \f]
972  *
973  * There is one special case that we have to consider: It can happen
974  * that \f$k\f$ is one too small. Example: \f$x_1 = 1 - \epsilon, x_2
975  * = 0, x_3 = \epsilon\f$. Then \f$w = 1 - \epsilon + 3 \epsilon = 1
976  * + 2 \epsilon\f$. This yields \f$k = 1\f$ and hence the first
977  * branch does not change the solution. We therefore increase \f$k\f$
978  * by one if \f$x_k \neq 0\f$. This is valid, since we know that
979  * \f$x_{k+1} \neq 0\f$ (with respect to the original \f$k\f$); the
980  * corresponding branch will cut off the current solution, since
981  * \f$x_k \neq 0\f$.
982  */
983 static
985  SCIP* scip, /**< SCIP pointer */
986  SCIP_CONSHDLR* conshdlr, /**< constraint handler */
987  int nconss, /**< number of constraints */
988  SCIP_CONS** conss, /**< indicator constraints */
989  SCIP_RESULT* result /**< result */
990  )
991 {
992  SCIP_CONSDATA* consdata;
993  SCIP_Bool infeasible;
994  SCIP_NODE* node1;
995  SCIP_NODE* node2;
997  SCIP_VAR** vars;
998  SCIP_Real nodeselest;
999  SCIP_Real objest;
1000  int nvars;
1001  int maxNonzeros;
1002  int maxInd;
1003  int j;
1004  int c;
1005 
1006  assert( scip != NULL );
1007  assert( conshdlr != NULL );
1008  assert( conss != NULL );
1009  assert( result != NULL );
1010 
1011  maxNonzeros = 0;
1012  maxInd = -1;
1013 
1014  SCIPdebugMessage("Enforcing SOS2 constraints <%s>.\n", SCIPconshdlrGetName(conshdlr) );
1015  *result = SCIP_FEASIBLE;
1016 
1017  /* check each constraint */
1018  for (c = 0; c < nconss; ++c)
1019  {
1020  SCIP_CONS* cons;
1021  SCIP_Bool cutoff;
1022  SCIP_Real weight1;
1023  SCIP_Real weight2;
1024  SCIP_Real w;
1025  int lastNonzero;
1026  int nGen;
1027  int cnt;
1028  int ind;
1029 
1030  cons = conss[c];
1031  assert( cons != NULL );
1032 
1033  consdata = SCIPconsGetData(cons);
1034  assert( consdata != NULL );
1035 
1036  nvars = consdata->nvars;
1037  vars = consdata->vars;
1038 
1039  /* do nothing if there are not enough variables - this is usually eliminated by preprocessing */
1040  if ( nvars <= 2 )
1041  return SCIP_OKAY;
1042 
1043  nGen = 0;
1044 
1045  /* first perform propagation (it might happen that standard propagation is turned off) */
1046  SCIP_CALL( propSOS2(scip, cons, consdata, &cutoff, &nGen) );
1047  SCIPdebugMessage("propagating <%s> in enforcing (cutoff: %u, domain reductions: %d).\n", SCIPconsGetName(cons), cutoff, nGen);
1048  if ( cutoff )
1049  {
1050  *result = SCIP_CUTOFF;
1051  return SCIP_OKAY;
1052  }
1053  if ( nGen > 0 )
1054  {
1055  *result = SCIP_REDUCEDDOM;
1056  return SCIP_OKAY;
1057  }
1058 
1059  cnt = 0;
1060  weight1 = 0.0;
1061  weight2 = 0.0;
1062  lastNonzero = -1;
1063 
1064  /* compute weight */
1065  for (j = 0; j < nvars; ++j)
1066  {
1067  SCIP_Real val;
1068 
1069  val = REALABS(SCIPgetSolVal(scip, NULL, vars[j]));
1070  weight1 += val * (SCIP_Real) j;
1071  weight2 += val;
1072 
1073  if ( ! SCIPisFeasZero(scip, val) )
1074  {
1075  lastNonzero = j;
1076  ++cnt;
1077  }
1078  }
1079 
1080  /* if at most one variable is nonzero, the constraint is feasible */
1081  if ( cnt < 2 )
1082  continue;
1083 
1084  /* if two adjacent variables are nonzero */
1085  assert( 0 < lastNonzero && lastNonzero < nvars );
1086  if ( cnt == 2 && ! SCIPisFeasZero(scip, SCIPgetSolVal(scip, NULL, vars[lastNonzero-1])) )
1087  continue;
1088 
1089  assert( !SCIPisFeasZero(scip, weight2) );
1090  w = weight1/weight2; /*lint !e795*/
1091 
1092  ind = (int) SCIPfeasFloor(scip, w);
1093  assert( 0 <= ind && ind < nvars-1 );
1094 
1095  /* correct index if necessary - see above for an explanation */
1096  if ( ! SCIPisFeasZero(scip, SCIPgetSolVal(scip, NULL, vars[ind])) && ind < lastNonzero-1 )
1097  ++ind;
1098 
1099  /* check if the constraint has more nonzeros */
1100  if ( cnt > maxNonzeros )
1101  {
1102  maxNonzeros = cnt;
1103  branchCons = cons;
1104  maxInd = ind;
1105  }
1106  }
1107 
1108  /* if all constraints are feasible */
1109  if ( branchCons == NULL )
1110  {
1111  SCIPdebugMessage("All SOS2 constraints are feasible.\n");
1112  return SCIP_OKAY;
1113  }
1114 
1115  /* create branches */
1116  consdata = SCIPconsGetData(branchCons);
1117  assert( consdata != NULL );
1118  nvars = consdata->nvars;
1119  vars = consdata->vars;
1120 
1121  assert( 0 < maxInd && maxInd < nvars-1 );
1122 
1123  /* branch on variable ind: either all variables before ind or all variables after ind are zero */
1124  SCIPdebugMessage("Branching on variable <%s> in constraint <%s> (nonzeros: %d).\n", SCIPvarGetName(vars[maxInd]),
1125  SCIPconsGetName(branchCons), maxNonzeros);
1126 
1127  /* calculate node selection and objective estimate for node 1 */
1128  nodeselest = 0.0;
1129  objest = 0.0;
1130  for (j = 0; j < maxInd; ++j)
1131  {
1132  nodeselest += SCIPcalcNodeselPriority(scip, vars[j], SCIP_BRANCHDIR_DOWNWARDS, 0.0);
1133  objest += SCIPcalcChildEstimate(scip, vars[j], 0.0);
1134  }
1135  /* take the average of the individual estimates */
1136  objest = objest/((SCIP_Real) maxInd);
1137 
1138  /* create node 1 */
1139  SCIP_CALL( SCIPcreateChild(scip, &node1, nodeselest, objest) );
1140 
1141  for (j = 0; j < maxInd; ++j)
1142  {
1143  SCIP_CALL( fixVariableZeroNode(scip, vars[j], node1, &infeasible) );
1144  assert( ! infeasible );
1145  }
1146 
1147  /* calculate node selection and objective estimate for node 2 */
1148  nodeselest = 0.0;
1149  objest = 0.0;
1150  for (j = maxInd+1; j < nvars; ++j)
1151  {
1152  nodeselest += SCIPcalcNodeselPriority(scip, vars[j], SCIP_BRANCHDIR_DOWNWARDS, 0.0);
1153  objest += SCIPcalcChildEstimate(scip, vars[j], 0.0);
1154  }
1155  /* take the average of the individual estimates */
1156  objest = objest/((SCIP_Real) (nvars-maxInd-1));
1157 
1158  /* create node 2 */
1159  SCIP_CALL( SCIPcreateChild(scip, &node2, nodeselest, objest) );
1160  for (j = maxInd+1; j < nvars; ++j)
1161  {
1162  SCIP_CALL( fixVariableZeroNode(scip, vars[j], node2, &infeasible) );
1163  assert( ! infeasible );
1164  }
1165  SCIP_CALL( SCIPresetConsAge(scip, branchCons) );
1166  *result = SCIP_BRANCHED;
1167 
1168  return SCIP_OKAY;
1169 }
1170 
1171 
1172 /** Generate basic row
1173  *
1174  * We generate the row corresponding to the following simple valid
1175  * inequalities. Let \f$U\f$ and \f$U'\f$ be the largest and second
1176  * largest upper bound of variables appearing in the
1177  * constraint. Similarly let \f$L\f$ and \f$L'\f$ be the smallest and
1178  * second smallest lower bound. The inequalities are:
1179  * \f[
1180  * x_1 + \ldots + x_n \leq U + U' \qquad\mbox{and}\qquad
1181  * x_1 + \ldots + x_n \geq L + L'.
1182  * \f]
1183  * Of course, these inequalities are only added if the upper and
1184  * lower bounds are all finite and \f$L+L' < 0\f$ or \f$U+U' > 0\f$.
1185  */
1186 static
1188  SCIP* scip, /**< SCIP pointer */
1189  SCIP_CONSHDLR* conshdlr, /**< constraint handler */
1190  SCIP_CONS* cons, /**< constraint */
1191  SCIP_Bool local /**< produce local cut? */
1192  )
1193 {
1194  char name[SCIP_MAXSTRLEN];
1195  SCIP_CONSDATA* consdata;
1196  SCIP_VAR** vars;
1197  SCIP_Real minLb = SCIPinfinity(scip);
1198  SCIP_Real minLb2 = SCIPinfinity(scip);
1199  SCIP_Real maxUb = -SCIPinfinity(scip);
1200  SCIP_Real maxUb2 = -SCIPinfinity(scip);
1201  SCIP_Real lhs;
1202  SCIP_Real rhs;
1203  SCIP_ROW* row;
1204  int nvars;
1205  int j;
1206 
1207  assert( scip != NULL );
1208  assert( conshdlr != NULL );
1209  assert( cons != NULL );
1210 
1211  consdata = SCIPconsGetData(cons);
1212  assert( consdata != NULL );
1213  assert( consdata->row == NULL );
1214 
1215  nvars = consdata->nvars;
1216  vars = consdata->vars;
1217  assert( vars != NULL );
1218 
1219  /* find minimum and maximum lower and upper bounds */
1220  for (j = 0; j < nvars; ++j)
1221  {
1222  SCIP_Real val;
1223 
1224  if ( local )
1225  val = SCIPvarGetLbLocal(vars[j]);
1226  else
1227  val = SCIPvarGetLbGlobal(vars[j]);
1228 
1229  if ( val < minLb )
1230  {
1231  minLb2 = minLb;
1232  minLb = val;
1233  }
1234  else
1235  {
1236  if ( val < minLb2 )
1237  minLb2 = val;
1238  }
1239 
1240  if ( local )
1241  val = SCIPvarGetUbLocal(vars[j]);
1242  else
1243  val = SCIPvarGetUbGlobal(vars[j]);
1244 
1245  if ( val > maxUb )
1246  {
1247  maxUb2 = maxUb;
1248  maxUb = val;
1249  }
1250  else
1251  {
1252  if ( val > maxUb2 )
1253  maxUb2 = val;
1254  }
1255  }
1256  lhs = minLb + minLb2;
1257  rhs = maxUb + maxUb2;
1258 
1259  /* ignore trivial inequality if left hand side would be 0 */
1260  if ( SCIPisFeasZero(scip, lhs) )
1261  lhs = -SCIPinfinity(scip);
1262 
1263  /* ignore trivial inequality if right hand side would be 0 */
1264  if ( SCIPisFeasZero(scip, rhs) )
1265  rhs = SCIPinfinity(scip);
1266 
1267  /* create upper and lower bound inequality if one of the bounds is finite */
1268  if ( ! SCIPisInfinity(scip, REALABS(lhs)) || ! SCIPisInfinity(scip, REALABS(rhs)) )
1269  {
1270  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "sos2bnd#%s", SCIPconsGetName(cons));
1271  SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, conshdlr, name, lhs, rhs, local, FALSE, FALSE) );
1272  SCIP_CALL( SCIPaddVarsToRowSameCoef(scip, row, nvars, vars, 1.0) );
1273  consdata->row = row;
1274 
1275  SCIPdebug( SCIP_CALL( SCIPprintRow(scip, row, NULL) ) );
1276  }
1277 
1278  return SCIP_OKAY;
1279 }
1280 
1281 
1282 /* ---------------------------- constraint handler callback methods ----------------------*/
1283 
1284 /** copy method for constraint handler plugins (called when SCIP copies plugins) */
1285 static
1286 SCIP_DECL_CONSHDLRCOPY(conshdlrCopySOS2)
1287 { /*lint --e{715}*/
1288  assert( scip != NULL );
1289  assert( conshdlr != NULL );
1290  assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
1291 
1292  /* call inclusion method of constraint handler */
1294 
1295  *valid = TRUE;
1296 
1297  return SCIP_OKAY;
1298 }
1299 
1300 
1301 /** destructor of constraint handler to free constraint handler data (called when SCIP is exiting) */
1302 static
1303 SCIP_DECL_CONSFREE(consFreeSOS2)
1304 {
1305  SCIP_CONSHDLRDATA* conshdlrdata;
1306 
1307  assert( scip != NULL );
1308  assert( conshdlr != NULL );
1309  assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
1310 
1311  conshdlrdata = SCIPconshdlrGetData(conshdlr);
1312  assert(conshdlrdata != NULL);
1313 
1314  SCIPfreeMemory(scip, &conshdlrdata);
1315 
1316  return SCIP_OKAY;
1317 }
1318 
1319 
1320 /** solving process deinitialization method of constraint handler (called before branch and bound process data is freed) */
1321 static
1322 SCIP_DECL_CONSEXITSOL(consExitsolSOS2)
1323 { /*lint --e{715}*/
1324  int c;
1325 
1326  assert( scip != NULL );
1327  assert( conshdlr != NULL );
1328  assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
1329 
1330  /* check each constraint */
1331  for (c = 0; c < nconss; ++c)
1332  {
1333  SCIP_CONSDATA* consdata;
1334 
1335  assert( conss != NULL );
1336  assert( conss[c] != NULL );
1337  consdata = SCIPconsGetData(conss[c]);
1338  assert( consdata != NULL );
1339 
1340  SCIPdebugMessage("Exiting SOS2 constraint <%s>.\n", SCIPconsGetName(conss[c]) );
1341 
1342  /* free row */
1343  if ( consdata->row != NULL )
1344  {
1345  SCIP_CALL( SCIPreleaseRow(scip, &consdata->row) );
1346  }
1347  }
1348  return SCIP_OKAY;
1349 }
1350 
1351 
1352 /** frees specific constraint data */
1353 static
1354 SCIP_DECL_CONSDELETE(consDeleteSOS2)
1355 {
1356  assert( scip != NULL );
1357  assert( conshdlr != NULL );
1358  assert( cons != NULL );
1359  assert( consdata != NULL );
1360  assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
1361 
1362  SCIPdebugMessage("Deleting SOS2 constraint <%s>.\n", SCIPconsGetName(cons) );
1363 
1364  /* drop events on transformed variables */
1365  if ( SCIPconsIsTransformed(cons) )
1366  {
1367  SCIP_CONSHDLRDATA* conshdlrdata;
1368  int j;
1369 
1370  /* get constraint handler data */
1371  conshdlrdata = SCIPconshdlrGetData(conshdlr);
1372  assert( conshdlrdata != NULL );
1373  assert( conshdlrdata->eventhdlr != NULL );
1374 
1375  for (j = 0; j < (*consdata)->nvars; ++j)
1376  {
1377  SCIP_CALL( SCIPdropVarEvent(scip, (*consdata)->vars[j], SCIP_EVENTTYPE_BOUNDCHANGED, conshdlrdata->eventhdlr,
1378  (SCIP_EVENTDATA*)*consdata, -1) );
1379  }
1380  }
1381 
1382  SCIPfreeBlockMemoryArray(scip, &(*consdata)->vars, (*consdata)->maxvars);
1383  if ( (*consdata)->weights != NULL )
1384  {
1385  SCIPfreeBlockMemoryArray(scip, &(*consdata)->weights, (*consdata)->maxvars);
1386  }
1387 
1388  /* free row */
1389  if ( (*consdata)->row != NULL )
1390  {
1391  SCIP_CALL( SCIPreleaseRow(scip, &(*consdata)->row) );
1392  }
1393  assert( (*consdata)->row == NULL );
1394 
1395  SCIPfreeBlockMemory(scip, consdata);
1396 
1397  return SCIP_OKAY;
1398 }
1399 
1400 
1401 /** transforms constraint data into data belonging to the transformed problem */
1402 static
1403 SCIP_DECL_CONSTRANS(consTransSOS2)
1404 {
1405  SCIP_CONSDATA* consdata;
1406  SCIP_CONSHDLRDATA* conshdlrdata;
1407  SCIP_CONSDATA* sourcedata;
1408  char s[SCIP_MAXSTRLEN];
1409  int j;
1410 
1411  assert( scip != NULL );
1412  assert( conshdlr != NULL );
1413  assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
1414  assert( sourcecons != NULL );
1415  assert( targetcons != NULL );
1416 
1417  /* get constraint handler data */
1418  conshdlrdata = SCIPconshdlrGetData(conshdlr);
1419  assert( conshdlrdata != NULL );
1420  assert( conshdlrdata->eventhdlr != NULL );
1421 
1422  SCIPdebugMessage("Transforming SOS2 constraint: <%s>.\n", SCIPconsGetName(sourcecons) );
1423 
1424  /* get data of original constraint */
1425  sourcedata = SCIPconsGetData(sourcecons);
1426  assert( sourcedata != NULL );
1427  assert( sourcedata->nvars > 0 );
1428  assert( sourcedata->nvars <= sourcedata->maxvars );
1429 
1430  /* create constraint data */
1431  SCIP_CALL( SCIPallocBlockMemory(scip, &consdata) );
1432 
1433  consdata->nvars = sourcedata->nvars;
1434  consdata->maxvars = sourcedata->nvars;
1435  consdata->row = NULL;
1436  consdata->nfixednonzeros = 0;
1437  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->vars, consdata->nvars) );
1438 
1439  /* if weights were used */
1440  if ( sourcedata->weights != NULL )
1441  {
1442  SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &consdata->weights, sourcedata->weights, consdata->nvars) );
1443  }
1444  else
1445  consdata->weights = NULL;
1446 
1447  for (j = 0; j < sourcedata->nvars; ++j)
1448  {
1449  assert( sourcedata->vars[j] != 0 );
1450  SCIP_CALL( SCIPgetTransformedVar(scip, sourcedata->vars[j], &(consdata->vars[j])) );
1451 
1452  /* if variable is fixed to be nonzero */
1453  if ( SCIPisFeasPositive(scip, SCIPvarGetLbLocal(consdata->vars[j])) || SCIPisFeasNegative(scip, SCIPvarGetUbLocal(consdata->vars[j])) )
1454  ++(consdata->nfixednonzeros);
1455  }
1456 
1457  /* create transformed constraint with the same flags */
1458  (void) SCIPsnprintf(s, SCIP_MAXSTRLEN, "t_%s", SCIPconsGetName(sourcecons));
1459  SCIP_CALL( SCIPcreateCons(scip, targetcons, s, conshdlr, consdata,
1460  SCIPconsIsInitial(sourcecons), SCIPconsIsSeparated(sourcecons),
1461  SCIPconsIsEnforced(sourcecons), SCIPconsIsChecked(sourcecons),
1462  SCIPconsIsPropagated(sourcecons), SCIPconsIsLocal(sourcecons),
1463  SCIPconsIsModifiable(sourcecons), SCIPconsIsDynamic(sourcecons),
1464  SCIPconsIsRemovable(sourcecons), SCIPconsIsStickingAtNode(sourcecons)) );
1465 
1466  /* catch bound change events on variable */
1467  for (j = 0; j < consdata->nvars; ++j)
1468  {
1469  SCIP_CALL( SCIPcatchVarEvent(scip, consdata->vars[j], SCIP_EVENTTYPE_BOUNDCHANGED, conshdlrdata->eventhdlr,
1470  (SCIP_EVENTDATA*)consdata, NULL) );
1471  }
1472 
1473 #ifdef SCIP_DEBUG
1474  if ( consdata->nfixednonzeros > 0 )
1475  {
1476  SCIPdebugMessage("constraint <%s> has %d variables fixed to be nonzero.\n", SCIPconsGetName(*targetcons), consdata->nfixednonzeros );
1477  }
1478 #endif
1479 
1480  return SCIP_OKAY;
1481 }
1482 
1483 
1484 /** presolving method of constraint handler */
1485 static
1486 SCIP_DECL_CONSPRESOL(consPresolSOS2)
1487 { /*lint --e{715}*/
1488  int oldnfixedvars;
1489  int oldndelconss;
1490  int nremovedvars;
1491  SCIP_EVENTHDLR* eventhdlr;
1492  int c;
1493 
1494  assert( scip != NULL );
1495  assert( conshdlr != NULL );
1496  assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
1497  assert( result != NULL );
1498 
1499  *result = SCIP_DIDNOTRUN;
1500  oldnfixedvars = *nfixedvars;
1501  oldndelconss = *ndelconss;
1502  nremovedvars = 0;
1503 
1504  /* only run if success is possible */
1505  if( nrounds == 0 || nnewfixedvars > 0 || nnewaggrvars > 0 || nnewchgcoefs > 0 )
1506  {
1507  /* get constraint handler data */
1508  assert( SCIPconshdlrGetData(conshdlr) != NULL );
1509  eventhdlr = SCIPconshdlrGetData(conshdlr)->eventhdlr;
1510  assert( eventhdlr != NULL );
1511 
1512  *result = SCIP_DIDNOTFIND;
1513 
1514  /* check each constraint */
1515  for (c = 0; c < nconss; ++c)
1516  {
1517  SCIP_CONSDATA* consdata;
1518  SCIP_CONS* cons;
1519  SCIP_Bool cutoff;
1520  SCIP_Bool success;
1521 
1522  assert( conss != NULL );
1523  assert( conss[c] != NULL );
1524 
1525  cons = conss[c];
1526  consdata = SCIPconsGetData(cons);
1527 
1528  assert( consdata != NULL );
1529  assert( consdata->nvars >= 0 );
1530  assert( consdata->nvars <= consdata->maxvars );
1531  assert( ! SCIPconsIsModifiable(cons) );
1532 
1533  /* perform one presolving round */
1534  SCIP_CALL( presolRoundSOS2(scip, cons, consdata, eventhdlr, &cutoff, &success, ndelconss, nfixedvars, &nremovedvars) );
1535 
1536  if ( cutoff )
1537  {
1538  *result = SCIP_CUTOFF;
1539  return SCIP_OKAY;
1540  }
1541 
1542  if ( success )
1543  *result = SCIP_SUCCESS;
1544  }
1545  }
1546  (*nchgcoefs) += nremovedvars;
1547 
1548  SCIPdebugMessage("presolving fixed %d variables, removed %d variables, and deleted %d constraints.\n",
1549  *nfixedvars - oldnfixedvars, nremovedvars, *ndelconss - oldndelconss);
1550 
1551  return SCIP_OKAY;
1552 }
1553 
1554 
1555 /** LP initialization method of constraint handler (called before the initial LP relaxation at a node is solved) */
1556 static
1557 SCIP_DECL_CONSINITLP(consInitlpSOS2)
1558 {
1559  int c;
1560 
1561  assert( scip != NULL );
1562  assert( conshdlr != NULL );
1563  assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
1564 
1565  /* check each constraint */
1566  for (c = 0; c < nconss; ++c)
1567  {
1568  SCIP_CONSDATA* consdata;
1569 
1570  assert( conss != NULL );
1571  assert( conss[c] != NULL );
1572  consdata = SCIPconsGetData(conss[c]);
1573  assert( consdata != NULL );
1574 
1575  SCIPdebugMessage("Checking for initial rows for SOS2 constraint <%s>.\n", SCIPconsGetName(conss[c]) );
1576 
1577  /* possibly generate row if not yet done */
1578  if ( consdata->row == NULL )
1579  {
1580  SCIP_CALL( generateRowSOS2(scip, conshdlr, conss[c], FALSE) );
1581  }
1582 
1583  /* put corresponding rows into LP */
1584  if ( consdata->row != NULL && ! SCIProwIsInLP(consdata->row) )
1585  {
1586  SCIP_Bool infeasible;
1587 
1588  assert( ! SCIPisInfinity(scip, REALABS(SCIProwGetLhs(consdata->row))) || ! SCIPisInfinity(scip, REALABS(SCIProwGetRhs(consdata->row))) );
1589 
1590  SCIP_CALL( SCIPaddCut(scip, NULL, consdata->row, FALSE, &infeasible) );
1591  assert( ! infeasible );
1592  SCIPdebug( SCIP_CALL( SCIPprintRow(scip, consdata->row, NULL) ) );
1593  }
1594  }
1595 
1596  return SCIP_OKAY;
1597 }
1598 
1599 
1600 /** separation method of constraint handler for LP solutions */
1601 static
1602 SCIP_DECL_CONSSEPALP(consSepalpSOS2)
1603 { /*lint --e{715}*/
1604  SCIP_Bool cutoff = FALSE;
1605  int c;
1606  int nGen = 0;
1607 
1608  assert( scip != NULL );
1609  assert( conshdlr != NULL );
1610  assert( conss != NULL );
1611  assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
1612  assert( result != NULL );
1613 
1614  *result = SCIP_DIDNOTRUN;
1615 
1616  /* check each constraint */
1617  for (c = 0; c < nconss && ! cutoff; ++c)
1618  {
1619  SCIP_CONSDATA* consdata;
1620  SCIP_ROW* row;
1621 
1622  *result = SCIP_DIDNOTFIND;
1623  assert( conss[c] != NULL );
1624  consdata = SCIPconsGetData(conss[c]);
1625  assert( consdata != NULL );
1626  SCIPdebugMessage("Separating inequalities for SOS2 constraint <%s>.\n", SCIPconsGetName(conss[c]) );
1627 
1628  /* put corresponding rows into LP if they are useful */
1629  row = consdata->row;
1630 
1631  /* possibly generate row if not yet done */
1632  if ( row == NULL )
1633  {
1634  SCIP_CALL( generateRowSOS2(scip, conshdlr, conss[c], FALSE) );
1635  }
1636 
1637  /* possibly add row to LP if it is useful */
1638  if ( row != NULL && ! SCIProwIsInLP(row) && SCIPisCutEfficacious(scip, NULL, row) )
1639  {
1640  SCIP_CALL( SCIPaddCut(scip, NULL, row, FALSE, &cutoff) );
1641  SCIPdebug( SCIP_CALL( SCIPprintRow(scip, row, NULL) ) );
1642  SCIP_CALL( SCIPresetConsAge(scip, conss[c]) );
1643  ++nGen;
1644  }
1645  }
1646  SCIPdebugMessage("Separated %d SOS2 constraints.\n", nGen);
1647  if ( cutoff )
1648  *result = SCIP_CUTOFF;
1649  else if ( nGen > 0 )
1650  *result = SCIP_SEPARATED;
1651 
1652  return SCIP_OKAY;
1653 }
1654 
1655 
1656 /** separation method of constraint handler for arbitrary primal solutions */
1657 static
1658 SCIP_DECL_CONSSEPASOL(consSepasolSOS2)
1659 { /*lint --e{715}*/
1660  SCIP_Bool cutoff = FALSE;
1661  int c;
1662  int nGen = 0;
1663 
1664  assert( scip != NULL );
1665  assert( conshdlr != NULL );
1666  assert( conss != NULL );
1667  assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
1668  assert( result != NULL );
1669 
1670  *result = SCIP_DIDNOTRUN;
1671 
1672  /* check each constraint */
1673  for (c = 0; c < nconss && ! cutoff; ++c)
1674  {
1675  SCIP_CONSDATA* consdata;
1676  SCIP_ROW* row;
1677 
1678  *result = SCIP_DIDNOTFIND;
1679  assert( conss[c] != NULL );
1680  consdata = SCIPconsGetData(conss[c]);
1681  assert( consdata != NULL );
1682  SCIPdebugMessage("Separating solution for SOS2 constraint <%s>.\n", SCIPconsGetName(conss[c]) );
1683 
1684  /* put corresponding row into LP if it is useful */
1685  row = consdata->row;
1686 
1687  /* possibly generate row if not yet done */
1688  if ( row == NULL )
1689  {
1690  SCIP_CALL( generateRowSOS2(scip, conshdlr, conss[c], FALSE) );
1691  }
1692 
1693  /* possibly add row to LP if it is useful */
1694  if ( row != NULL && ! SCIProwIsInLP(row) && SCIPisCutEfficacious(scip, sol, row) )
1695  {
1696  SCIP_CALL( SCIPaddCut(scip, sol, row, FALSE, &cutoff) );
1697  SCIPdebug( SCIP_CALL( SCIPprintRow(scip, row, NULL) ) );
1698  SCIP_CALL( SCIPresetConsAge(scip, conss[c]) );
1699  ++nGen;
1700  }
1701  }
1702  SCIPdebugMessage("Separated %d SOS2 constraints.\n", nGen);
1703  if ( cutoff )
1704  *result = SCIP_CUTOFF;
1705  else if ( nGen > 0 )
1706  *result = SCIP_SEPARATED;
1707 
1708  return SCIP_OKAY;
1709 }
1710 
1711 
1712 /** constraint enforcing method of constraint handler for LP solutions */
1713 static
1714 SCIP_DECL_CONSENFOLP(consEnfolpSOS2)
1715 { /*lint --e{715}*/
1716  assert( scip != NULL );
1717  assert( conshdlr != NULL );
1718  assert( conss != NULL );
1719  assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
1720  assert( result != NULL );
1721 
1722  SCIP_CALL( enforceSOS2(scip, conshdlr, nconss, conss, result) );
1723 
1724  return SCIP_OKAY;
1725 }
1726 
1727 
1728 /** constraint enforcing method of constraint handler for pseudo solutions */
1729 static
1730 SCIP_DECL_CONSENFOPS(consEnfopsSOS2)
1731 { /*lint --e{715}*/
1732  assert( scip != NULL );
1733  assert( conshdlr != NULL );
1734  assert( conss != NULL );
1735  assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
1736  assert( result != NULL );
1737 
1738  SCIP_CALL( enforceSOS2(scip, conshdlr, nconss, conss, result) );
1739 
1740  return SCIP_OKAY;
1741 }
1742 
1743 
1744 /** feasibility check method of constraint handler for integral solutions
1745  *
1746  * We simply check whether at most two variable are nonzero and in the
1747  * case there are exactly two nonzero, then they have to be direct
1748  * neighbors in the given solution.
1749  */
1750 static
1751 SCIP_DECL_CONSCHECK(consCheckSOS2)
1752 { /*lint --e{715}*/
1753  int c;
1754 
1755  assert( scip != NULL );
1756  assert( conshdlr != NULL );
1757  assert( conss != NULL );
1758  assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
1759  assert( result != NULL );
1760 
1761  /* check each constraint */
1762  for (c = 0; c < nconss; ++c)
1763  {
1764  SCIP_CONSDATA* consdata;
1765  int firstNonzero;
1766  int j;
1767 
1768  firstNonzero = -1;
1769  assert( conss[c] != NULL );
1770  consdata = SCIPconsGetData(conss[c]);
1771  assert( consdata != NULL );
1772  SCIPdebugMessage("Checking SOS2 constraint <%s>.\n", SCIPconsGetName(conss[c]));
1773 
1774  /* check all variables */
1775  for (j = 0; j < consdata->nvars; ++j)
1776  {
1777  /* if variable is nonzero */
1778  if ( ! SCIPisFeasZero(scip, SCIPgetSolVal(scip, sol, consdata->vars[j])) )
1779  {
1780  if ( firstNonzero < 0 )
1781  firstNonzero = j;
1782  else
1783  {
1784  /* if we are more than one position away from the firstNonzero variable */
1785  if ( j > firstNonzero+1 )
1786  {
1787  SCIP_CALL( SCIPresetConsAge(scip, conss[c]) );
1788  *result = SCIP_INFEASIBLE;
1789 
1790  if ( printreason )
1791  {
1792  SCIP_CALL( SCIPprintCons(scip, conss[c], NULL) );
1793 
1794  SCIPinfoMessage(scip, NULL, ";\nviolation: <%s> = %.15g and <%s> = %.15g\n",
1795  SCIPvarGetName(consdata->vars[firstNonzero]),
1796  SCIPgetSolVal(scip, sol, consdata->vars[firstNonzero]),
1797  SCIPvarGetName(consdata->vars[j]),
1798  SCIPgetSolVal(scip, sol, consdata->vars[j]));
1799  }
1800 
1801  SCIPdebugMessage("SOS2 constraint <%s> infeasible.\n", SCIPconsGetName(conss[c]));
1802  return SCIP_OKAY;
1803  }
1804  }
1805  }
1806  }
1807  }
1808  SCIPdebugMessage("All SOS2 constraint are feasible.\n");
1809  *result = SCIP_FEASIBLE;
1810 
1811  return SCIP_OKAY;
1812 }
1813 
1814 
1815 /** domain propagation method of constraint handler */
1816 static
1817 SCIP_DECL_CONSPROP(consPropSOS2)
1818 { /*lint --e{715}*/
1819  int c;
1820  int nGen = 0;
1821 
1822  assert( scip != NULL );
1823  assert( conshdlr != NULL );
1824  assert( conss != NULL );
1825  assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
1826  assert( result != NULL );
1827  *result = SCIP_DIDNOTRUN;
1828 
1829  assert( SCIPisTransformed(scip) );
1830 
1831  /* check each constraint */
1832  for (c = 0; c < nconss; ++c)
1833  {
1834  SCIP_CONS* cons;
1835  SCIP_CONSDATA* consdata;
1836  SCIP_Bool cutoff;
1837 
1838  *result = SCIP_DIDNOTFIND;
1839  assert( conss[c] != NULL );
1840  cons = conss[c];
1841  consdata = SCIPconsGetData(cons);
1842  assert( consdata != NULL );
1843  SCIPdebugMessage("Propagating SOS2 constraint <%s>.\n", SCIPconsGetName(cons) );
1844 
1845  *result = SCIP_DIDNOTFIND;
1846  SCIP_CALL( propSOS2(scip, cons, consdata, &cutoff, &nGen) );
1847  if ( cutoff )
1848  {
1849  *result = SCIP_CUTOFF;
1850  return SCIP_OKAY;
1851  }
1852  }
1853  SCIPdebugMessage("Propagated %d domains.\n", nGen);
1854  if ( nGen > 0 )
1855  *result = SCIP_REDUCEDDOM;
1856 
1857  return SCIP_OKAY;
1858 }
1859 
1860 
1861 /** propagation conflict resolving method of constraint handler
1862  *
1863  * We check which bound changes were the reason for infeasibility. We
1864  * use that @a inferinfo stores the index of the variable that has
1865  * bounds that fix it to be nonzero (these bounds are the reason). */
1866 static
1867 SCIP_DECL_CONSRESPROP(consRespropSOS2)
1868 { /*lint --e{715}*/
1869  SCIP_CONSDATA* consdata;
1870  SCIP_VAR* var;
1871 
1872  assert( scip != NULL );
1873  assert( cons != NULL );
1874  assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
1875  assert( infervar != NULL );
1876  assert( bdchgidx != NULL );
1877  assert( result != NULL );
1878 
1879  *result = SCIP_DIDNOTFIND;
1880  SCIPdebugMessage("Propagation resolution method of SOS2 constraint <%s>.\n", SCIPconsGetName(cons));
1881 
1882  consdata = SCIPconsGetData(cons);
1883  assert( consdata != NULL );
1884  assert( 0 <= inferinfo && inferinfo < consdata->nvars );
1885  var = consdata->vars[inferinfo];
1886  assert( var != infervar );
1887 
1888  /* check if lower bound of var was the reason */
1889  if ( SCIPisFeasPositive(scip, SCIPvarGetLbAtIndex(var, bdchgidx, FALSE)) )
1890  {
1891  SCIP_CALL( SCIPaddConflictLb(scip, var, bdchgidx) );
1892  *result = SCIP_SUCCESS;
1893  }
1894 
1895  /* check if upper bound of var was the reason */
1896  if ( SCIPisFeasNegative(scip, SCIPvarGetUbAtIndex(var, bdchgidx, FALSE)) )
1897  {
1898  SCIP_CALL( SCIPaddConflictUb(scip, var, bdchgidx) );
1899  *result = SCIP_SUCCESS;
1900  }
1901 
1902  return SCIP_OKAY;
1903 }
1904 
1905 
1906 /** variable rounding lock method of constraint handler
1907  *
1908  * Let lb and ub be the lower and upper bounds of a
1909  * variable. Preprocessing usually makes sure that lb <= 0 <= ub.
1910  *
1911  * - If lb < 0 then rounding down may violate the constraint.
1912  * - If ub > 0 then rounding up may violated the constraint.
1913  * - If lb > 0 or ub < 0 then the constraint is infeasible and we do
1914  * not have to deal with it here.
1915  * - If lb == 0 then rounding down does not violate the constraint.
1916  * - If ub == 0 then rounding up does not violate the constraint.
1917  */
1918 static
1919 SCIP_DECL_CONSLOCK(consLockSOS2)
1920 {
1921  SCIP_CONSDATA* consdata;
1922  SCIP_VAR** vars;
1923  int nvars;
1924  int j;
1925 
1926  assert( scip != NULL );
1927  assert( conshdlr != NULL );
1928  assert( cons != NULL );
1929  assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
1930  consdata = SCIPconsGetData(cons);
1931  assert( consdata != NULL );
1932 
1933  SCIPdebugMessage("Locking constraint <%s>.\n", SCIPconsGetName(cons));
1934 
1935  vars = consdata->vars;
1936  nvars = consdata->nvars;
1937  assert( vars != NULL );
1938 
1939  for (j = 0; j < nvars; ++j)
1940  {
1941  SCIP_VAR* var;
1942  var = vars[j];
1943 
1944  /* if lower bound is negative, rounding down may violate constraint */
1945  if ( SCIPisFeasNegative(scip, SCIPvarGetLbLocal(var)) )
1946  SCIP_CALL( SCIPaddVarLocks(scip, var, nlockspos, nlocksneg) );
1947 
1948  /* additionally: if upper bound is positive, rounding up may violate constraint */
1949  if ( SCIPisFeasPositive(scip, SCIPvarGetUbLocal(var)) )
1950  SCIP_CALL( SCIPaddVarLocks(scip, var, nlocksneg, nlockspos) );
1951  }
1952 
1953  return SCIP_OKAY;
1954 }
1955 
1956 
1957 /** constraint display method of constraint handler */
1958 static
1959 SCIP_DECL_CONSPRINT(consPrintSOS2)
1960 { /*lint --e{715}*/
1961  SCIP_CONSDATA* consdata;
1962  int j;
1963 
1964  assert( scip != NULL );
1965  assert( conshdlr != NULL );
1966  assert( cons != NULL );
1967  assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
1968 
1969  consdata = SCIPconsGetData(cons);
1970  assert( consdata != NULL );
1971 
1972  for (j = 0; j < consdata->nvars; ++j)
1973  {
1974  if ( j > 0 )
1975  SCIPinfoMessage(scip, file, ", ");
1976  SCIP_CALL( SCIPwriteVarName(scip, file, consdata->vars[j], FALSE) );
1977  if ( consdata->weights == NULL )
1978  SCIPinfoMessage(scip, file, " (%d)", j+1);
1979  else
1980  SCIPinfoMessage(scip, file, " (%3.2f)", consdata->weights[j]);
1981  }
1982 
1983  return SCIP_OKAY;
1984 }
1985 
1986 
1987 /** constraint copying method of constraint handler */
1988 static
1989 SCIP_DECL_CONSCOPY(consCopySOS2)
1990 { /*lint --e{715}*/
1991  SCIP_CONSDATA* sourceconsdata;
1992  SCIP_VAR** sourcevars;
1993  SCIP_VAR** targetvars;
1994  SCIP_Real* sourceweights;
1995  SCIP_Real* targetweights;
1996  const char* consname;
1997  int nvars;
1998  int v;
1999 
2000  assert( scip != NULL );
2001  assert( sourcescip != NULL );
2002  assert( sourcecons != NULL );
2003  assert( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(sourcecons)), CONSHDLR_NAME) == 0 );
2004 
2005  *valid = TRUE;
2006 
2007  if ( name != NULL )
2008  consname = name;
2009  else
2010  consname = SCIPconsGetName(sourcecons);
2011 
2012  SCIPdebugMessage("Copying SOS2 constraint <%s> ...\n", consname);
2013 
2014  sourceconsdata = SCIPconsGetData(sourcecons);
2015  assert( sourceconsdata != NULL );
2016 
2017  /* get variables and weights of the source constraint */
2018  nvars = sourceconsdata->nvars;
2019 
2020  if ( nvars == 0 )
2021  return SCIP_OKAY;
2022 
2023  sourcevars = sourceconsdata->vars;
2024  assert( sourcevars != NULL );
2025  sourceweights = sourceconsdata->weights;
2026  assert( sourceweights != NULL );
2027 
2028  /* duplicate variable array */
2029  SCIP_CALL( SCIPallocBufferArray(sourcescip, &targetvars, nvars) );
2030  SCIP_CALL( SCIPduplicateBufferArray(sourcescip, &targetweights, sourceweights, nvars) );
2031 
2032  /* get copied variables in target SCIP */
2033  for( v = 0; v < nvars && *valid; ++v )
2034  {
2035  SCIP_CALL( SCIPgetVarCopy(sourcescip, scip, sourcevars[v], &(targetvars[v]), varmap, consmap, global, valid) );
2036  }
2037 
2038  /* only create the target constraint, if all variables could be copied */
2039  if( *valid )
2040  {
2041  SCIP_CALL( SCIPcreateConsSOS2(scip, cons, consname, nvars, targetvars, targetweights,
2042  initial, separate, enforce, check, propagate, local, dynamic, removable, stickingatnode) );
2043  }
2044 
2045  /* free buffer array */
2046  SCIPfreeBufferArray(sourcescip, &targetweights);
2047  SCIPfreeBufferArray(sourcescip, &targetvars);
2048 
2049  return SCIP_OKAY;
2050 }
2051 
2052 
2053 /** constraint parsing method of constraint handler */
2054 static
2055 SCIP_DECL_CONSPARSE(consParseSOS2)
2056 { /*lint --e{715}*/
2057  SCIP_VAR* var;
2058  SCIP_Real weight;
2059  const char* s;
2060  char* t;
2061 
2062  *success = TRUE;
2063  s = str;
2064 
2065  /* create empty SOS2 constraint */
2066  SCIP_CALL( SCIPcreateConsSOS2(scip, cons, name, 0, NULL, NULL, initial, separate, enforce, check, propagate, local, dynamic, removable, stickingatnode) );
2067 
2068  /* loop through string */
2069  do
2070  {
2071  /* parse variable name */
2072  SCIP_CALL( SCIPparseVarName(scip, s, &var, &t) );
2073  s = t;
2074 
2075  /* skip until beginning of weight */
2076  while ( *s != '\0' && *s != '(' )
2077  ++s;
2078 
2079  if ( *s == '\0' )
2080  {
2081  SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "Syntax error: expected weight at input: %s\n", s);
2082  *success = FALSE;
2083  return SCIP_OKAY;
2084  }
2085  /* skip '(' */
2086  ++s;
2087 
2088  /* find weight */
2089  weight = strtod(s, &t);
2090  if ( t == NULL )
2091  {
2092  SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "Syntax error during parsing of the weight: %s\n", s);
2093  *success = FALSE;
2094  return SCIP_OKAY;
2095  }
2096  s = t;
2097 
2098  /* skip white space, ',', and ')' */
2099  while ( *s != '\0' && ( isspace((unsigned char)*s) || *s == ',' || *s == ')' ) )
2100  ++s;
2101 
2102  /* add variable */
2103  SCIP_CALL( SCIPaddVarSOS2(scip, *cons, var, weight) );
2104  }
2105  while ( *s != '\0' );
2106 
2107  return SCIP_OKAY;
2108 }
2109 
2110 
2111 /** constraint method of constraint handler which returns the variables (if possible) */
2112 static
2113 SCIP_DECL_CONSGETVARS(consGetVarsSOS2)
2114 { /*lint --e{715}*/
2115  SCIP_CONSDATA* consdata;
2116 
2117  consdata = SCIPconsGetData(cons);
2118  assert(consdata != NULL);
2119 
2120  if( varssize < consdata->nvars )
2121  (*success) = FALSE;
2122  else
2123  {
2124  assert(vars != NULL);
2125 
2126  BMScopyMemoryArray(vars, consdata->vars, consdata->nvars);
2127  (*success) = TRUE;
2128  }
2129 
2130  return SCIP_OKAY;
2131 }
2132 
2133 
2134 /** constraint method of constraint handler which returns the number of variables (if possible) */
2135 static
2136 SCIP_DECL_CONSGETNVARS(consGetNVarsSOS2)
2137 { /*lint --e{715}*/
2138  SCIP_CONSDATA* consdata;
2139 
2140  consdata = SCIPconsGetData(cons);
2141  assert(consdata != NULL);
2142 
2143  (*nvars) = consdata->nvars;
2144  (*success) = TRUE;
2145 
2146  return SCIP_OKAY;
2147 }
2148 
2149 
2150 /* ---------------- Callback methods of event handler ---------------- */
2151 
2152 /* exec the event handler
2153  *
2154  * We update the number of variables fixed to be nonzero
2155  */
2156 static
2157 SCIP_DECL_EVENTEXEC(eventExecSOS2)
2158 {
2159  SCIP_EVENTTYPE eventtype;
2160  SCIP_CONSDATA* consdata;
2161  SCIP_Real oldbound, newbound;
2162 
2163  assert( eventhdlr != NULL );
2164  assert( eventdata != NULL );
2165  assert( strcmp(SCIPeventhdlrGetName(eventhdlr), EVENTHDLR_NAME) == 0 );
2166  assert( event != NULL );
2167 
2168  consdata = (SCIP_CONSDATA*)eventdata;
2169  assert( consdata != NULL );
2170  assert( 0 <= consdata->nfixednonzeros && consdata->nfixednonzeros <= consdata->nvars );
2171 
2172  oldbound = SCIPeventGetOldbound(event);
2173  newbound = SCIPeventGetNewbound(event);
2174 
2175  eventtype = SCIPeventGetType(event);
2176  switch ( eventtype )
2177  {
2179  /* if variable is now fixed to be nonzero */
2180  if ( ! SCIPisFeasPositive(scip, oldbound) && SCIPisFeasPositive(scip, newbound) )
2181  ++(consdata->nfixednonzeros);
2182  break;
2184  /* if variable is now fixed to be nonzero */
2185  if ( ! SCIPisFeasNegative(scip, oldbound) && SCIPisFeasNegative(scip, newbound) )
2186  ++(consdata->nfixednonzeros);
2187  break;
2189  /* if variable is not fixed to be nonzero anymore */
2190  if ( SCIPisFeasPositive(scip, oldbound) && ! SCIPisFeasPositive(scip, newbound) )
2191  --(consdata->nfixednonzeros);
2192  break;
2194  /* if variable is not fixed to be nonzero anymore */
2195  if ( SCIPisFeasNegative(scip, oldbound) && ! SCIPisFeasNegative(scip, newbound) )
2196  --(consdata->nfixednonzeros);
2197  break;
2198  default:
2199  SCIPerrorMessage("invalid event type.\n");
2200  return SCIP_INVALIDDATA;
2201  }
2202  assert( 0 <= consdata->nfixednonzeros && consdata->nfixednonzeros <= consdata->nvars );
2203 
2204  SCIPdebugMessage("changed bound of variable <%s> from %f to %f (nfixednonzeros: %d).\n", SCIPvarGetName(SCIPeventGetVar(event)),
2205  oldbound, newbound, consdata->nfixednonzeros);
2206 
2207  return SCIP_OKAY;
2208 }
2209 
2210 
2211 /* ---------------- Constraint specific interface methods ---------------- */
2212 
2213 /** creates the handler for SOS2 constraints and includes it in SCIP */
2215  SCIP* scip /**< SCIP data structure */
2216  )
2217 {
2218  SCIP_CONSHDLRDATA* conshdlrdata;
2219  SCIP_CONSHDLR* conshdlr;
2220 
2221  /* create constraint handler data */
2222  SCIP_CALL( SCIPallocMemory(scip, &conshdlrdata) );
2223 
2224  conshdlrdata->eventhdlr = NULL;
2225  /* create event handler for bound change events */
2226  SCIP_CALL( SCIPincludeEventhdlrBasic(scip, &(conshdlrdata->eventhdlr), EVENTHDLR_NAME, EVENTHDLR_DESC,
2227  eventExecSOS2, NULL) );
2228  if ( conshdlrdata->eventhdlr == NULL )
2229  {
2230  SCIPerrorMessage("event handler for SOS2 constraints not found.\n");
2231  return SCIP_PLUGINNOTFOUND;
2232  }
2233 
2234  /* include constraint handler */
2237  consEnfolpSOS2, consEnfopsSOS2, consCheckSOS2, consLockSOS2, conshdlrdata) );
2238  assert(conshdlr != NULL);
2239 
2240  /* set non-fundamental callbacks via specific setter functions */
2241  SCIP_CALL( SCIPsetConshdlrCopy(scip, conshdlr, conshdlrCopySOS2, consCopySOS2) );
2242  SCIP_CALL( SCIPsetConshdlrDelete(scip, conshdlr, consDeleteSOS2) );
2243  SCIP_CALL( SCIPsetConshdlrExitsol(scip, conshdlr, consExitsolSOS2) );
2244  SCIP_CALL( SCIPsetConshdlrFree(scip, conshdlr, consFreeSOS2) );
2245  SCIP_CALL( SCIPsetConshdlrGetVars(scip, conshdlr, consGetVarsSOS2) );
2246  SCIP_CALL( SCIPsetConshdlrGetNVars(scip, conshdlr, consGetNVarsSOS2) );
2247  SCIP_CALL( SCIPsetConshdlrInitlp(scip, conshdlr, consInitlpSOS2) );
2248  SCIP_CALL( SCIPsetConshdlrParse(scip, conshdlr, consParseSOS2) );
2249  SCIP_CALL( SCIPsetConshdlrPresol(scip, conshdlr, consPresolSOS2, CONSHDLR_MAXPREROUNDS, CONSHDLR_DELAYPRESOL) );
2250  SCIP_CALL( SCIPsetConshdlrPrint(scip, conshdlr, consPrintSOS2) );
2252  SCIP_CALL( SCIPsetConshdlrResprop(scip, conshdlr, consRespropSOS2) );
2253  SCIP_CALL( SCIPsetConshdlrSepa(scip, conshdlr, consSepalpSOS2, consSepasolSOS2, CONSHDLR_SEPAFREQ, CONSHDLR_SEPAPRIORITY, CONSHDLR_DELAYSEPA) );
2254  SCIP_CALL( SCIPsetConshdlrTrans(scip, conshdlr, consTransSOS2) );
2255 
2256  return SCIP_OKAY;
2257 }
2258 
2259 
2260 /** creates and captures a SOS2 constraint
2261  *
2262  * We set the constraint to not be modifable. If the weights are non
2263  * NULL, the variables are ordered according to these weights (in
2264  * ascending order).
2265  *
2266  * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
2267  */
2269  SCIP* scip, /**< SCIP data structure */
2270  SCIP_CONS** cons, /**< pointer to hold the created constraint */
2271  const char* name, /**< name of constraint */
2272  int nvars, /**< number of variables in the constraint */
2273  SCIP_VAR** vars, /**< array with variables of constraint entries */
2274  SCIP_Real* weights, /**< weights determining the variable order, or NULL if natural order should be used */
2275  SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP?
2276  * Usually set to TRUE. Set to FALSE for 'lazy constraints'. */
2277  SCIP_Bool separate, /**< should the constraint be separated during LP processing?
2278  * Usually set to TRUE. */
2279  SCIP_Bool enforce, /**< should the constraint be enforced during node processing?
2280  * TRUE for model constraints, FALSE for additional, redundant constraints. */
2281  SCIP_Bool check, /**< should the constraint be checked for feasibility?
2282  * TRUE for model constraints, FALSE for additional, redundant constraints. */
2283  SCIP_Bool propagate, /**< should the constraint be propagated during node processing?
2284  * Usually set to TRUE. */
2285  SCIP_Bool local, /**< is constraint only valid locally?
2286  * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */
2287  SCIP_Bool dynamic, /**< is constraint subject to aging?
2288  * Usually set to FALSE. Set to TRUE for own cuts which
2289  * are separated as constraints. */
2290  SCIP_Bool removable, /**< should the relaxation be removed from the LP due to aging or cleanup?
2291  * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */
2292  SCIP_Bool stickingatnode /**< should the constraint always be kept at the node where it was added, even
2293  * if it may be moved to a more global node?
2294  * Usually set to FALSE. Set to TRUE to for constraints that represent node data. */
2295  )
2296 {
2297  SCIP_CONSHDLR* conshdlr;
2298  SCIP_CONSDATA* consdata;
2299  SCIP_Bool modifiable;
2300 
2301  modifiable = FALSE;
2302 
2303  /* find the SOS2 constraint handler */
2304  conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
2305  if ( conshdlr == NULL )
2306  {
2307  SCIPerrorMessage("<%s> constraint handler not found\n", CONSHDLR_NAME);
2308  return SCIP_PLUGINNOTFOUND;
2309  }
2310 
2311  /* create constraint data */
2312  SCIP_CALL( SCIPallocBlockMemory(scip, &consdata) );
2313  consdata->vars = NULL;
2314  consdata->nvars = nvars;
2315  consdata->maxvars = nvars;
2316  consdata->row = NULL;
2317  consdata->nfixednonzeros = -1;
2318  consdata->weights = NULL;
2319  if ( nvars > 0 )
2320  {
2321  SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &consdata->vars, vars, nvars) );
2322 
2323  /* check weights */
2324  if ( weights != NULL )
2325  {
2326  /* store weights */
2327  SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &consdata->weights, weights, nvars) );
2328 
2329  /* sort variables - ascending order */
2330  SCIPsortRealPtr(consdata->weights, (void**)consdata->vars, nvars);
2331  }
2332  }
2333  else
2334  assert( weights == NULL );
2335 
2336  /* create constraint */
2337  SCIP_CALL( SCIPcreateCons(scip, cons, name, conshdlr, consdata, initial, separate, enforce, check, propagate,
2338  local, modifiable, dynamic, removable, stickingatnode) );
2339 
2340  return SCIP_OKAY;
2341 }
2342 
2343 
2344 /** creates and captures a SOS2 constraint with all constraint flags set to their default values.
2345  *
2346  * @warning Do NOT set the constraint to be modifiable manually, because this might lead
2347  * to wrong results as the variable array will not be resorted
2348  *
2349  * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
2350  */
2352  SCIP* scip, /**< SCIP data structure */
2353  SCIP_CONS** cons, /**< pointer to hold the created constraint */
2354  const char* name, /**< name of constraint */
2355  int nvars, /**< number of variables in the constraint */
2356  SCIP_VAR** vars, /**< array with variables of constraint entries */
2357  SCIP_Real* weights /**< weights determining the variable order, or NULL if natural order should be used */
2358  )
2359 {
2360  SCIP_CALL( SCIPcreateConsSOS2( scip, cons, name, nvars, vars, weights, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE) );
2361 
2362  return SCIP_OKAY;
2363 }
2364 
2365 
2366 /** adds variable to SOS2 constraint, the position is determined by the given weight */
2368  SCIP* scip, /**< SCIP data structure */
2369  SCIP_CONS* cons, /**< constraint */
2370  SCIP_VAR* var, /**< variable to add to the constraint */
2371  SCIP_Real weight /**< weight determining position of variable */
2372  )
2373 {
2374  assert( scip != NULL );
2375  assert( var != NULL );
2376  assert( cons != NULL );
2377 
2378  SCIPdebugMessage("adding variable <%s> to constraint <%s> with weight %g\n", SCIPvarGetName(var), SCIPconsGetName(cons), weight);
2379 
2380  if ( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
2381  {
2382  SCIPerrorMessage("constraint is not an SOS2 constraint.\n");
2383  return SCIP_INVALIDDATA;
2384  }
2385 
2386  SCIP_CALL( addVarSOS2(scip, cons, var, weight) );
2387 
2388  return SCIP_OKAY;
2389 }
2390 
2391 
2392 /** appends variable to SOS2 constraint */
2394  SCIP* scip, /**< SCIP data structure */
2395  SCIP_CONS* cons, /**< constraint */
2396  SCIP_VAR* var /**< variable to add to the constraint */
2397  )
2398 {
2399  assert( scip != NULL );
2400  assert( var != NULL );
2401  assert( cons != NULL );
2402 
2403  SCIPdebugMessage("appending variable <%s> to constraint <%s>\n", SCIPvarGetName(var), SCIPconsGetName(cons));
2404 
2405  if ( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
2406  {
2407  SCIPerrorMessage("constraint is not an SOS2 constraint.\n");
2408  return SCIP_INVALIDDATA;
2409  }
2410 
2411  SCIP_CALL( appendVarSOS2(scip, cons, var) );
2412 
2413  return SCIP_OKAY;
2414 }
2415 
2416 
2417 /** gets number of variables in SOS2 constraint */
2419  SCIP* scip, /**< SCIP data structure */
2420  SCIP_CONS* cons /**< constraint */
2421  )
2422 {
2423  SCIP_CONSDATA* consdata;
2424 
2425  assert( scip != NULL );
2426  assert( cons != NULL );
2427 
2428  if ( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
2429  {
2430  SCIPerrorMessage("constraint is not an SOS2 constraint.\n");
2431  SCIPABORT();
2432  return -1; /*lint !e527*/
2433  }
2434 
2435  consdata = SCIPconsGetData(cons);
2436  assert( consdata != NULL );
2437 
2438  return consdata->nvars;
2439 }
2440 
2441 
2442 /** gets array of variables in SOS2 constraint */
2444  SCIP* scip, /**< SCIP data structure */
2445  SCIP_CONS* cons /**< constraint data */
2446  )
2447 {
2448  SCIP_CONSDATA* consdata;
2449 
2450  assert( scip != NULL );
2451  assert( cons != NULL );
2452 
2453  if ( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
2454  {
2455  SCIPerrorMessage("constraint is not an SOS2 constraint.\n");
2456  SCIPABORT();
2457  return NULL; /*lint !e527*/
2458  }
2459 
2460  consdata = SCIPconsGetData(cons);
2461  assert( consdata != NULL );
2462 
2463  return consdata->vars;
2464 }
2465 
2466 
2467 /** gets array of weights in SOS2 constraint (or NULL if not existent) */
2469  SCIP* scip, /**< SCIP data structure */
2470  SCIP_CONS* cons /**< constraint data */
2471  )
2472 {
2473  SCIP_CONSDATA* consdata;
2474 
2475  assert( scip != NULL );
2476  assert( cons != NULL );
2477 
2478  if ( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
2479  {
2480  SCIPerrorMessage("constraint is not an SOS2 constraint.\n");
2481  SCIPABORT();
2482  return NULL; /*lint !e527*/
2483  }
2484 
2485  consdata = SCIPconsGetData(cons);
2486  assert( consdata != NULL );
2487 
2488  return consdata->weights;
2489 }
2490