Scippy

SCIP

Solving Constraint Integer Programs

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