1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program and library */
4 /* SCIP --- Solving Constraint Integer Programs */
5 /* */
6 /* Copyright (C) 2002-2022 Konrad-Zuse-Zentrum */
7 /* fuer Informationstechnik Berlin */
8 /* */
9 /* SCIP is distributed under the terms of the ZIB Academic License. */
10 /* */
11 /* You should have received a copy of the ZIB Academic License */
12 /* along with SCIP; see the file COPYING. If not visit scipopt.org. */
13 /* */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15
16 /**@file cons_indicator.c
17 * @ingroup DEFPLUGINS_CONS
18 * @brief constraint handler for indicator constraints
19 * @author Marc Pfetsch
20 *
21 * An indicator constraint is given by a binary variable \f$y\f$ and an inequality \f$ax \leq
22 * b\f$. It states that if \f$y = 1\f$ then \f$ax \leq b\f$ holds.
23 *
24 * This constraint is handled by adding a slack variable \f$s:\; ax - s \leq b\f$ with \f$s \geq
25 * 0\f$. The constraint is enforced by fixing \f$s\f$ to 0 if \f$y = 1\f$.
26 *
27 * @note The constraint only implements an implication not an equivalence, i.e., it does not ensure
28 * that \f$y = 1\f$ if \f$ax \leq b\f$ or equivalently if \f$s = 0\f$ holds.
29 *
30 * This constraint is equivalent to a linear constraint \f$ax - s \leq b\f$ and an SOS1 constraint on
31 * \f$y\f$ and \f$s\f$ (at most one should be nonzero). In the indicator context we can, however,
32 * separate more inequalities.
33 *
34 * The name indicator apparently comes from CPLEX.
35 *
36 *
37 * @section SEPARATION Separation Methods
38 *
39 * We now explain the handling of indicator constraints in more detail. The indicator constraint
40 * handler adds an inequality for each indicator constraint. We assume that this system (with added
41 * slack variables) is \f$ Ax - s \leq b \f$, where \f$ x \f$ are the original variables and \f$ s
42 * \f$ are the slack variables added by the indicator constraint. Variables \f$ y \f$ are the binary
43 * variables corresponding to the indicator constraints.
44 *
45 * @note In the implementation, we assume that bounds on the original variables \f$x\f$ cannot be
46 * influenced by the indicator constraint. If it should be possible to relax these constraints as
47 * well, then these constraints have to be added as indicator constraints.
48 *
49 * We separate inequalities by using the so-called alternative polyhedron.
50 *
51 *
52 * @section ALTERNATIVEPOLYHEDRON Separation via the Alternative Polyhedron
53 *
54 * We now describe the separation method of the first method in more detail.
55 *
56 * Consider the LP-relaxation of the current subproblem:
57 * \f[
58 * \begin{array}{ll}
59 * min & c^T x + d^T z\\
60 * & A x - s \leq b, \\
61 * & D x + C z \leq f, \\
62 * & l \leq x \leq u, \\
63 * & u \leq z \leq v, \\
64 * & 0 \leq s.
65 * \end{array}
66 * \f]
67 * As above \f$Ax - s \leq b\f$ contains all inequalities corresponding to indicator constraints,
68 * while the system \f$Dx + Cy \leq f\f$ contains all other inequalities (which are ignored in the
69 * following). Similarly, variables \f$z\f$ not appearing in indicator constraints are
70 * ignored. Bounds for the variables \f$x_j\f$ can be given, in particular, variables can be
71 * fixed. Note that \f$s \leq 0\f$ renders the system infeasible.
72 *
73 * To generate cuts, we construct the so-called @a alternative @a polyhedron:
74 * \f[
75 * \begin{array}{ll}
76 * P = \{ (w,r,t) : & A^T w - r + t = 0,\\
77 * & b^T w - l^T r + u^T t = -1,\\
78 * & w, r, t \geq 0 \}.
79 * \end{array}
80 * \f]
81 * Here, \f$r\f$ and \f$t\f$ correspond to the lower and upper bounds on \f$x\f$, respectively.
82 *
83 * It turns out that the vertices of \f$P\f$ correspond to minimal infeasible subsystems of \f$A x
84 * \leq b\f$, \f$l \leq x \leq u\f$. If \f$I\f$ is the index set of such a system, it follows that not all \f$s_i\f$ for
85 * \f$i \in I\f$ can be 0, i.e., \f$y_i\f$ can be 1. In other words, the following cut is valid:
86 * \f[
87 * \sum_{i \in I} y_i \leq |I| - 1.
88 * \f]
89 *
90 *
91 * @subsection DETAIL Separation heuristic
92 *
93 * We separate the above inequalities by a heuristic described in
94 *
95 * Branch-And-Cut for the Maximum Feasible Subsystem Problem,@n
96 * Marc Pfetsch, SIAM Journal on Optimization 19, No.1, 21-38 (2008)
97 *
98 * The first step in the separation heuristic is to apply the transformation \f$\bar{y} = 1 - y\f$, which
99 * transforms the above inequality into the constraint
100 * \f[
101 * \sum_{i \in I} \bar{y}_i \geq 1,
102 * \f]
103 * that is, it is a set covering constraint on the negated variables.
104 *
105 * The basic idea is to use the current solution to the LP relaxation and use it as the objective,
106 * when optimizing of the alternative polyhedron. Since any vertex corresponds to such an
107 * inequality, we can check whether it is violated. To enlarge the chance that we find a @em
108 * violated inequality, we perform a fixing procedure, in which the variable corresponding to an
109 * arbitrary element of the last IIS \f$I\f$ is fixed to zero, i.e., cannot be used in the next
110 * IISs. This is repeated until the corresponding alternative polyhedron is infeasible, i.e., we
111 * have obtained an IIS-cover. For more details see the paper above.
112 *
113 *
114 * @subsection PREPROC Preprocessing
115 *
116 * Since each indicator constraint adds a linear constraint to the formulation, preprocessing of the
117 * linear constraints change the above approach as follows.
118 *
119 * The system as present in the formulation is the following (ignoring variables that are not
120 * contained in indicator constraints and the objective function):
121 * \f[
122 * \begin{array}{ll}
123 * & A x - s \leq b, \\
124 * & l \leq x \leq u, \\
125 * & s \leq 0.
126 * \end{array}
127 * \f]
128 * Note again that the requirement \f$s \leq 0\f$ leads to an infeasible system. Consider now the
129 * preprocessing of the linear constraint (aggregation, bound strengthening, etc.) and assume that
130 * this changes the above system to the following:
131 * \f[
132 * \begin{array}{ll}
133 * & \tilde{A} x - \tilde{B} s \leq \tilde{b}, \\
134 * & \tilde{l} \leq x \leq \tilde{u}, \\
135 * & s \leq 0. \\
136 * \end{array}
137 * \f]
138 * Note that we forbid multi-aggregation of the \f$s\f$ variables in order to be able to change their
139 * bounds in propagation/branching. The corresponding alternative system is the following:
140 * \f[
141 * \begin{array}{ll}
142 * & \tilde{A}^T w - r + t = 0,\\
143 * & - \tilde{B}^T w + v = 0,\\
144 * & b^T w - l^T r + u^T t = -1,\\
145 * & w, v, r, t \geq 0
146 * \end{array}
147 * \qquad \Leftrightarrow \qquad
148 * \begin{array}{ll}
149 * & \tilde{A}^T w - r + t = 0,\\
150 * & \tilde{B}^T w \geq 0,\\
151 * & b^T w - l^T r + u^T t = -1,\\
152 * & w, r, t \geq 0,
153 * \end{array}
154 * \f]
155 * where the second form arises by substituting \f$v \geq 0\f$. A closer look at this system reveals
156 * that it is not larger than the original one:
157 *
158 * - (Multi-)Aggregation of variables \f$x\f$ will remove these variables from the formulation, such that
159 * the corresponding column of \f$\tilde{A}\f$ (row of \f$\tilde{A}^T\f$) will be zero.
160 *
161 * - The rows of \f$\tilde{B}^T\f$ are not unit vectors, i.e., do not correspond to redundant
162 * nonnegativity constraints, only if the corresponding slack variables appear in an aggregation.
163 *
164 * Taken together, these two observations yield the conclusion that the new system is roughly as
165 * large as the original one.
166 *
167 * @note Because of possible (multi-)aggregation it might happen that the linear constraint
168 * corresponding to an indicator constraint becomes redundant and is deleted. From this we cannot
169 * conclude that the indicator constraint is redundant as well (i.e. always fulfilled), because the
170 * corresponding slack variable is still present and its setting to 0 might influence other
171 * (linear) constraints. Thus, we have to rely on the dual presolving of the linear constraints to
172 * detect this case: If the linear constraint is really redundant, i.e., is always fulfilled, it is
173 * deleted and the slack variable can be fixed to 0. In this case, the indicator constraint can be
174 * deleted as well.
175 *
176 * @todo Accept arbitrary ranged linear constraints as input (in particular: equations). Internally
177 * create two indicator constraints or correct alternative polyhedron accordingly (need to split the
178 * variables there, but not in original problem).
179 *
180 * @todo Treat variable upper bounds in a special way: Do not create the artificial slack variable,
181 * but directly enforce the propagations etc.
182 *
183 * @todo Turn off separation if the alternative polyhedron is infeasible and updateBounds is false.
184 *
185 * @todo Improve parsing of indicator constraint in CIP-format. Currently, we have to rely on a particular name, i.e.,
186 * the slack variable has to start with "indslack" and end with the name of the corresponding linear constraint.
187 *
188 * @todo Check whether one can further use the fact that the slack variable is aggregated.
189 */
190
191 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
192
193 #include "blockmemshell/memory.h"
194 #include "lpi/lpi.h"
195 #include "lpi/type_lpi.h"
196 #include "scip/expr_var.h"
197 #include "scip/expr_product.h"
198 #include "scip/cons_nonlinear.h"
199 #include "scip/cons_indicator.h"
200 #include "scip/cons_linear.h"
201 #include "scip/cons_logicor.h"
202 #include "scip/cons_varbound.h"
203 #include "scip/heur_indicator.h"
204 #include "scip/heur_trysol.h"
205 #include "scip/pub_conflict.h"
206 #include "scip/pub_cons.h"
207 #include "scip/pub_event.h"
208 #include "scip/pub_lp.h"
209 #include "scip/pub_message.h"
210 #include "scip/pub_misc.h"
211 #include "scip/pub_paramset.h"
212 #include "scip/pub_var.h"
213 #include "scip/scip_branch.h"
214 #include "scip/scip_conflict.h"
215 #include "scip/scip_cons.h"
216 #include "scip/scip_copy.h"
217 #include "scip/scip_cut.h"
218 #include "scip/scip_event.h"
219 #include "scip/scip_general.h"
220 #include "scip/scip_heur.h"
221 #include "scip/scip_lp.h"
222 #include "scip/scip_mem.h"
223 #include "scip/scip_message.h"
224 #include "scip/scip_nlp.h"
225 #include "scip/scip_numerics.h"
226 #include "scip/scip_param.h"
227 #include "scip/scip_prob.h"
228 #include "scip/scip_probing.h"
229 #include "scip/scip_sol.h"
230 #include "scip/scip_solve.h"
231 #include "scip/scip_solvingstats.h"
232 #include "scip/scip_tree.h"
233 #include "scip/scip_var.h"
234 #include <string.h>
235
236 /* #define SCIP_OUTPUT */
237 /* #define SCIP_ENABLE_IISCHECK */
238
239 /* constraint handler properties */
240 #define CONSHDLR_NAME "indicator"
241 #define CONSHDLR_DESC "indicator constraint handler"
242 #define CONSHDLR_SEPAPRIORITY 10 /**< priority of the constraint handler for separation */
243 #define CONSHDLR_ENFOPRIORITY -100 /**< priority of the constraint handler for constraint enforcing */
244 #define CONSHDLR_CHECKPRIORITY -6000000 /**< priority of the constraint handler for checking feasibility */
245 #define CONSHDLR_SEPAFREQ 10 /**< frequency for separating cuts; zero means to separate only in the root node */
246 #define CONSHDLR_PROPFREQ 1 /**< frequency for propagating domains; zero means only preprocessing propagation */
247 #define CONSHDLR_EAGERFREQ 100 /**< frequency for using all instead of only the useful constraints in separation,
248 * propagation and enforcement, -1 for no eager evaluations, 0 for first only */
249 #define CONSHDLR_MAXPREROUNDS -1 /**< maximal number of presolving rounds the constraint handler participates in (-1: no limit) */
250 #define CONSHDLR_DELAYSEPA FALSE /**< Should separation method be delayed, if other separators found cuts? */
251 #define CONSHDLR_DELAYPROP FALSE /**< Should propagation method be delayed, if other propagators found reductions? */
252 #define CONSHDLR_NEEDSCONS TRUE /**< Should the constraint handler be skipped, if no constraints are available? */
253
254 #define CONSHDLR_PRESOLTIMING SCIP_PRESOLTIMING_FAST
255 #define CONSHDLR_PROP_TIMING SCIP_PROPTIMING_BEFORELP
256
257
258 /* event handler properties */
259 #define EVENTHDLR_BOUND_NAME "indicatorbound"
260 #define EVENTHDLR_BOUND_DESC "bound change event handler for indicator constraints"
261
262 #define EVENTHDLR_RESTART_NAME "indicatorrestart"
263 #define EVENTHDLR_RESTART_DESC "force restart if absolute gap is 1 or enough binary variables have been fixed"
264
265
266 /* conflict handler properties */
267 #define CONFLICTHDLR_NAME "indicatorconflict"
268 #define CONFLICTHDLR_DESC "replace slack variables and generate logicor constraints"
269 #define CONFLICTHDLR_PRIORITY 200000
270
271 /* upgrade properties */
272 #define LINCONSUPGD_PRIORITY +100000 /**< priority of the constraint handler for upgrading of linear constraints */
273
274 /* default values for parameters */
275 #define DEFAULT_BRANCHINDICATORS FALSE /**< Branch on indicator constraints in enforcing? */
276 #define DEFAULT_GENLOGICOR FALSE /**< Generate logicor constraints instead of cuts? */
277 #define DEFAULT_ADDCOUPLING TRUE /**< Add coupling constraints or rows if big-M is small enough? */
278 #define DEFAULT_MAXCOUPLINGVALUE 1e4 /**< maximum coefficient for binary variable in coupling constraint */
279 #define DEFAULT_ADDCOUPLINGCONS FALSE /**< Add initial variable upper bound constraints, if 'addcoupling' is true? */
280 #define DEFAULT_SEPACOUPLINGCUTS TRUE /**< Should the coupling inequalities be separated dynamically? */
281 #define DEFAULT_SEPACOUPLINGLOCAL FALSE /**< Allow to use local bounds in order to separate coupling inequalities? */
282 #define DEFAULT_SEPACOUPLINGVALUE 1e4 /**< maximum coefficient for binary variable in separated coupling constraint */
283 #define DEFAULT_SEPAALTERNATIVELP FALSE /**< Separate using the alternative LP? */
284 #define DEFAULT_SEPAPERSPECTIVE FALSE /**< Separate cuts based on perspective formulation? */
285 #define DEFAULT_SEPAPERSPLOCAL TRUE /**< Allow to use local bounds in order to separate perspectice cuts? */
286 #define DEFAULT_MAXSEPANONVIOLATED 3 /**< maximal number of separated non violated IISs, before separation is stopped */
287 #define DEFAULT_TRYSOLFROMCOVER FALSE /**< Try to construct a feasible solution from a cover? */
288 #define DEFAULT_UPGRADELINEAR FALSE /**< Try to upgrade linear constraints to indicator constraints? */
289 #define DEFAULT_USEOTHERCONSS FALSE /**< Collect other constraints to alternative LP? */
290 #define DEFAULT_USEOBJECTIVECUT FALSE /**< Use objective cut with current best solution to alternative LP? */
291 #define DEFAULT_UPDATEBOUNDS FALSE /**< Update bounds of original variables for separation? */
292 #define DEFAULT_MAXCONDITIONALTLP 0.0 /**< max. estimated condition of the solution basis matrix of the alt. LP to be trustworthy (0.0 to disable check) */
293 #define DEFAULT_MAXSEPACUTS 100 /**< maximal number of cuts separated per separation round */
294 #define DEFAULT_MAXSEPACUTSROOT 2000 /**< maximal number of cuts separated per separation round in the root node */
295 #define DEFAULT_REMOVEINDICATORS FALSE /**< Remove indicator constraint if corresponding variable bound constraint has been added? */
296 #define DEFAULT_GENERATEBILINEAR FALSE /**< Do not generate indicator constraint, but a bilinear constraint instead? */
297 #define DEFAULT_SCALESLACKVAR FALSE /**< Scale slack variable coefficient at construction time? */
298 #define DEFAULT_NOLINCONSCONT FALSE /**< Decompose problem (do not generate linear constraint if all variables are continuous)? */
299 #define DEFAULT_TRYSOLUTIONS TRUE /**< Try to make solutions feasible by setting indicator variables? */
300 #define DEFAULT_ENFORCECUTS FALSE /**< In enforcing try to generate cuts (only if sepaalternativelp is true)? */
301 #define DEFAULT_DUALREDUCTIONS TRUE /**< Should dual reduction steps be performed? */
302 #define DEFAULT_ADDOPPOSITE FALSE /**< Add opposite inequality in nodes in which the binary variable has been fixed to 0? */
303 #define DEFAULT_CONFLICTSUPGRADE FALSE /**< Try to upgrade bounddisjunction conflicts by replacing slack variables? */
304 #define DEFAULT_FORCERESTART FALSE /**< Force restart if absolute gap is 1 or enough binary variables have been fixed? */
305 #define DEFAULT_RESTARTFRAC 0.9 /**< fraction of binary variables that need to be fixed before restart occurs (in forcerestart) */
306
307
308 /* other values */
309 #define OBJEPSILON 0.001 /**< value to add to objective in alt. LP if the binary variable is 1 to get small IISs */
310 #define SEPAALTTHRESHOLD 10 /**< only separate IIS cuts if the number of separated coupling cuts is less than this value */
311 #define MAXROUNDINGROUNDS 1 /**< maximal number of rounds that produced cuts in separation */
312
313
314 /** constraint data for indicator constraints */
315 struct SCIP_ConsData
316 {
317 SCIP_VAR* binvar; /**< binary variable for indicator constraint */
318 SCIP_VAR* slackvar; /**< slack variable of inequality of indicator constraint */
319 SCIP_CONS* lincons; /**< linear constraint corresponding to indicator constraint */
320 SCIP_Bool activeone; /**< whether the constraint is active on 1 or 0 */
321 SCIP_Bool lessthanineq; /**< whether the original linear constraint is less-than-rhs or greater-than-rhs */
322 int nfixednonzero; /**< number of variables among binvar and slackvar fixed to be nonzero */
323 int colindex; /**< column index in alternative LP */
324 unsigned int linconsactive:1; /**< whether linear constraint and slack variable are active */
325 unsigned int implicationadded:1; /**< whether corresponding implication has been added */
326 unsigned int slacktypechecked:1; /**< whether it has been checked to convert the slack variable to be implicit integer */
327 };
328
329
330 /** indicator constraint handler data */
331 struct SCIP_ConshdlrData
332 {
333 SCIP_EVENTHDLR* eventhdlrbound; /**< event handler for bound change events */
334 SCIP_EVENTHDLR* eventhdlrrestart; /**< event handler for performing restarts */
335 SCIP_Bool removable; /**< whether the separated cuts should be removable */
336 SCIP_Bool scaled; /**< if first row of alt. LP has been scaled */
337 SCIP_Bool objindicatoronly; /**< whether the objective is nonzero only for indicator variables */
338 SCIP_Bool objothervarsonly; /**< whether the objective is nonzero only for non-indicator variables */
339 SCIP_Real minabsobj; /**< minimum absolute nonzero objective of indicator variables */
340 SCIP_LPI* altlp; /**< alternative LP for cut separation */
341 int nrows; /**< # rows in the alt. LP corr. to original variables in linear constraints and slacks */
342 int nlbbounds; /**< # lower bounds of original variables */
343 int nubbounds; /**< # upper bounds of original variables */
344 SCIP_HASHMAP* varhash; /**< hash map from variable to row index in alternative LP */
345 SCIP_HASHMAP* lbhash; /**< hash map from variable to index of lower bound column in alternative LP */
346 SCIP_HASHMAP* ubhash; /**< hash map from variable to index of upper bound column in alternative LP */
347 SCIP_HASHMAP* slackhash; /**< hash map from slack variable to row index in alternative LP */
348 SCIP_HASHMAP* binvarhash; /**< hash map from binary indicator variable to indicator constraint */
349 int nslackvars; /**< # slack variables */
350 int niiscutsgen; /**< number of IIS-cuts generated */
351 int nperspcutsgen; /**< number of cuts based on perspective formulation generated */
352 int objcutindex; /**< index of objective cut in alternative LP (-1 if not added) */
353 SCIP_Real objupperbound; /**< best upper bound on objective known */
354 SCIP_Real objaltlpbound; /**< upper objective bound stored in alternative LP (infinity if not added) */
355 int maxroundingrounds; /**< maximal number of rounds that produced cuts in separation */
356 SCIP_Real roundingminthres; /**< minimal value for rounding in separation */
357 SCIP_Real roundingmaxthres; /**< maximal value for rounding in separation */
358 SCIP_Real roundingoffset; /**< offset for rounding in separation */
359 SCIP_Bool branchindicators; /**< Branch on indicator constraints in enforcing? */
360 SCIP_Bool genlogicor; /**< Generate logicor constraints instead of cuts? */
361 SCIP_Bool addcoupling; /**< whether the coupling inequalities should be added at the beginning */
362 SCIP_Bool addcouplingcons; /**< Add initial variable upper bound constraints, if 'addcoupling' is true? */
363 SCIP_Bool sepacouplingcuts; /**< Should the coupling inequalities be separated dynamically? */
364 SCIP_Bool sepacouplinglocal; /**< Allow to use local bounds in order to separate coupling inequalities? */
365 SCIP_Bool sepaperspective; /**< Separate cuts based on perspective formulation? */
366 SCIP_Bool sepapersplocal; /**< Allow to use local bounds in order to separate perspectice cuts? */
367 SCIP_Bool removeindicators; /**< Remove indicator constraint if corresponding variable bound constraint has been added? */
368 SCIP_Bool updatebounds; /**< whether the bounds of the original variables should be changed for separation */
369 SCIP_Bool trysolutions; /**< Try to make solutions feasible by setting indicator variables? */
370 SCIP_Bool enforcecuts; /**< in enforcing try to generate cuts (only if sepaalternativelp is true) */
371 SCIP_Bool dualreductions; /**< Should dual reduction steps be performed? */
372 SCIP_Bool addopposite; /**< Add opposite inequality in nodes in which the binary variable has been fixed to 0? */
373 SCIP_Bool generatebilinear; /**< Do not generate indicator constraint, but a bilinear constraint instead? */
374 SCIP_Bool scaleslackvar; /**< Scale slack variable coefficient at construction time? */
375 SCIP_Bool conflictsupgrade; /**< Try to upgrade bounddisjunction conflicts by replacing slack variables? */
376 SCIP_Bool performedrestart; /**< whether a restart has been performed already */
377 int maxsepacuts; /**< maximal number of cuts separated per separation round */
378 int maxsepacutsroot; /**< maximal number of cuts separated per separation round in root node */
379 int maxsepanonviolated; /**< maximal number of separated non violated IISs, before separation is stopped */
380 int nbinvarszero; /**< binary variables globally fixed to zero */
381 int ninitconss; /**< initial number of indicator constraints (needed in event handlers) */
382 SCIP_Real maxcouplingvalue; /**< maximum coefficient for binary variable in initial coupling constraint */
383 SCIP_Real sepacouplingvalue; /**< maximum coefficient for binary variable in separated coupling constraint */
384 SCIP_Real maxconditionaltlp; /**< maximum estimated condition number of the alternative LP to trust its solution */
385 SCIP_Real restartfrac; /**< fraction of binary variables that need to be fixed before restart occurs (in forcerestart) */
386 SCIP_HEUR* heurtrysol; /**< trysol heuristic */
387 SCIP_Bool addedcouplingcons; /**< whether the coupling constraints have been added already */
388 SCIP_CONS** addlincons; /**< additional linear constraints that should be added to the alternative LP */
389 int naddlincons; /**< number of additional constraints */
390 int maxaddlincons; /**< maximal number of additional constraints */
391 SCIP_Bool useotherconss; /**< Collect other constraints to alternative LP? */
392 SCIP_Bool useobjectivecut; /**< Use objective cut with current best solution to alternative LP? */
393 SCIP_Bool trysolfromcover; /**< Try to construct a feasible solution from a cover? */
394 SCIP_Bool upgradelinear; /**< Try to upgrade linear constraints to indicator constraints? */
395 char normtype; /**< norm type for cut computation */
396 /* parameters that should not be changed after problem stage: */
397 SCIP_Bool sepaalternativelp; /**< Separate using the alternative LP? */
398 SCIP_Bool sepaalternativelp_; /**< used to store the sepaalternativelp parameter */
399 SCIP_Bool nolinconscont; /**< decompose problem - do not generate linear constraint if all variables are continuous */
400 SCIP_Bool nolinconscont_; /**< used to store the nolinconscont parameter */
401 SCIP_Bool forcerestart; /**< Force restart if absolute gap is 1 or enough binary variables have been fixed? */
402 SCIP_Bool forcerestart_; /**< used to store the forcerestart parameter */
403 };
404
405
406 /** indicator conflict handler data */
407 struct SCIP_ConflicthdlrData
408 {
409 SCIP_CONSHDLR* conshdlr; /**< indicator constraint handler */
410 SCIP_CONSHDLRDATA* conshdlrdata; /**< indicator constraint handler data */
411 };
412
413
414 /** type of enforcing/separation call */
415 enum SCIP_enfosepatype
416 {
417 SCIP_TYPE_ENFOLP = 0, /**< enforce LP */
418 SCIP_TYPE_ENFOPS = 1, /**< enforce pseudo solution */
419 SCIP_TYPE_ENFORELAX = 2, /**< enforce relaxation solution */
420 SCIP_TYPE_SEPALP = 3, /**< separate LP */
421 SCIP_TYPE_SEPARELAX = 4, /**< separate relaxation solution */
422 SCIP_TYPE_SEPASOL = 5 /**< separate relaxation solution */
423 };
424 typedef enum SCIP_enfosepatype SCIP_ENFOSEPATYPE;
425
426
427 /* macro for parameters */
428 #define SCIP_CALL_PARAM(x) /*lint -e527 */ do \
429 { \
430 SCIP_RETCODE _restat_; \
431 if ( (_restat_ = (x)) != SCIP_OKAY && (_restat_ != SCIP_PARAMETERUNKNOWN) ) \
432 { \
433 SCIPerrorMessage("[%s:%d] Error <%d> in function call\n", __FILE__, __LINE__, _restat_); \
434 SCIPABORT(); \
435 return _restat_; \
436 } \
437 } \
438 while ( FALSE )
439
440
441 /* ---------------- Callback methods of event handlers ---------------- */
442
443 /** execute the event handler for getting variable bound changes
444 *
445 * We update the number of variables fixed to be nonzero.
446 */
447 static
448 SCIP_DECL_EVENTEXEC(eventExecIndicatorBound)
449 {
450 SCIP_EVENTTYPE eventtype;
451 SCIP_CONSDATA* consdata;
452 SCIP_Real oldbound;
453 SCIP_Real newbound;
454
455 assert( eventhdlr != NULL );
456 assert( eventdata != NULL );
457 assert( strcmp(SCIPeventhdlrGetName(eventhdlr), EVENTHDLR_BOUND_NAME) == 0 );
458 assert( event != NULL );
459
460 consdata = (SCIP_CONSDATA*)eventdata;
461 assert( consdata != NULL );
462 assert( 0 <= consdata->nfixednonzero && consdata->nfixednonzero <= 2 );
463 assert( consdata->linconsactive );
464
465 oldbound = SCIPeventGetOldbound(event);
466 newbound = SCIPeventGetNewbound(event);
467
468 eventtype = SCIPeventGetType(event);
469 switch ( eventtype )
470 {
471 case SCIP_EVENTTYPE_LBTIGHTENED:
472 /* if variable is now fixed to be positive */
473 if ( ! SCIPisFeasPositive(scip, oldbound) && SCIPisFeasPositive(scip, newbound) )
474 ++(consdata->nfixednonzero);
475 #ifdef SCIP_MORE_DEBUG
476 SCIPdebugMsg(scip, "Changed lower bound of variable <%s> from %g to %g (nfixednonzero: %d).\n",
477 SCIPvarGetName(SCIPeventGetVar(event)), oldbound, newbound, consdata->nfixednonzero);
478 #endif
479 break;
480
481 case SCIP_EVENTTYPE_UBTIGHTENED:
482 /* if variable is now fixed to be negative */
483 if ( ! SCIPisFeasNegative(scip, oldbound) && SCIPisFeasNegative(scip, newbound) )
484 ++(consdata->nfixednonzero);
485 #ifdef SCIP_MORE_DEBUG
486 SCIPdebugMsg(scip, "Changed upper bound of variable <%s> from %g to %g (nfixednonzero: %d).\n",
487 SCIPvarGetName(SCIPeventGetVar(event)), oldbound, newbound, consdata->nfixednonzero);
488 #endif
489 break;
490
491 case SCIP_EVENTTYPE_LBRELAXED:
492 /* if variable is not fixed to be positive anymore */
493 if ( SCIPisFeasPositive(scip, oldbound) && ! SCIPisFeasPositive(scip, newbound) )
494 --(consdata->nfixednonzero);
495 #ifdef SCIP_MORE_DEBUG
496 SCIPdebugMsg(scip, "Changed lower bound of variable <%s> from %g to %g (nfixednonzero: %d).\n",
497 SCIPvarGetName(SCIPeventGetVar(event)), oldbound, newbound, consdata->nfixednonzero);
498 #endif
499 break;
500
501 case SCIP_EVENTTYPE_UBRELAXED:
502 /* if variable is not fixed to be negative anymore */
503 if ( SCIPisFeasNegative(scip, oldbound) && ! SCIPisFeasNegative(scip, newbound) )
504 --(consdata->nfixednonzero);
505 #ifdef SCIP_MORE_DEBUG
506 SCIPdebugMsg(scip, "Changed upper bound of variable <%s> from %g to %g (nfixednonzero: %d).\n",
507 SCIPvarGetName(SCIPeventGetVar(event)), oldbound, newbound, consdata->nfixednonzero);
508 #endif
509 break;
510
511 default:
512 SCIPerrorMessage("Invalid event type.\n");
513 SCIPABORT();
514 return SCIP_INVALIDDATA; /*lint !e527*/
515 }
516 assert( 0 <= consdata->nfixednonzero && consdata->nfixednonzero <= 2 );
517
518 return SCIP_OKAY;
519 }
520
521
522 /** exec the event handler for forcing a restart
523 *
524 * There are two cases in which we perform a (user) restart:
525 * - If we have a max FS instance, i.e., the objective is 1 for indicator variables and 0 otherwise,
526 * we can force a restart if the gap is 1. In this case, the remaining work consists of proving
527 * infeasibility of the non-fixed indicators.
528 * - If a large fraction of the binary indicator variables have been globally fixed, it makes sense
529 * to force a restart.
530 */
531 static
532 SCIP_DECL_EVENTEXEC(eventExecIndicatorRestart)
533 {
534 SCIP_CONSHDLRDATA* conshdlrdata;
535 SCIP_EVENTTYPE eventtype;
536
537 assert( scip != NULL );
538 assert( eventhdlr != NULL );
539 assert( eventdata != NULL );
540 assert( strcmp(SCIPeventhdlrGetName(eventhdlr), EVENTHDLR_RESTART_NAME) == 0 );
541 assert( event != NULL );
542
543 conshdlrdata = (SCIP_CONSHDLRDATA*)eventdata;
544 assert( conshdlrdata != NULL );
545 assert( conshdlrdata->forcerestart );
546
547 eventtype = SCIPeventGetType(event);
548 switch ( eventtype )
549 {
550 case SCIP_EVENTTYPE_GUBCHANGED:
551 case SCIP_EVENTTYPE_GLBCHANGED:
552 {
553 #ifndef NDEBUG
554 SCIP_Real oldbound;
555 SCIP_Real newbound;
556
557 assert( SCIPvarGetType(SCIPeventGetVar(event)) == SCIP_VARTYPE_BINARY );
558 oldbound = SCIPeventGetOldbound(event);
559 newbound = SCIPeventGetNewbound(event);
560 assert( SCIPisIntegral(scip, oldbound) );
561 assert( SCIPisIntegral(scip, newbound) );
562 assert( ! SCIPisEQ(scip, oldbound, newbound) );
563 assert( SCIPisZero(scip, oldbound) || SCIPisEQ(scip, oldbound, 1.0) );
564 assert( SCIPisZero(scip, newbound) || SCIPisEQ(scip, newbound, 1.0) );
565 #endif
566
567 /* do not treat this case if we have performed a restart already */
568 if ( conshdlrdata->performedrestart )
569 return SCIP_OKAY;
570
571 /* variable is now fixed */
572 ++(conshdlrdata->nbinvarszero);
573 SCIPdebugMsg(scip, "Fixed variable <%s> (nbinvarszero: %d, total: %d).\n",
574 SCIPvarGetName(SCIPeventGetVar(event)), conshdlrdata->nbinvarszero, conshdlrdata->ninitconss);
575
576 if ( SCIPgetStage(scip) != SCIP_STAGE_SOLVING )
577 break;
578
579 /* if enough variables have been fixed */
580 if ( conshdlrdata->nbinvarszero > (int) ((SCIP_Real) conshdlrdata->ninitconss * conshdlrdata->restartfrac) )
581 {
582 SCIPverbMessage(scip, SCIP_VERBLEVEL_NORMAL, NULL,
583 "Forcing restart, since %d binary variables among %d have been fixed.\n", conshdlrdata->nbinvarszero, conshdlrdata->ninitconss);
584 SCIP_CALL( SCIPrestartSolve(scip) );
585
586 /* drop event */
587 if ( conshdlrdata->objindicatoronly )
588 {
589 SCIP_CALL( SCIPdropEvent(scip, SCIP_EVENTTYPE_BESTSOLFOUND, eventhdlr, (SCIP_EVENTDATA*) conshdlrdata, -1) );
590 }
591 conshdlrdata->performedrestart = TRUE;
592 }
593 break;
594 }
595
596 case SCIP_EVENTTYPE_BESTSOLFOUND:
597 assert( SCIPisIntegral(scip, conshdlrdata->minabsobj) );
598 assert( SCIPisGE(scip, conshdlrdata->minabsobj, 1.0 ) );
599
600 if ( SCIPgetStage(scip) != SCIP_STAGE_SOLVING )
601 break;
602
603 if ( ! conshdlrdata->objindicatoronly )
604 break;
605
606 /* if the absolute gap is equal to minabsobj */
607 if ( SCIPisEQ(scip, REALABS(SCIPgetPrimalbound(scip) - SCIPgetDualbound(scip)), conshdlrdata->minabsobj) )
608 {
609 SCIPverbMessage(scip, SCIP_VERBLEVEL_NORMAL, NULL, "Forcing restart, since the absolute gap is %f.\n", conshdlrdata->minabsobj);
610 SCIP_CALL( SCIPrestartSolve(scip) );
611
612 /* use inference branching, since the objective is not meaningful */
613 if ( SCIPfindBranchrule(scip, "inference") != NULL && !SCIPisParamFixed(scip, "branching/inference/priority") )
614 {
615 SCIP_CALL( SCIPsetIntParam(scip, "branching/inference/priority", INT_MAX/4) );
616 }
617
618 /* drop event */
619 SCIP_CALL( SCIPdropEvent(scip, SCIP_EVENTTYPE_BESTSOLFOUND, eventhdlr, (SCIP_EVENTDATA*) conshdlrdata, -1) );
620 conshdlrdata->performedrestart = TRUE;
621 }
622 break;
623
624 default:
625 SCIPerrorMessage("invalid event type.\n");
626 SCIPABORT();
627 return SCIP_INVALIDDATA; /*lint !e527*/
628 }
629
630 return SCIP_OKAY;
631 }
632
633
634 /* ------------------------ conflict handler ---------------------------------*/
635
636 /** destructor of conflict handler to free conflict handler data (called when SCIP is exiting) */
637 static
638 SCIP_DECL_CONFLICTFREE(conflictFreeIndicator)
639 {
640 SCIP_CONFLICTHDLRDATA* conflicthdlrdata;
641
642 assert( scip != NULL );
643 assert( conflicthdlr != NULL );
644 assert( strcmp(SCIPconflicthdlrGetName(conflicthdlr), CONFLICTHDLR_NAME) == 0 );
645
646 conflicthdlrdata = SCIPconflicthdlrGetData(conflicthdlr);
647 SCIPfreeBlockMemory(scip, &conflicthdlrdata);
648
649 return SCIP_OKAY;
650 }
651
652
653 /** conflict processing method of conflict handler (called when conflict was found)
654 *
655 * In this conflict handler we try to replace slack variables by binary indicator variables and
656 * generate a logicor constraint if possible.
657 *
658 * @todo Extend to integral case.
659 */
660 static
661 SCIP_DECL_CONFLICTEXEC(conflictExecIndicator)
662 { /*lint --e{715}*/
663 SCIP_CONFLICTHDLRDATA* conflicthdlrdata;
664 SCIP_Bool haveslack;
665 SCIP_VAR* var;
666 int i;
667
668 assert( conflicthdlr != NULL );
669 assert( strcmp(SCIPconflicthdlrGetName(conflicthdlr), CONFLICTHDLR_NAME) == 0 );
670 assert( bdchginfos != NULL || nbdchginfos == 0 );
671 assert( result != NULL );
672
673 /* don't process already resolved conflicts */
674 if ( resolved )
675 {
676 *result = SCIP_DIDNOTRUN;
677 return SCIP_OKAY;
678 }
679
680 SCIPdebugMsg(scip, "Indicator conflict handler.\n");
681
682 conflicthdlrdata = SCIPconflicthdlrGetData(conflicthdlr);
683 assert( conflicthdlrdata != NULL );
684
685 /* possibly skip conflict handler */
686 if ( ! ((SCIP_CONFLICTHDLRDATA*) conflicthdlrdata)->conshdlrdata->conflictsupgrade )
687 return SCIP_OKAY;
688
689 *result = SCIP_DIDNOTFIND;
690
691 /* check whether there seems to be one slack variable and all other variables are binary */
692 haveslack = FALSE;
693 for (i = 0; i < nbdchginfos; ++i)
694 {
695 assert( bdchginfos != NULL ); /* for flexelint */
696 assert( bdchginfos[i] != NULL );
697
698 var = SCIPbdchginfoGetVar(bdchginfos[i]);
699
700 /* quick check for slack variable that is implicitly integral or continuous */
701 if ( SCIPvarGetType(var) == SCIP_VARTYPE_IMPLINT || SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS )
702 {
703 /* check string */
704 if ( strstr(SCIPvarGetName(var), "indslack") != NULL )
705 {
706 /* make sure that the slack variable occurs with its lower bound */
707 if ( SCIPboundtypeOpposite(SCIPbdchginfoGetBoundtype(bdchginfos[i])) != SCIP_BOUNDTYPE_LOWER )
708 break;
709
710 /* make sure that the lower bound is 0 */
711 if ( ! SCIPisFeasZero(scip, SCIPbdchginfoGetNewbound(bdchginfos[i])) )
712 break;
713
714 haveslack = TRUE;
715 continue;
716 }
717 }
718
719 /* we only treat binary variables (other than slack variables) */
720 if ( ! SCIPvarIsBinary(var) )
721 break;
722 }
723
724 /* if we have found at least one slack variable and all other variables are binary */
725 if ( haveslack && i == nbdchginfos )
726 {
727 SCIP_CONS** conss;
728 SCIP_VAR** vars;
729 int nconss;
730 int j;
731
732 SCIPdebugMsg(scip, "Found conflict involving slack variables that can be remodelled.\n");
733
734 assert( conflicthdlrdata->conshdlr != NULL );
735 assert( strcmp(SCIPconshdlrGetName(conflicthdlrdata->conshdlr), CONSHDLR_NAME) == 0 );
736
737 nconss = SCIPconshdlrGetNConss(conflicthdlrdata->conshdlr);
738 conss = SCIPconshdlrGetConss(conflicthdlrdata->conshdlr);
739
740 /* create array of variables in conflict constraint */
741 SCIP_CALL( SCIPallocBufferArray(scip, &vars, nbdchginfos) );
742 for (i = 0; i < nbdchginfos; ++i)
743 {
744 assert( bdchginfos != NULL ); /* for flexelint */
745 assert( bdchginfos[i] != NULL );
746
747 var = SCIPbdchginfoGetVar(bdchginfos[i]);
748
749 SCIPdebugMsg(scip, " <%s> %s %g\n", SCIPvarGetName(var), SCIPbdchginfoGetBoundtype(bdchginfos[i]) == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=",
750 SCIPbdchginfoGetNewbound(bdchginfos[i]));
751
752 /* quick check for slack variable that is implicitly integral or continuous */
753 if ( (SCIPvarGetType(var) == SCIP_VARTYPE_IMPLINT || SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS) && strstr(SCIPvarGetName(var), "indslack") != NULL )
754 {
755 SCIP_VAR* slackvar;
756
757 /* search for slack variable */
758 for (j = 0; j < nconss; ++j)
759 {
760 assert( conss[j] != NULL );
761 slackvar = SCIPgetSlackVarIndicator(conss[j]);
762 assert( slackvar != NULL );
763
764 /* check whether we found the variable */
765 if ( slackvar == var )
766 {
767 /* replace slack variable by binary variable */
768 var = SCIPgetBinaryVarIndicator(conss[j]);
769 break;
770 }
771 }
772
773 /* check whether we found the slack variable */
774 if ( j >= nconss )
775 {
776 SCIPdebugMsg(scip, "Could not find slack variable <%s>.\n", SCIPvarGetName(var));
777 break;
778 }
779 }
780 else
781 {
782 /* if the variable is fixed to one in the conflict set, we have to use its negation */
783 if ( SCIPbdchginfoGetNewbound(bdchginfos[i]) > 0.5 )
784 {
785 SCIP_CALL( SCIPgetNegatedVar(scip, var, &var) );
786 }
787 }
788
789 vars[i] = var;
790 }
791
792 /* whether all slack variables have been found */
793 if ( i == nbdchginfos )
794 {
795 SCIP_CONS* cons;
796 char consname[SCIP_MAXSTRLEN];
797
798 SCIPdebugMsg(scip, "Generated logicor conflict constraint.\n");
799
800 /* create a logicor constraint out of the conflict set */
801 (void) SCIPsnprintf(consname, SCIP_MAXSTRLEN, "cf%d_%" SCIP_LONGINT_FORMAT, SCIPgetNRuns(scip), SCIPgetNConflictConssApplied(scip));
802 SCIP_CALL( SCIPcreateConsLogicor(scip, &cons, consname, nbdchginfos, vars,
803 FALSE, separate, FALSE, FALSE, TRUE, local, FALSE, dynamic, removable, FALSE) );
804
805 #ifdef SCIP_OUTPUT
806 SCIP_CALL( SCIPprintCons(scip, cons, NULL) );
807 SCIPinfoMessage(scip, NULL, ";\n");
808 #endif
809
810 /* add constraint to SCIP */
811 SCIP_CALL( SCIPaddConflict(scip, node, cons, validnode, conftype, cutoffinvolved) );
812
813 *result = SCIP_CONSADDED;
814 }
815
816 /* free temporary memory */
817 SCIPfreeBufferArray(scip, &vars);
818 }
819
820 return SCIP_OKAY;
821 }
822
823
824 /* ------------------------ parameter handling ---------------------------------*/
825
826 /** check whether we transfer a changed parameter to the given value
827 *
828 * @see paramChangedIndicator()
829 */
830 static
831 SCIP_RETCODE checkTransferBoolParam(
832 SCIP* scip, /**< SCIP data structure */
833 SCIP_PARAM* param, /**< parameter */
834 const char* name, /**< parameter name to check */
835 SCIP_Bool newvalue, /**< new value */
836 SCIP_Bool* value /**< old and possibly changed value of parameter */
837 )
838 {
839 const char* paramname;
840
841 assert( scip != NULL );
842 assert( param != NULL );
843 assert( name != NULL );
844 assert( value != NULL );
845
846 if ( SCIPparamGetType(param) != SCIP_PARAMTYPE_BOOL )
847 return SCIP_OKAY;
848
849 if ( *value == newvalue )
850 return SCIP_OKAY;
851
852 paramname = SCIPparamGetName(param);
853 assert( paramname != NULL );
854
855 /* check whether the change parameter corresponds to our name to check */
856 if ( strcmp(paramname, name) == 0 )
857 {
858 /* check stage and possibly ignore parameter change */
859 if ( SCIPgetStage(scip) > SCIP_STAGE_PROBLEM )
860 {
861 SCIPwarningMessage(scip, "Cannot change parameter <%s> stage %d - reset to old value %s.\n", name, SCIPgetStage(scip), *value ? "true" : "false");
862 /* Note that the following command will create a recursive call, but then *value == newvalue above. */
863 SCIP_CALL( SCIPchgBoolParam(scip, param, *value) );
864 }
865 else
866 {
867 /* otherwise copy value */
868 *value = newvalue;
869 }
870 }
871
872 return SCIP_OKAY;
873 }
874
875
876 /** called after a parameter has been changed */
877 static
878 SCIP_DECL_PARAMCHGD(paramChangedIndicator)
879 {
880 SCIP_CONSHDLR* conshdlr;
881 SCIP_CONSHDLRDATA* conshdlrdata;
882
883 assert( scip != NULL );
884 assert( param != NULL );
885
886 /* get indicator constraint handler */
887 conshdlr = SCIPfindConshdlr(scip, "indicator");
888 assert( conshdlr != NULL );
889
890 /* get constraint handler data */
891 conshdlrdata = SCIPconshdlrGetData(conshdlr);
892 assert( conshdlrdata != NULL );
893
894 SCIP_CALL( checkTransferBoolParam(scip, param, "constraints/indicator/sepaalternativelp", conshdlrdata->sepaalternativelp_, &conshdlrdata->sepaalternativelp) );
895 SCIP_CALL( checkTransferBoolParam(scip, param, "constraints/indicator/forcerestart", conshdlrdata->forcerestart_, &conshdlrdata->forcerestart) );
896 SCIP_CALL( checkTransferBoolParam(scip, param, "constraints/indicator/nolinconscont", conshdlrdata->nolinconscont_, &conshdlrdata->nolinconscont) );
897
898 return SCIP_OKAY;
899 }
900
901
902 /* ------------------------ debugging routines ---------------------------------*/
903
904 #ifdef SCIP_ENABLE_IISCHECK
905 /** Check that indicator constraints corresponding to nonnegative entries in @a vector are infeasible in original problem
906 *
907 * @note This function will probably fail if the has been presolved by the cons_linear presolver. To make it complete
908 * we would have to substitute active variables.
909 */
910 static
911 SCIP_RETCODE checkIIS(
912 SCIP* scip, /**< SCIP pointer */
913 int nconss, /**< number of constraints */
914 SCIP_CONS** conss, /**< indicator constraints */
915 SCIP_Real* vector /**< vector */
916 )
917 {
918 SCIP_CONSHDLRDATA* conshdlrdata;
919 SCIP_CONSHDLR* conshdlr;
920 SCIP_HASHMAP* varhash; /* hash map from variable to column index in auxiliary LP */
921 SCIP_LPI* lp;
922 int nvars = 0;
923 int c;
924
925 assert( scip != NULL );
926 assert( vector != NULL );
927
928 SCIPdebugMsg(scip, "Checking IIS ...\n");
929
930 /* now check indicator constraints */
931 conshdlr = SCIPfindConshdlr(scip, "indicator");
932 assert( conshdlr != NULL );
933
934 conshdlrdata = SCIPconshdlrGetData(conshdlr);
935 assert( conshdlrdata != NULL );
936
937 conss = SCIPconshdlrGetConss(conshdlr);
938 nconss = SCIPconshdlrGetNConss(conshdlr);
939
940 /* create LP */
941 SCIP_CALL( SCIPlpiCreate(&lp, SCIPgetMessagehdlr(scip), "checkLP", SCIP_OBJSEN_MINIMIZE) );
942
943 /* set up hash map */
944 SCIP_CALL( SCIPhashmapCreate(&varhash, SCIPblkmem(scip), SCIPgetNVars(scip)) );
945
946 /* loop through indicator constraints */
947 for (c = 0; c < nconss; ++c)
948 {
949 SCIP_CONSDATA* consdata;
950 consdata = SCIPconsGetData(conss[c]);
951 assert( consdata != NULL );
952
953 /* check whether constraint should be included */
954 if ( consdata->colindex >= 0 && (! SCIPisFeasZero(scip, vector[consdata->colindex]) || ! SCIPconsIsEnabled(conss[c])) )
955 {
956 SCIP_CONS* lincons;
957 SCIP_VAR** linvars;
958 SCIP_Real* linvals;
959 SCIP_Real linrhs;
960 SCIP_Real linlhs;
961 SCIP_VAR* slackvar;
962 int nlinvars;
963 SCIP_Real sign = 1.0;
964 int matbeg;
965 int* matind;
966 SCIP_Real* matval;
967 SCIP_VAR** newvars;
968 int nnewvars;
969 SCIP_Real lhs;
970 SCIP_Real rhs;
971 int cnt;
972 int v;
973
974 lincons = consdata->lincons;
975 assert( lincons != NULL );
976 assert( ! SCIPconsIsEnabled(conss[c]) || SCIPconsIsActive(lincons) );
977 assert( ! SCIPconsIsEnabled(conss[c]) || SCIPconsIsEnabled(lincons) );
978
979 slackvar = consdata->slackvar;
980 assert( slackvar != NULL );
981
982 /* if the slack variable is aggregated (multi-aggregation should not happen) */
983 assert( SCIPvarGetStatus(slackvar) != SCIP_VARSTATUS_MULTAGGR );
984 if ( SCIPvarGetStatus(slackvar) == SCIP_VARSTATUS_AGGREGATED )
985 {
986 SCIP_VAR* var;
987 SCIP_Real scalar = 1.0;
988 SCIP_Real constant = 0.0;
989
990 var = slackvar;
991
992 SCIP_CALL( SCIPgetProbvarSum(scip, &var, &scalar, &constant) );
993 assert( ! SCIPisZero(scip, scalar) );
994
995 /* SCIPdebugMsg(scip, "slack variable aggregated (scalar: %f, constant: %f)\n", scalar, constant); */
996
997 /* otherwise construct a linear constraint */
998 SCIP_CALL( SCIPallocBufferArray(scip, &linvars, 1) );
999 SCIP_CALL( SCIPallocBufferArray(scip, &linvals, 1) );
1000 linvars[0] = var;
1001 linvals[0] = scalar;
1002 nlinvars = 1;
1003 linlhs = -SCIPinfinity(scip);
1004 linrhs = constant;
1005 }
1006 else
1007 {
1008 /* in this case, the linear constraint is directly usable */
1009 linvars = SCIPgetVarsLinear(scip, lincons);
1010 linvals = SCIPgetValsLinear(scip, lincons);
1011 nlinvars = SCIPgetNVarsLinear(scip, lincons);
1012 linlhs = SCIPgetLhsLinear(scip, lincons);
1013 linrhs = SCIPgetRhsLinear(scip, lincons);
1014 }
1015
1016 /* adapt rhs of linear constraint */
1017 assert( SCIPisInfinity(scip, -linlhs) || SCIPisInfinity(scip, linrhs) );
1018 if ( SCIPisInfinity(scip, linrhs) )
1019 {
1020 linrhs = -linlhs;
1021 assert( linrhs > -SCIPinfinity(scip) );
1022 sign = -1.0;
1023 }
1024
1025 SCIP_CALL( SCIPallocBufferArray(scip, &matind, 4*nlinvars) );
1026 SCIP_CALL( SCIPallocBufferArray(scip, &matval, 4*nlinvars) );
1027 SCIP_CALL( SCIPallocBufferArray(scip, &newvars, nlinvars) );
1028
1029 /* set up row */
1030 nnewvars = 0;
1031 for (v = 0; v < nlinvars; ++v)
1032 {
1033 SCIP_VAR* var;
1034 var = linvars[v];
1035 assert( var != NULL );
1036
1037 /* skip slack variable */
1038 if ( var == slackvar )
1039 continue;
1040
1041 /* if variable is new */
1042 if ( ! SCIPhashmapExists(varhash, var) )
1043 {
1044 /* add variable in map */
1045 SCIP_CALL( SCIPhashmapInsertInt(varhash, var, nvars) );
1046 assert( nvars == SCIPhashmapGetImageInt(varhash, var) );
1047 /* SCIPdebugMsg(scip, "Inserted variable <%s> into hashmap (%d).\n", SCIPvarGetName(var), nvars); */
1048 nvars++;
1049
1050 /* store new variables */
1051 newvars[nnewvars++] = var;
1052 }
1053 assert( SCIPhashmapExists(varhash, var) );
1054 }
1055
1056 /* add new columns */
1057 if ( nnewvars > 0 )
1058 {
1059 SCIP_Real* lb;
1060 SCIP_Real* ub;
1061 SCIP_Real* obj;
1062 char** colnames;
1063
1064 SCIP_CALL( SCIPallocBufferArray(scip, &lb, nnewvars) );
1065 SCIP_CALL( SCIPallocBufferArray(scip, &ub, nnewvars) );
1066 SCIP_CALL( SCIPallocBufferArray(scip, &obj, nnewvars) );
1067 SCIP_CALL( SCIPallocBufferArray(scip, &colnames, nnewvars) );
1068
1069 for (v = 0; v < nnewvars; ++v)
1070 {
1071 SCIP_VAR* var;
1072 var = newvars[v];
1073 obj[v] = 0.0;
1074 lb[v] = SCIPvarGetLbLocal(var);
1075 ub[v] = SCIPvarGetUbLocal(var);
1076 SCIP_CALL( SCIPallocBufferArray(scip, &(colnames[v]), SCIP_MAXSTRLEN) ); /*lint !e866*/
1077 (void) SCIPsnprintf(colnames[v], SCIP_MAXSTRLEN, "%s", SCIPvarGetName(var));
1078 }
1079
1080 /* now add columns */
1081 SCIP_CALL( SCIPlpiAddCols(lp, nnewvars, obj, lb, ub, colnames, 0, NULL, NULL, NULL) );
1082
1083 for (v = nnewvars - 1; v >= 0; --v)
1084 {
1085 SCIPfreeBufferArray(scip, &(colnames[v]));
1086 }
1087 SCIPfreeBufferArray(scip, &colnames);
1088 SCIPfreeBufferArray(scip, &obj);
1089 SCIPfreeBufferArray(scip, &ub);
1090 SCIPfreeBufferArray(scip, &lb);
1091 }
1092
1093 /* set up row */
1094 cnt = 0;
1095 for (v = 0; v < nlinvars; ++v)
1096 {
1097 SCIP_VAR* var;
1098 var = linvars[v];
1099 assert( var != NULL );
1100
1101 /* skip slack variable */
1102 if ( var == slackvar )
1103 continue;
1104
1105 assert( SCIPhashmapExists(varhash, var) );
1106 matind[cnt] = SCIPhashmapGetImageInt(varhash, var);
1107 matval[cnt] = sign * linvals[v];
1108 ++cnt;
1109 }
1110
1111 lhs = -SCIPlpiInfinity(lp);
1112 rhs = linrhs;
1113
1114 /* add new row */
1115 matbeg = 0;
1116 SCIP_CALL( SCIPlpiAddRows(lp, 1, &lhs, &rhs, NULL, cnt, &matbeg, matind, matval) );
1117
1118 SCIPfreeBufferArray(scip, &matind);
1119 SCIPfreeBufferArray(scip, &matval);
1120 SCIPfreeBufferArray(scip, &newvars);
1121
1122 assert( slackvar != NULL );
1123 if ( SCIPvarGetStatus(slackvar) == SCIP_VARSTATUS_AGGREGATED )
1124 {
1125 SCIPfreeBufferArray(scip, &linvals);
1126 SCIPfreeBufferArray(scip, &linvars);
1127 }
1128 }
1129 }
1130
1131 /* possibly handle additional linear constraints */
1132 if ( conshdlrdata->useotherconss )
1133 {
1134 /* get all linear constraints */
1135 conss = SCIPgetConss(scip);
1136 nconss = SCIPgetNConss(scip);
1137
1138 /* loop through constraints */
1139 for (c = 0; c < nconss; ++c)
1140 {
1141 SCIP_CONS* cons;
1142 SCIP_VAR** linvars;
1143 SCIP_Real* linvals;
1144 SCIP_Real linrhs;
1145 SCIP_Real linlhs;
1146 SCIP_Real* matval;
1147 SCIP_VAR** newvars;
1148 int nnewvars = 0;
1149 int* matind;
1150 int nlinvars;
1151 int matbeg = 0;
1152 int cnt = 0;
1153 int v;
1154
1155 cons = conss[c];
1156 assert( cons != NULL );
1157
1158 /* avoid non-active, local constraints */
1159 if ( ! SCIPconsIsEnabled(cons) || ! SCIPconsIsActive(cons) || SCIPconsIsLocal(cons) )
1160 continue;
1161
1162 /* check type of constraint (only take linear constraints) */
1163 if ( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), "linear") != 0 )
1164 continue;
1165
1166 /* avoid adding linear constraints that correspond to indicator constraints */
1167 if ( strncmp(SCIPconsGetName(cons), "indlin", 6) == 0 )
1168 continue;
1169
1170 /* get data of linear constraint */
1171 linvars = SCIPgetVarsLinear(scip, cons);
1172 linvals = SCIPgetValsLinear(scip, cons);
1173 nlinvars = SCIPgetNVarsLinear(scip, cons);
1174 linlhs = SCIPgetLhsLinear(scip, cons);
1175 linrhs = SCIPgetRhsLinear(scip, cons);
1176
1177 /* reserve space */
1178 SCIP_CALL( SCIPallocBufferArray(scip, &matind, 4*nlinvars) );
1179 SCIP_CALL( SCIPallocBufferArray(scip, &matval, 4*nlinvars) );
1180 SCIP_CALL( SCIPallocBufferArray(scip, &newvars, nlinvars) );
1181
1182 /* collect possibly new variables */
1183 for (v = 0; v < nlinvars; ++v)
1184 {
1185 SCIP_VAR* var;
1186 var = linvars[v];
1187 assert( var != NULL );
1188
1189 /* if variable is new */
1190 if ( ! SCIPhashmapExists(varhash, var) )
1191 {
1192 /* add variable in map */
1193 SCIP_CALL( SCIPhashmapInsertInt(varhash, var, nvars) );
1194 assert( nvars == SCIPhashmapGetImageInt(varhash, var) );
1195 /* SCIPdebugMsg(scip, "Inserted variable <%s> into hashmap (%d).\n", SCIPvarGetName(var), nvars); */
1196 nvars++;
1197
1198 /* store new variables */
1199 newvars[nnewvars++] = var;
1200 }
1201 assert( SCIPhashmapExists(varhash, var) );
1202 }
1203
1204 /* add new columns */
1205 if ( nnewvars > 0 )
1206 {
1207 SCIP_Real* lb;
1208 SCIP_Real* ub;
1209 SCIP_Real* obj;
1210 char** colnames;
1211
1212 SCIP_CALL( SCIPallocBufferArray(scip, &lb, nnewvars) );
1213 SCIP_CALL( SCIPallocBufferArray(scip, &ub, nnewvars) );
1214 SCIP_CALL( SCIPallocBufferArray(scip, &obj, nnewvars) );
1215 SCIP_CALL( SCIPallocBufferArray(scip, &colnames, nnewvars) );
1216
1217 for (v = 0; v < nnewvars; ++v)
1218 {
1219 SCIP_VAR* var;
1220 var = newvars[v];
1221 obj[v] = 0.0;
1222 lb[v] = SCIPvarGetLbLocal(var);
1223 ub[v] = SCIPvarGetUbLocal(var);
1224 SCIP_CALL( SCIPallocBufferArray(scip, &(colnames[v]), SCIP_MAXSTRLEN) ); /*lint !e866*/
1225 (void) SCIPsnprintf(colnames[v], SCIP_MAXSTRLEN, "%s", SCIPvarGetName(var));
1226 }
1227
1228 /* now add columns */
1229 SCIP_CALL( SCIPlpiAddCols(lp, nnewvars, obj, lb, ub, colnames, 0, NULL, NULL, NULL) );
1230
1231 for (v = nnewvars - 1; v >= 0; --v)
1232 {
1233 SCIPfreeBufferArray(scip, &(colnames[v]));
1234 }
1235 SCIPfreeBufferArray(scip, &colnames);
1236 SCIPfreeBufferArray(scip, &obj);
1237 SCIPfreeBufferArray(scip, &ub);
1238 SCIPfreeBufferArray(scip, &lb);
1239 }
1240
1241 /* set up row */
1242 for (v = 0; v < nlinvars; ++v)
1243 {
1244 SCIP_VAR* var;
1245 var = linvars[v];
1246 assert( var != NULL );
1247
1248 assert( SCIPhashmapExists(varhash, var) );
1249 matind[cnt] = SCIPhashmapGetImageInt(varhash, var);
1250 matval[cnt] = linvals[v];
1251 ++cnt;
1252 }
1253
1254 /* add new row */
1255 SCIP_CALL( SCIPlpiAddRows(lp, 1, &linlhs, &linrhs, NULL, cnt, &matbeg, matind, matval) );
1256
1257 SCIPfreeBufferArray(scip, &matind);
1258 SCIPfreeBufferArray(scip, &matval);
1259 SCIPfreeBufferArray(scip, &newvars);
1260 }
1261 }
1262
1263 /* solve LP and check status */
1264 SCIP_CALL( SCIPlpiSolvePrimal(lp) );
1265
1266 if ( ! SCIPlpiIsPrimalInfeasible(lp) )
1267 {
1268 SCIPerrorMessage("Detected IIS is not infeasible in original problem!\n");
1269
1270 SCIP_CALL( SCIPlpiWriteLP(lp, "check.lp") );
1271 SCIP_CALL( SCIPlpiWriteLP(conshdlrdata->altlp, "altdebug.lp") );
1272 SCIPABORT();
1273 return SCIP_ERROR; /*lint !e527*/
1274 }
1275 SCIPdebugMsg(scip, "Check successful!\n");
1276
1277 SCIPhashmapFree(&varhash);
1278 SCIP_CALL( SCIPlpiFree(&lp) );
1279
1280 return SCIP_OKAY;
1281 }
1282 #endif
1283
1284
1285 /* ------------------------ auxiliary operations -------------------------------*/
1286
1287 /** return objective contribution of variable
1288 *
1289 * Special treatment of negated variables: return negative of objective of original
1290 * variable. SCIPvarGetObj() would return 0 in these cases.
1291 */
1292 static
1293 SCIP_Real varGetObjDelta(
1294 SCIP_VAR* var /**< variable */
1295 )
1296 {
1297 if ( SCIPvarIsBinary(var) && SCIPvarIsNegated(var) )
1298 {
1299 assert( SCIPvarGetNegatedVar(var) != NULL );
1300 return -SCIPvarGetObj(SCIPvarGetNegatedVar(var));
1301 }
1302 else if ( SCIPvarGetStatus(var) == SCIP_VARSTATUS_AGGREGATED )
1303 {
1304 assert( SCIPvarGetAggrVar(var) != NULL );
1305 return SCIPvarGetAggrScalar(var) * SCIPvarGetObj(SCIPvarGetAggrVar(var));
1306 }
1307
1308 return SCIPvarGetObj(var);
1309 }
1310
1311
1312 /** ensures that the addlincons array can store at least num entries */
1313 static
1314 SCIP_RETCODE consdataEnsureAddLinConsSize(
1315 SCIP* scip, /**< SCIP data structure */
1316 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
1317 int num /**< minimum number of entries to store */
1318 )
1319 {
1320 SCIP_CONSHDLRDATA* conshdlrdata;
1321
1322 assert( scip != NULL );
1323 assert( conshdlr != NULL );
1324 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
1325
1326 conshdlrdata = SCIPconshdlrGetData(conshdlr);
1327 assert( conshdlrdata != NULL );
1328 assert( conshdlrdata->naddlincons <= conshdlrdata->maxaddlincons );
1329
1330 if ( num > conshdlrdata->maxaddlincons )
1331 {
1332 int newsize;
1333
1334 newsize = SCIPcalcMemGrowSize(scip, num);
1335 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &conshdlrdata->addlincons, conshdlrdata->maxaddlincons, newsize) );
1336 conshdlrdata->maxaddlincons = newsize;
1337 }
1338 assert( num <= conshdlrdata->maxaddlincons );
1339
1340 return SCIP_OKAY;
1341 }
1342
1343
1344 /* ------------------------ operations on the alternative LP -------------------*/
1345
1346 /** initialize alternative LP
1347 *
1348 * The alternative system is organized as follows:
1349 * - The first row corresponds to the right hand side of the original system.
1350 * - The next nconss constraints correspond to the slack variables.
1351 * - The rows after that correspond to the original variables.
1352 */
1353 static
1354 SCIP_RETCODE initAlternativeLP(
1355 SCIP* scip, /**< SCIP pointer */
1356 SCIP_CONSHDLR* conshdlr /**< constraint handler */
1357 )
1358 {
1359 SCIP_CONSHDLRDATA* conshdlrdata;
1360 SCIP_Real lhs = -1.0;
1361 SCIP_Real rhs = -1.0;
1362
1363 assert( scip != NULL );
1364 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
1365
1366 conshdlrdata = SCIPconshdlrGetData(conshdlr);
1367 assert( conshdlrdata != NULL );
1368 assert( conshdlrdata->altlp == NULL );
1369 assert( conshdlrdata->varhash == NULL );
1370 assert( conshdlrdata->lbhash == NULL );
1371 assert( conshdlrdata->ubhash == NULL );
1372 assert( conshdlrdata->slackhash != NULL );
1373
1374 /* create hash map of variables */
1375 SCIP_CALL( SCIPhashmapCreate(&conshdlrdata->varhash, SCIPblkmem(scip), SCIPgetNVars(scip)) );
1376 SCIP_CALL( SCIPhashmapCreate(&conshdlrdata->lbhash, SCIPblkmem(scip), SCIPgetNVars(scip)) );
1377 SCIP_CALL( SCIPhashmapCreate(&conshdlrdata->ubhash, SCIPblkmem(scip), SCIPgetNVars(scip)) );
1378
1379 /* create alternative LP */
1380 SCIP_CALL( SCIPlpiCreate(&conshdlrdata->altlp, SCIPgetMessagehdlr(scip), "altlp", SCIP_OBJSEN_MINIMIZE) );
1381
1382 /* add first row */
1383 SCIP_CALL( SCIPlpiAddRows(conshdlrdata->altlp, 1, &lhs, &rhs, NULL, 0, NULL, NULL, NULL) );
1384 conshdlrdata->nrows = 1;
1385
1386 /* set parameters */
1387 SCIP_CALL_PARAM( SCIPlpiSetIntpar(conshdlrdata->altlp, SCIP_LPPAR_FROMSCRATCH, FALSE) );
1388 SCIP_CALL_PARAM( SCIPlpiSetIntpar(conshdlrdata->altlp, SCIP_LPPAR_PRESOLVING, TRUE) );
1389 SCIP_CALL_PARAM( SCIPlpiSetIntpar(conshdlrdata->altlp, SCIP_LPPAR_SCALING, 1) );
1390 SCIP_CALL_PARAM( SCIPlpiSetIntpar(conshdlrdata->altlp, SCIP_LPPAR_FASTMIP, FALSE) );
1391
1392 SCIPdebugMsg(scip, "Initialized alternative LP.\n");
1393
1394 /* uncomment the following for debugging */
1395 /* SCIP_CALL_PARAM( SCIPlpiSetIntpar(conshdlrdata->altlp, SCIP_LPPAR_LPINFO, TRUE) ); */
1396
1397 return SCIP_OKAY;
1398 }
1399
1400
1401 /** check whether the bounds in given (alternative) LP are set correctly (for debugging) */
1402 #ifndef NDEBUG
1403 static
1404 SCIP_RETCODE checkLPBoundsClean(
1405 SCIP* scip, /**< SCIP pointer */
1406 SCIP_LPI* lp, /**< LP for which bounds should be checked */
1407 int nconss, /**< number of constraints */
1408 SCIP_CONS** conss /**< constraints */
1409 )
1410 {
1411 SCIP_Real* lb;
1412 SCIP_Real* ub;
1413 SCIP_Bool* covered;
1414 int nCols;
1415 int j;
1416
1417 assert( scip != NULL );
1418 assert( lp != NULL );
1419
1420 SCIP_CALL( SCIPlpiGetNCols(lp, &nCols) );
1421
1422 SCIP_CALL( SCIPallocBufferArray(scip, &lb, nCols) );
1423 SCIP_CALL( SCIPallocBufferArray(scip, &ub, nCols) );
1424 SCIP_CALL( SCIPallocBufferArray(scip, &covered, nCols) );
1425
1426 for (j = 0; j < nCols; ++j)
1427 covered[j] = FALSE;
1428
1429 /* check columns used by constraints */
1430 SCIP_CALL( SCIPlpiGetBounds(lp, 0, nCols-1, lb, ub) );
1431 for (j = 0; j < nconss; ++j)
1432 {
1433 SCIP_CONSDATA* consdata;
1434 int ind;
1435
1436 assert( conss[j] != NULL );
1437 consdata = SCIPconsGetData(conss[j]);
1438 assert( consdata != NULL );
1439 ind = consdata->colindex;
1440
1441 if ( ind >= 0 )
1442 {
1443 assert( ind < nCols );
1444 covered[ind] = TRUE;
1445 if ( ! SCIPisFeasZero(scip, lb[ind]) || ! SCIPlpiIsInfinity(lp, ub[ind]) )
1446 {
1447 SCIPABORT();
1448 }
1449 }
1450 }
1451
1452 /* check other columns */
1453 for (j = 0; j < nCols; ++j)
1454 {
1455 if (! covered[j] )
1456 {
1457 /* some columns can be fixed to 0, since they correspond to disabled constraints */
1458 if ( ( ! SCIPlpiIsInfinity(lp, -lb[j]) && ! SCIPisFeasZero(scip, lb[j])) || (! SCIPlpiIsInfinity(lp, ub[j]) && ! SCIPisFeasZero(scip, ub[j])) )
1459 {
1460 SCIPABORT();
1461 }
1462 }
1463 }
1464
1465 SCIPfreeBufferArray(scip, &covered);
1466 SCIPfreeBufferArray(scip, &lb);
1467 SCIPfreeBufferArray(scip, &ub);
1468
1469 return SCIP_OKAY;
1470 }
1471 #endif
1472
1473
1474 /** set the alternative system objective function
1475 *
1476 * We assume that the objective function coefficients of the variables other than the binary
1477 * indicators are always 0 and hence do not have to be changed.
1478 *
1479 * We already use the tranformation \f$y' = 1 - y\f$.
1480 */
1481 static
1482 SCIP_RETCODE setAltLPObj(
1483 SCIP* scip, /**< SCIP pointer */
1484 SCIP_LPI* lp, /**< alternative LP */
1485 SCIP_SOL* sol, /**< solution to be dealt with */
1486 int nconss, /**< number of constraints */
1487 SCIP_CONS** conss /**< indicator constraints */
1488 )
1489 {
1490 int j;
1491 SCIP_Real* obj = NULL;
1492 int* indices = NULL;
1493 int cnt = 0;
1494
1495 assert( scip != NULL );
1496 assert( lp != NULL );
1497 assert( conss != NULL );
1498
1499 SCIP_CALL( SCIPallocBufferArray(scip, &obj, nconss) );
1500 SCIP_CALL( SCIPallocBufferArray(scip, &indices, nconss) );
1501
1502 for (j = 0; j < nconss; ++j)
1503 {
1504 SCIP_CONSDATA* consdata;
1505
1506 assert( conss[j] != NULL );
1507 consdata = SCIPconsGetData(conss[j]);
1508 assert( consdata != NULL );
1509
1510 if ( consdata->colindex >= 0 )
1511 {
1512 SCIP_Real val = SCIPgetSolVal(scip, sol, consdata->binvar);
1513 if ( SCIPisFeasEQ(scip, val, 1.0) )
1514 obj[cnt] = OBJEPSILON; /* set objective to some small number to get small IISs */
1515 else
1516 obj[cnt] = 1.0 - val;
1517 indices[cnt++] = consdata->colindex;
1518 }
1519 }
1520
1521 if ( cnt > 0 )
1522 {
1523 SCIP_CALL( SCIPlpiChgObj(lp, cnt, indices, obj) );
1524 }
1525
1526 SCIPfreeBufferArray(scip, &indices);
1527 SCIPfreeBufferArray(scip, &obj);
1528
1529 return SCIP_OKAY;
1530 }
1531
1532
1533 /** set the alternative system objective function to some small value */
1534 static
1535 SCIP_RETCODE setAltLPObjZero(
1536 SCIP* scip, /**< SCIP pointer */
1537 SCIP_LPI* lp, /**< alternative LP */
1538 int nconss, /**< number of constraints */
1539 SCIP_CONS** conss /**< indicator constraints */
1540 )
1541 {
1542 int j;
1543 SCIP_Real* obj = NULL;
1544 int* indices = NULL;
1545 int cnt = 0;
1546
1547 assert( scip != NULL );
1548 assert( lp != NULL );
1549 assert( conss != NULL );
1550
1551 SCIP_CALL( SCIPallocBufferArray(scip, &obj, nconss) );
1552 SCIP_CALL( SCIPallocBufferArray(scip, &indices, nconss) );
1553
1554 for (j = 0; j < nconss; ++j)
1555 {
1556 SCIP_CONSDATA* consdata;
1557
1558 assert( conss[j] != NULL );
1559 consdata = SCIPconsGetData(conss[j]);
1560 assert( consdata != NULL );
1561
1562 if ( consdata->colindex >= 0 )
1563 {
1564 obj[cnt] = OBJEPSILON;
1565 indices[cnt++] = consdata->colindex;
1566 }
1567 }
1568
1569 if ( cnt > 0 )
1570 {
1571 SCIP_CALL( SCIPlpiChgObj(lp, cnt, indices, obj) );
1572 }
1573
1574 SCIPfreeBufferArray(scip, &indices);
1575 SCIPfreeBufferArray(scip, &obj);
1576
1577 return SCIP_OKAY;
1578 }
1579
1580
1581 /** fix variable given by @a S to 0 */
1582 static
1583 SCIP_RETCODE fixAltLPVariables(
1584 SCIP* scip, /**< SCIP pointer */
1585 SCIP_LPI* lp, /**< alternative LP */
1586 int nconss, /**< number of constraints */
1587 SCIP_CONS** conss, /**< indicator constraints */
1588 SCIP_Bool* S /**< bitset of variables */
1589 )
1590 {
1591 SCIP_Real* lb = NULL;
1592 SCIP_Real* ub = NULL;
1593 int* indices = NULL;
1594 int cnt = 0;
1595 int j;
1596
1597 assert( scip != NULL );
1598 assert( lp != NULL );
1599 assert( conss != NULL );
1600
1601 SCIP_CALL( SCIPallocBufferArray(scip, &lb, nconss) );
1602 SCIP_CALL( SCIPallocBufferArray(scip, &ub, nconss) );
1603 SCIP_CALL( SCIPallocBufferArray(scip, &indices, nconss) );
1604
1605 /* collect bounds to be changed */
1606 for (j = 0; j < nconss; ++j)
1607 {
1608 SCIP_CONSDATA* consdata;
1609
1610 assert( conss[j] != NULL );
1611 consdata = SCIPconsGetData(conss[j]);
1612 assert( consdata != NULL );
1613
1614 if ( consdata->colindex >= 0 )
1615 {
1616 if ( S[j] )
1617 {
1618 indices[cnt] = consdata->colindex;
1619 lb[cnt] = 0.0;
1620 ub[cnt] = 0.0;
1621 ++cnt;
1622 }
1623 }
1624 }
1625
1626 /* change bounds */
1627 if ( cnt > 0 )
1628 {
1629 SCIP_CALL( SCIPlpiChgBounds(lp, cnt, indices, lb, ub) );
1630 }
1631
1632 SCIPfreeBufferArray(scip, &indices);
1633 SCIPfreeBufferArray(scip, &ub);
1634 SCIPfreeBufferArray(scip, &lb);
1635
1636 return SCIP_OKAY;
1637 }
1638
1639
1640 /** fix variable @a ind to 0 */
1641 static
1642 SCIP_RETCODE fixAltLPVariable(
1643 SCIP_LPI* lp, /**< alternative LP */
1644 int ind /**< variable that should be fixed to 0 */
1645 )
1646 {
1647 SCIP_Real lb = 0.0;
1648 SCIP_Real ub = 0.0;
1649
1650 /* change bounds */
1651 SCIP_CALL( SCIPlpiChgBounds(lp, 1, &ind, &lb, &ub) );
1652
1653 return SCIP_OKAY;
1654 }
1655
1656
1657 /** unfix variable @a ind to 0 */
1658 static
1659 SCIP_RETCODE unfixAltLPVariable(
1660 SCIP_LPI* lp, /**< alternative LP */
1661 int ind /**< variable that should be fixed to 0 */
1662 )
1663 {
1664 SCIP_Real lb = 0.0;
1665 SCIP_Real ub = SCIPlpiInfinity(lp);
1666
1667 /* change bounds */
1668 SCIP_CALL( SCIPlpiChgBounds(lp, 1, &ind, &lb, &ub) );
1669
1670 return SCIP_OKAY;
1671 }
1672
1673 /** unfix variable given by @a S to 0 */
1674 static
1675 SCIP_RETCODE unfixAltLPVariables(
1676 SCIP* scip, /**< SCIP pointer */
1677 SCIP_LPI* lp, /**< alternative LP */
1678 int nconss, /**< number of constraints */
1679 SCIP_CONS** conss, /**< indicator constraints */
1680 SCIP_Bool* S /**< bitset of variables */
1681 )
1682 {
1683 SCIP_Real* lb = NULL;
1684 SCIP_Real* ub = NULL;
1685 int* indices = NULL;
1686 int cnt = 0;
1687 int j;
1688
1689 assert( scip != NULL );
1690 assert( lp != NULL );
1691 assert( conss != NULL );
1692
1693 SCIP_CALL( SCIPallocBufferArray(scip, &lb, nconss) );
1694 SCIP_CALL( SCIPallocBufferArray(scip, &ub, nconss) );
1695 SCIP_CALL( SCIPallocBufferArray(scip, &indices, nconss) );
1696
1697 /* collect bounds to be changed */
1698 for (j = 0; j < nconss; ++j)
1699 {
1700 if ( S[j] )
1701 {
1702 SCIP_CONSDATA* consdata;
1703
1704 assert( conss[j] != NULL );
1705 consdata = SCIPconsGetData(conss[j]);
1706 assert( consdata != NULL );
1707
1708 if ( consdata->colindex >= 0 )
1709 {
1710 indices[cnt] = consdata->colindex;
1711 lb[cnt] = 0.0;
1712 ub[cnt] = SCIPlpiInfinity(lp);
1713 ++cnt;
1714 }
1715 }
1716 }
1717
1718 /* change bounds */
1719 if ( cnt > 0 )
1720 {
1721 SCIP_CALL( SCIPlpiChgBounds(lp, cnt, indices, lb, ub) );
1722 }
1723
1724 SCIPfreeBufferArray(scip, &indices);
1725 SCIPfreeBufferArray(scip, &ub);
1726 SCIPfreeBufferArray(scip, &lb);
1727
1728 return SCIP_OKAY;
1729 }
1730
1731
1732 /** update bounds in first row to the current ones */
1733 static
1734 SCIP_RETCODE updateFirstRow(
1735 SCIP* scip, /**< SCIP pointer */
1736 SCIP_CONSHDLRDATA* conshdlrdata /**< constraint handler */
1737 )
1738 {
1739 SCIP_HASHMAP* lbhash;
1740 SCIP_HASHMAP* ubhash;
1741 SCIP_VAR** vars;
1742 SCIP_LPI* altlp;
1743 int nvars;
1744 int cnt;
1745 int v;
1746
1747 assert( scip != NULL );
1748 assert( conshdlrdata != NULL );
1749
1750 altlp = conshdlrdata->altlp;
1751 lbhash = conshdlrdata->lbhash;
1752 ubhash = conshdlrdata->ubhash;
1753 assert( lbhash != NULL && ubhash != NULL );
1754
1755 /* check all variables */
1756 vars = SCIPgetVars(scip);
1757 nvars = SCIPgetNVars(scip);
1758 cnt = 0;
1759
1760 for (v = 0; v < nvars; ++v)
1761 {
1762 SCIP_VAR* var;
1763 var = vars[v];
1764 if ( SCIPhashmapExists(lbhash, var) )
1765 {
1766 int col;
1767
1768 col = SCIPhashmapGetImageInt(lbhash, var);
1769 SCIP_CALL( SCIPlpiChgCoef(altlp, 0, col, -SCIPvarGetLbLocal(var)) );
1770 if ( ! SCIPisEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetLbGlobal(var)) )
1771 ++cnt;
1772 }
1773 if ( SCIPhashmapExists(ubhash, var) )
1774 {
1775 int col;
1776
1777 col = SCIPhashmapGetImageInt(ubhash, var);
1778 SCIP_CALL( SCIPlpiChgCoef(altlp, 0, col, SCIPvarGetUbLocal(var)) );
1779 if ( ! SCIPisEQ(scip, SCIPvarGetUbLocal(var), SCIPvarGetUbGlobal(var)) )
1780 ++cnt;
1781 }
1782 }
1783 if ( cnt > 10 )
1784 {
1785 /* possible force a rescaling: */
1786 conshdlrdata->scaled = FALSE;
1787
1788 /* SCIP_CALL( SCIPlpiWriteLP(altlp, "altChg.lp") ); */
1789 SCIPdebugMsg(scip, "Updated bounds of original variables: %d.\n", cnt);
1790 }
1791
1792 return SCIP_OKAY;
1793 }
1794
1795
1796 /** update bounds in first row to the global bounds */
1797 static
1798 SCIP_RETCODE updateFirstRowGlobal(
1799 SCIP* scip, /**< SCIP pointer */
1800 SCIP_CONSHDLRDATA* conshdlrdata /**< constraint handler */
1801 )
1802 {
1803 SCIP_HASHMAP* lbhash;
1804 SCIP_HASHMAP* ubhash;
1805 SCIP_VAR** vars;
1806 SCIP_LPI* altlp;
1807 int nvars;
1808 int cnt;
1809 int v;
1810
1811 assert( scip != NULL );
1812 assert( conshdlrdata != NULL );
1813
1814 altlp = conshdlrdata->altlp;
1815 lbhash = conshdlrdata->lbhash;
1816 ubhash = conshdlrdata->ubhash;
1817 assert( lbhash != NULL && ubhash != NULL );
1818
1819 /* check all variables */
1820 vars = SCIPgetVars(scip);
1821 nvars = SCIPgetNVars(scip);
1822 cnt = 0;
1823
1824 for (v = 0; v < nvars; ++v)
1825 {
1826 SCIP_VAR* var;
1827 int col;
1828
1829 var = vars[v];
1830 if ( SCIPhashmapExists(lbhash, var) )
1831 {
1832 col = SCIPhashmapGetImageInt(lbhash, var);
1833 SCIP_CALL( SCIPlpiChgCoef(altlp, 0, col, -SCIPvarGetLbGlobal(var)) );
1834 ++cnt;
1835 }
1836 if ( SCIPhashmapExists(ubhash, var) )
1837 {
1838 col = SCIPhashmapGetImageInt(ubhash, var);
1839 SCIP_CALL( SCIPlpiChgCoef(altlp, 0, col, SCIPvarGetUbGlobal(var)) );
1840 ++cnt;
1841 }
1842 }
1843
1844 if ( cnt > 0 )
1845 {
1846 /* SCIP_CALL( SCIPlpiWriteLP(altlp, "altChg.lp") ); */
1847 SCIPdebugMsg(scip, "Updated bounds of original variables: %d.\n", cnt);
1848 }
1849
1850 /* possible force a rescaling: */
1851 /* conshdlrdata->scaled = FALSE; */
1852
1853 return SCIP_OKAY;
1854 }
1855
1856
1857 /** check whether IIS defined by @a vector corresponds to a local cut */
1858 static
1859 SCIP_RETCODE checkIISlocal(
1860 SCIP* scip, /**< SCIP pointer */
1861 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler */
1862 SCIP_Real* vector, /**< solution to alternative LP defining IIS */
1863 SCIP_Bool* isLocal /**< whether the IIS uses local bounds different from the global ones */
1864 )
1865 {
1866 SCIP_HASHMAP* lbhash;
1867 SCIP_HASHMAP* ubhash;
1868 SCIP_VAR** vars;
1869 #ifndef NDEBUG
1870 int nCols;
1871 #endif
1872 int nvars;
1873 int v;
1874
1875 assert( scip != NULL );
1876 assert( conshdlrdata != NULL );
1877 assert( vector != NULL );
1878 assert( isLocal != NULL );
1879
1880 *isLocal = FALSE;
1881
1882 #ifndef NDEBUG
1883 SCIP_CALL( SCIPlpiGetNCols(conshdlrdata->altlp, &nCols) );
1884 #endif
1885
1886 lbhash = conshdlrdata->lbhash;
1887 ubhash = conshdlrdata->ubhash;
1888 assert( lbhash != NULL && ubhash != NULL );
1889
1890 /* get all variables */
1891 vars = SCIPgetVars(scip);
1892 nvars = SCIPgetNVars(scip);
1893
1894 /* check all variables */
1895 for (v = 0; v < nvars; ++v)
1896 {
1897 SCIP_VAR* var;
1898 var = vars[v];
1899
1900 /* if local bound is different from global bound */
1901 if ( ! SCIPisEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetLbGlobal(var)) )
1902 {
1903 /* check whether the variable corresponding to the lower bounds has been used */
1904 if ( SCIPhashmapExists(lbhash, var) )
1905 {
1906 int col;
1907
1908 col = SCIPhashmapGetImageInt(lbhash, var);
1909 assert( 0 <= col && col < nCols );
1910 if ( ! SCIPisFeasZero(scip, vector[col]) )
1911 {
1912 *isLocal = TRUE;
1913 return SCIP_OKAY;
1914 }
1915 }
1916 }
1917
1918 /* if local bound is different from global bound */
1919 if ( ! SCIPisEQ(scip, SCIPvarGetUbLocal(var), SCIPvarGetUbGlobal(var)) )
1920 {
1921 /* check whether the variable corresponding to the upper bounds has been used */
1922 if ( SCIPhashmapExists(ubhash, var) )
1923 {
1924 int col;
1925
1926 col = SCIPhashmapGetImageInt(ubhash, var);
1927 assert( 0 <= col && col < nCols );
1928 if ( ! SCIPisFeasZero(scip, vector[col]) )
1929 {
1930 *isLocal = TRUE;
1931 return SCIP_OKAY;
1932 }
1933 }
1934 }
1935 }
1936
1937 return SCIP_OKAY;
1938 }
1939
1940
1941 /** compute scaling for first row
1942 *
1943 * If the coefficients in the first row are large, a right hand side of -1 might not be
1944 * adequate. Here, we replace the right hand side by the sum of the coefficients divided by the
1945 * number of nonzeros.
1946 */
1947 static
1948 SCIP_RETCODE scaleFirstRow(
1949 SCIP* scip, /**< SCIP pointer */
1950 SCIP_CONSHDLRDATA* conshdlrdata /**< constraint handler */
1951 )
1952 {
1953 assert( scip != NULL );
1954 assert( conshdlrdata != NULL );
1955
1956 if ( ! conshdlrdata->scaled )
1957 {
1958 SCIP_Real* val;
1959 SCIP_LPI* altlp;
1960 int* ind;
1961 SCIP_Real sum = 0.0;
1962 int beg[1];
1963 int nCols;
1964 int cnt;
1965 int j;
1966
1967 altlp = conshdlrdata->altlp;
1968 SCIP_CALL( SCIPlpiGetNCols(altlp, &nCols) );
1969 SCIP_CALL( SCIPallocBufferArray(scip, &ind, nCols) );
1970 SCIP_CALL( SCIPallocBufferArray(scip, &val, nCols) );
1971
1972 SCIP_CALL( SCIPlpiGetRows(altlp, 0, 0, NULL, NULL, &cnt, beg, ind, val) );
1973
1974 if ( cnt > 0 )
1975 {
1976 /* compute sum */
1977 for (j = 0; j < cnt; ++j)
1978 sum += REALABS(val[j]);
1979
1980 /* set rhs */
1981 sum = - REALABS(sum) / ((double) cnt);
1982 j = 0;
1983 SCIP_CALL( SCIPlpiChgSides(altlp, 1, &j, &sum, &sum) );
1984 }
1985
1986 SCIPfreeBufferArray(scip, &val);
1987 SCIPfreeBufferArray(scip, &ind);
1988
1989 conshdlrdata->scaled = TRUE;
1990 }
1991
1992 return SCIP_OKAY;
1993 }
1994
1995
1996 /** add column to alternative LP
1997 *
1998 * See the description at the top of the file for more information.
1999 */
2000 static
2001 SCIP_RETCODE addAltLPColumn(
2002 SCIP* scip, /**< SCIP pointer */
2003 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
2004 SCIP_CONSHDLRDATA* conshdlrdata, /**< data of constraint handler */
2005 SCIP_VAR* slackvar, /**< slack variable or NULL */
2006 int nvars, /**< number of variables in column */
2007 SCIP_VAR** vars, /**< variables for column */
2008 SCIP_Real* vals, /**< values for column */
2009 SCIP_Real rhscoef, /**< coefficient for first row */
2010 SCIP_Real objcoef, /**< objective in alternative LP */
2011 SCIP_Real sign, /**< sign (+1,-1) for column */
2012 SCIP_Bool colfree, /**< whether column should be free, e.g., for equations */
2013 int* colindex /**< index of new column (return value) */
2014 )
2015 {
2016 SCIP_VAR** newvars;
2017 SCIP_Real val;
2018 SCIP_Real* matval;
2019 SCIP_Bool* newrowsslack;
2020 SCIP_Real* obj;
2021 SCIP_Real* lb;
2022 SCIP_Real* ub;
2023 int* matbeg;
2024 int* matind;
2025 int nnewvars = 0;
2026 int nnewcols = 0;
2027 int nnewrows = 0;
2028 int ncols = 0;
2029 int cnt = 0;
2030 int v;
2031
2032 assert( scip != NULL );
2033 assert( conshdlrdata != NULL );
2034 assert( vars != NULL || nvars == 0 );
2035 assert( vals != NULL || nvars == 0 );
2036 assert( ! SCIPisInfinity(scip, rhscoef) && ! SCIPisInfinity(scip, -rhscoef) );
2037 assert( SCIPisEQ(scip, sign, 1.0) || SCIPisEQ(scip, sign, -1.0) );
2038 assert( colindex != NULL );
2039
2040 *colindex = -1;
2041
2042 if ( conshdlrdata->altlp == NULL )
2043 {
2044 SCIP_CALL( initAlternativeLP(scip, conshdlr) );
2045 }
2046 assert( conshdlrdata->altlp != NULL );
2047 assert( conshdlrdata->varhash != NULL );
2048 assert( conshdlrdata->lbhash != NULL );
2049 assert( conshdlrdata->ubhash != NULL );
2050 assert( conshdlrdata->slackhash != NULL );
2051
2052 #ifndef NDEBUG
2053 {
2054 int nrows;
2055 SCIP_CALL( SCIPlpiGetNRows(conshdlrdata->altlp, &nrows) );
2056 assert( nrows == conshdlrdata->nrows );
2057 }
2058 #endif
2059
2060 /* set up data for construction */
2061 SCIP_CALL( SCIPallocBufferArray(scip, &matbeg, nvars) );
2062 SCIP_CALL( SCIPallocBufferArray(scip, &matind, 4 * nvars) );
2063 SCIP_CALL( SCIPallocBufferArray(scip, &matval, 4 * nvars) );
2064 SCIP_CALL( SCIPallocBufferArray(scip, &obj, 2 * nvars) );
2065 SCIP_CALL( SCIPallocBufferArray(scip, &lb, 2 * nvars) );
2066 SCIP_CALL( SCIPallocBufferArray(scip, &ub, 2 * nvars) );
2067 SCIP_CALL( SCIPallocBufferArray(scip, &newvars, nvars) );
2068 SCIP_CALL( SCIPallocBufferArray(scip, &newrowsslack, 2 * nvars) );
2069
2070 /* store index of column in constraint */
2071 /* coverity[var_deref_model] */
2072 SCIP_CALL( SCIPlpiGetNCols(conshdlrdata->altlp, &ncols) );
2073 *colindex = ncols;
2074
2075 /* handle first row */
2076 if ( ! SCIPisFeasZero(scip, rhscoef) )
2077 {
2078 matind[cnt] = 0;
2079 matval[cnt++] = sign * rhscoef;
2080 }
2081
2082 /* set up column (recognize new original variables) */
2083 for (v = 0; v < nvars; ++v)
2084 {
2085 SCIP_VAR* var;
2086
2087 var = vars[v];
2088 assert( var != NULL );
2089
2090 /* if variable is a slack variable */
2091 if ( SCIPhashmapExists(conshdlrdata->slackhash, var) )
2092 {
2093 /* to avoid trivial rows: only add row corresponding to slack variable if it appears outside its own constraint */
2094 if ( var != slackvar )
2095 {
2096 int ind;
2097
2098 ind = SCIPhashmapGetImageInt(conshdlrdata->slackhash, var);
2099
2100 if ( ind < INT_MAX )
2101 matind[cnt] = ind;
2102 else
2103 {
2104 /* correct number of variable already in map/array and remember to add a new row */
2105 SCIP_CALL( SCIPhashmapSetImageInt(conshdlrdata->slackhash, var, conshdlrdata->nrows) );
2106 assert( conshdlrdata->nrows == SCIPhashmapGetImageInt(conshdlrdata->slackhash, var) );
2107 SCIPdebugMsg(scip, "Inserted slack variable <%s> into hashmap (row: %d).\n", SCIPvarGetName(var), conshdlrdata->nrows);
2108 matind[cnt] = (conshdlrdata->nrows)++;
2109
2110 /* store new variables */
2111 newrowsslack[nnewrows++] = TRUE;
2112 }
2113 assert( conshdlrdata->nrows >= SCIPhashmapGetImageInt(conshdlrdata->slackhash, var) );
2114 matval[cnt++] = sign * vals[v];
2115 }
2116 }
2117 else
2118 {
2119 /* if variable exists */
2120 if ( SCIPhashmapExists(conshdlrdata->varhash, var) )
2121 matind[cnt] = SCIPhashmapGetImageInt(conshdlrdata->varhash, var);
2122 else
2123 {
2124 /* add variable in map and array and remember to add a new row */
2125 SCIP_CALL( SCIPhashmapInsertInt(conshdlrdata->varhash, var, conshdlrdata->nrows) );
2126 assert( conshdlrdata->nrows == SCIPhashmapGetImageInt(conshdlrdata->varhash, var) );
2127 SCIPdebugMsg(scip, "Inserted variable <%s> into hashmap (row: %d).\n", SCIPvarGetName(var), conshdlrdata->nrows);
2128 matind[cnt] = (conshdlrdata->nrows)++;
2129
2130 /* store new variables */
2131 newrowsslack[nnewrows++] = FALSE;
2132 newvars[nnewvars++] = var;
2133 }
2134 assert( SCIPhashmapExists(conshdlrdata->varhash, var) );
2135 matval[cnt++] = sign * vals[v];
2136 }
2137 }
2138
2139 /* add new rows */
2140 if ( nnewrows > 0 )
2141 {
2142 SCIP_Real* lhs;
2143 SCIP_Real* rhs;
2144 int i;
2145
2146 SCIP_CALL( SCIPallocBufferArray(scip, &lhs, nnewrows) );
2147 SCIP_CALL( SCIPallocBufferArray(scip, &rhs, nnewrows) );
2148 for (i = 0; i < nnewrows; ++i)
2149 {
2150 if ( newrowsslack[i] )
2151 lhs[i] = -SCIPlpiInfinity(conshdlrdata->altlp);
2152 else
2153 lhs[i] = 0.0;
2154 rhs[i] = 0.0;
2155 }
2156 /* add new rows */
2157 SCIP_CALL( SCIPlpiAddRows(conshdlrdata->altlp, nnewrows, lhs, rhs, NULL, 0, NULL, NULL, NULL) );
2158
2159 SCIPfreeBufferArray(scip, &lhs);
2160 SCIPfreeBufferArray(scip, &rhs);
2161 }
2162
2163 /* now add column */
2164 obj[0] = objcoef;
2165 if ( colfree )
2166 {
2167 /* create a free variable -> should only happen for additional linear constraints */
2168 assert( slackvar == NULL );
2169 lb[0] = -SCIPlpiInfinity(conshdlrdata->altlp);
2170 }
2171 else
2172 lb[0] = 0.0;
2173 ub[0] = SCIPlpiInfinity(conshdlrdata->altlp);
2174 matbeg[0] = 0;
2175
2176 SCIP_CALL( SCIPlpiAddCols(conshdlrdata->altlp, 1, obj, lb, ub, NULL, cnt, matbeg, matind, matval) );
2177
2178 /* add columns corresponding to bounds of original variables - no bounds needed for slack vars */
2179 cnt = 0;
2180 for (v = 0; v < nnewvars; ++v)
2181 {
2182 SCIP_VAR* var = newvars[v];
2183 assert( var != NULL );
2184
2185 /* if the lower bound is finite */
2186 val = SCIPvarGetLbGlobal(var);
2187 if ( ! SCIPisInfinity(scip, -val) )
2188 {
2189 matbeg[nnewcols] = cnt;
2190 if ( ! SCIPisZero(scip, val) )
2191 {
2192 matind[cnt] = 0;
2193 matval[cnt++] = -val;
2194 }
2195 assert( SCIPhashmapExists(conshdlrdata->varhash, var) );
2196
2197 matind[cnt] = SCIPhashmapGetImageInt(conshdlrdata->varhash, var);
2198 matval[cnt++] = -1.0;
2199 obj[nnewcols] = 0.0;
2200 lb[nnewcols] = 0.0;
2201 ub[nnewcols] = SCIPlpiInfinity(conshdlrdata->altlp);
2202 ++conshdlrdata->nlbbounds;
2203
2204 SCIP_CALL( SCIPhashmapInsertInt(conshdlrdata->lbhash, var, ncols + 1 + nnewcols) );
2205 assert( SCIPhashmapExists(conshdlrdata->lbhash, var) );
2206 SCIPdebugMsg(scip, "Added column for lower bound (%f) of variable <%s> to alternative polyhedron (col: %d).\n",
2207 val, SCIPvarGetName(var), ncols + 1 + nnewcols);
2208 ++nnewcols;
2209 }
2210
2211 /* if the upper bound is finite */
2212 val = SCIPvarGetUbGlobal(var);
2213 if ( ! SCIPisInfinity(scip, val) )
2214 {
2215 matbeg[nnewcols] = cnt;
2216 if ( ! SCIPisZero(scip, val) )
2217 {
2218 matind[cnt] = 0;
2219 matval[cnt++] = val;
2220 }
2221 assert( SCIPhashmapExists(conshdlrdata->varhash, var) );
2222
2223 matind[cnt] = SCIPhashmapGetImageInt(conshdlrdata->varhash, var);
2224 matval[cnt++] = 1.0;
2225 obj[nnewcols] = 0.0;
2226 lb[nnewcols] = 0.0;
2227 ub[nnewcols] = SCIPlpiInfinity(conshdlrdata->altlp);
2228 ++conshdlrdata->nubbounds;
2229
2230 SCIP_CALL( SCIPhashmapInsertInt(conshdlrdata->ubhash, var, ncols + 1 + nnewcols) );
2231 assert( SCIPhashmapExists(conshdlrdata->ubhash, var) );
2232 SCIPdebugMsg(scip, "Added column for upper bound (%f) of variable <%s> to alternative polyhedron (col: %d).\n",
2233 val, SCIPvarGetName(var), ncols + 1 + nnewcols);
2234 ++nnewcols;
2235 }
2236 }
2237
2238 /* add columns if necessary */
2239 if ( nnewcols > 0 )
2240 {
2241 SCIP_CALL( SCIPlpiAddCols(conshdlrdata->altlp, nnewcols, obj, lb, ub, NULL, cnt, matbeg, matind, matval) );
2242 }
2243
2244 #ifndef NDEBUG
2245 SCIP_CALL( SCIPlpiGetNCols(conshdlrdata->altlp, &cnt) );
2246 assert( cnt == ncols + nnewcols + 1 );
2247 #endif
2248
2249 SCIPfreeBufferArray(scip, &ub);
2250 SCIPfreeBufferArray(scip, &lb);
2251 SCIPfreeBufferArray(scip, &obj);
2252 SCIPfreeBufferArray(scip, &matind);
2253 SCIPfreeBufferArray(scip, &matval);
2254 SCIPfreeBufferArray(scip, &matbeg);
2255 SCIPfreeBufferArray(scip, &newvars);
2256 SCIPfreeBufferArray(scip, &newrowsslack);
2257
2258 conshdlrdata->scaled = FALSE;
2259
2260 #ifdef SCIP_OUTPUT
2261 SCIP_CALL( SCIPlpiWriteLP(conshdlrdata->altlp, "alt.lp") );
2262 #endif
2263
2264 return SCIP_OKAY;
2265 }
2266
2267
2268 /** add column corresponding to constraint to alternative LP
2269 *
2270 * See the description at the top of the file for more information.
2271 */
2272 static
2273 SCIP_RETCODE addAltLPConstraint(
2274 SCIP* scip, /**< SCIP pointer */
2275 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
2276 SCIP_CONS* lincons, /**< linear constraint */
2277 SCIP_VAR* slackvar, /**< slack variable or NULL */
2278 SCIP_Real objcoef, /**< objective coefficient */
2279 int* colindex /**< index of new column */
2280 )
2281 {
2282 SCIP_CONSHDLRDATA* conshdlrdata;
2283 SCIP_VAR** linvars;
2284 SCIP_Real* linvals;
2285 SCIP_Real linrhs;
2286 SCIP_Real linlhs;
2287 int nlinvars;
2288
2289 assert( scip != NULL );
2290 assert( conshdlr != NULL );
2291 assert( lincons != NULL );
2292 assert( colindex != NULL );
2293 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
2294
2295 *colindex = -1;
2296
2297 conshdlrdata = SCIPconshdlrGetData(conshdlr);
2298 assert( conshdlrdata != NULL );
2299
2300 /* if the slack variable is aggregated (multi-aggregation should not happen) */
2301 assert( slackvar == NULL || SCIPvarGetStatus(slackvar) != SCIP_VARSTATUS_MULTAGGR );
2302 if ( slackvar != NULL && SCIPvarGetStatus(slackvar) == SCIP_VARSTATUS_AGGREGATED )
2303 {
2304 SCIP_VAR* var;
2305 SCIP_Real scalar = 1.0;
2306 SCIP_Real constant = 0.0;
2307
2308 var = slackvar;
2309
2310 SCIP_CALL( SCIPgetProbvarSum(scip, &var, &scalar, &constant) );
2311
2312 SCIPdebugMsg(scip, "Slack variable is aggregated (scalar: %f, constant: %f).\n", scalar, constant);
2313
2314 /* if the slack variable is fixed */
2315 if ( SCIPisZero(scip, scalar) && ! SCIPconsIsActive(lincons) )
2316 return SCIP_OKAY;
2317
2318 /* otherwise construct a linear constraint */
2319 SCIP_CALL( SCIPallocBufferArray(scip, &linvars, 1) );
2320 SCIP_CALL( SCIPallocBufferArray(scip, &linvals, 1) );
2321 linvars[0] = var;
2322 linvals[0] = scalar;
2323 nlinvars = 1;
2324 linlhs = -SCIPinfinity(scip);
2325 linrhs = constant;
2326 }
2327 else
2328 {
2329 /* exit if linear constraint is not active */
2330 if ( ! SCIPconsIsActive(lincons) && slackvar != NULL )
2331 return SCIP_OKAY;
2332
2333 /* in this case, the linear constraint is directly usable */
2334 linvars = SCIPgetVarsLinear(scip, lincons);
2335 linvals = SCIPgetValsLinear(scip, lincons);
2336 nlinvars = SCIPgetNVarsLinear(scip, lincons);
2337 linlhs = SCIPgetLhsLinear(scip, lincons);
2338 linrhs = SCIPgetRhsLinear(scip, lincons);
2339 }
2340
2341 /* create column */
2342 if ( SCIPisEQ(scip, linlhs, linrhs) )
2343 {
2344 /* create free variable for equations (should only happen for additional linear constraints) */
2345 SCIP_CALL( addAltLPColumn(scip, conshdlr, conshdlrdata, slackvar, nlinvars, linvars, linvals, linrhs, objcoef, 1.0, TRUE, colindex) );
2346 }
2347 else if ( ! SCIPisInfinity(scip, linrhs) )
2348 {
2349 /* create column for rhs */
2350 SCIP_CALL( addAltLPColumn(scip, conshdlr, conshdlrdata, slackvar, nlinvars, linvars, linvals, linrhs, objcoef, 1.0, FALSE, colindex) );
2351 }
2352 else
2353 {
2354 /* create column for lhs */
2355 assert( ! SCIPisInfinity(scip, -linlhs) );
2356 SCIP_CALL( addAltLPColumn(scip, conshdlr, conshdlrdata, slackvar, nlinvars, linvars, linvals, linlhs, objcoef, -1.0, FALSE, colindex) );
2357 }
2358
2359 if ( slackvar != NULL && SCIPvarGetStatus(slackvar) == SCIP_VARSTATUS_AGGREGATED )
2360 {
2361 SCIPfreeBufferArray(scip, &linvals);
2362 SCIPfreeBufferArray(scip, &linvars);
2363 }
2364
2365 return SCIP_OKAY;
2366 }
2367
2368
2369 /** add column corresponding to row to alternative LP
2370 *
2371 * See the description at the top of the file for more information.
2372 */
2373 static
2374 SCIP_RETCODE addAltLPRow(
2375 SCIP* scip, /**< SCIP pointer */
2376 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
2377 SCIP_ROW* row, /**< row to add */
2378 SCIP_Real objcoef, /**< objective coefficient */
2379 int* colindex /**< index of new column */
2380 )
2381 {
2382 SCIP_CONSHDLRDATA* conshdlrdata;
2383 SCIP_COL** rowcols;
2384 SCIP_Real* rowvals;
2385 SCIP_VAR** rowvars;
2386 SCIP_Real rowrhs;
2387 SCIP_Real rowlhs;
2388 int nrowcols;
2389 int j;
2390
2391 assert( scip != NULL );
2392 assert( conshdlr != NULL );
2393 assert( row != NULL );
2394 assert( colindex != NULL );
2395 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
2396
2397 /* initialize data */
2398 *colindex = -1;
2399
2400 /* exit if row is not global */
2401 if ( SCIProwIsLocal(row) )
2402 return SCIP_OKAY;
2403
2404 conshdlrdata = SCIPconshdlrGetData(conshdlr);
2405 assert( conshdlrdata != NULL );
2406
2407 /* get row data */
2408 rowcols = SCIProwGetCols(row);
2409 rowvals = SCIProwGetVals(row);
2410 nrowcols = SCIProwGetNNonz(row);
2411 rowlhs = SCIProwGetLhs(row) - SCIProwGetConstant(row);
2412 rowrhs = SCIProwGetRhs(row) - SCIProwGetConstant(row);
2413
2414 SCIP_CALL( SCIPallocBufferArray(scip, &rowvars, nrowcols) );
2415 for (j = 0; j < nrowcols; ++j)
2416 {
2417 rowvars[j] = SCIPcolGetVar(rowcols[j]);
2418 assert( rowvars[j] != NULL );
2419 }
2420
2421 /* create column */
2422 if ( SCIPisEQ(scip, rowlhs, rowrhs) )
2423 {
2424 /* create free variable for equations (should only happen for additional linear constraints) */
2425 SCIP_CALL( addAltLPColumn(scip, conshdlr, conshdlrdata, NULL, nrowcols, rowvars, rowvals, rowrhs, objcoef, 1.0, TRUE, colindex) );
2426 }
2427 else if ( ! SCIPisInfinity(scip, rowrhs) )
2428 {
2429 /* create column for rhs */
2430 SCIP_CALL( addAltLPColumn(scip, conshdlr, conshdlrdata, NULL, nrowcols, rowvars, rowvals, rowrhs, objcoef, 1.0, FALSE, colindex) );
2431 }
2432 else
2433 {
2434 /* create column for lhs */
2435 assert( ! SCIPisInfinity(scip, -rowlhs) );
2436 SCIP_CALL( addAltLPColumn(scip, conshdlr, conshdlrdata, NULL, nrowcols, rowvars, rowvals, rowlhs, objcoef, -1.0, FALSE, colindex) );
2437 }
2438
2439 SCIPfreeBufferArray(scip, &rowvars);
2440
2441 return SCIP_OKAY;
2442 }
2443
2444
2445 /** try to add objective cut as column to alternative LP */
2446 static
2447 SCIP_RETCODE addObjcut(
2448 SCIP* scip, /**< SCIP pointer */
2449 SCIP_CONSHDLR* conshdlr /**< constraint handler */
2450 )
2451 {
2452 SCIP_CONSHDLRDATA* conshdlrdata;
2453 SCIP_VAR** objvars;
2454 SCIP_Real* objvals;
2455 SCIP_VAR** vars;
2456 int nobjvars = 0;
2457 int nvars;
2458 int v;
2459
2460 assert( scip != NULL );
2461 assert( conshdlr != NULL );
2462 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
2463
2464 conshdlrdata = SCIPconshdlrGetData(conshdlr);
2465 assert( conshdlrdata != NULL );
2466
2467 /* skip procedure if already added */
2468 if ( conshdlrdata->objcutindex >= 0 )
2469 return SCIP_OKAY;
2470
2471 /* check whether we can add objective cut: all indicator variables have zero objective */
2472 if ( ! conshdlrdata->objothervarsonly )
2473 return SCIP_OKAY;
2474
2475 assert( ! SCIPisInfinity(scip, conshdlrdata->objupperbound) );
2476 SCIPdebugMsg(scip, "Add objective cut to alternative LP (obj. bound: %g).\n", conshdlrdata->objupperbound);
2477
2478 SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) );
2479 SCIP_CALL( SCIPallocBufferArray(scip, &objvars, nvars) );
2480 SCIP_CALL( SCIPallocBufferArray(scip, &objvals, nvars) );
2481
2482 /* collect nonzeros */
2483 for (v = 0; v < nvars; ++v)
2484 {
2485 SCIP_VAR* var;
2486 SCIP_Real objval;
2487
2488 var = vars[v];
2489 assert( var != NULL );
2490 objval = SCIPvarGetObj(var);
2491
2492 /* skip variables with zero objective - this includes slack and indicator variables */
2493 if ( ! SCIPisZero(scip, objval) )
2494 {
2495 objvars[nobjvars] = var;
2496 objvals[nobjvars++] = objval;
2497 }
2498 }
2499
2500 /* create column (with rhs = upperbound, objective 0, and scaling factor 1.0) */
2501 SCIP_CALL( addAltLPColumn(scip, conshdlr, conshdlrdata, NULL, nobjvars, objvars, objvals, conshdlrdata->objupperbound, 0.0, 1.0, FALSE, &conshdlrdata->objcutindex) );
2502 assert( conshdlrdata->objcutindex >= 0 );
2503 conshdlrdata->objaltlpbound = conshdlrdata->objupperbound;
2504
2505 SCIPfreeBufferArray(scip, &objvals);
2506 SCIPfreeBufferArray(scip, &objvars);
2507
2508 return SCIP_OKAY;
2509 }
2510
2511
2512 /** delete column corresponding to constraint in alternative LP
2513 *
2514 * We currently just fix the corresponding variable to 0.
2515 */
2516 static
2517 SCIP_RETCODE deleteAltLPConstraint(
2518 SCIP* scip, /**< SCIP pointer */
2519 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
2520 SCIP_CONS* cons /**< indicator constraint */
2521 )
2522 {
2523 SCIP_CONSHDLRDATA* conshdlrdata;
2524
2525 assert( scip != NULL );
2526 assert( conshdlr != NULL );
2527 assert( cons != NULL );
2528 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
2529
2530 conshdlrdata = SCIPconshdlrGetData(conshdlr);
2531 assert( conshdlrdata != NULL );
2532
2533 if ( conshdlrdata->altlp != NULL )
2534 {
2535 SCIP_CONSDATA* consdata;
2536
2537 consdata = SCIPconsGetData(cons);
2538 assert( consdata != NULL );
2539
2540 if ( consdata->colindex >= 0 )
2541 {
2542 SCIP_CALL( fixAltLPVariable(conshdlrdata->altlp, consdata->colindex) );
2543 }
2544 consdata->colindex = -1;
2545
2546 SCIPdebugMsg(scip, "Fixed variable for column %d (constraint: <%s>) from alternative LP to 0.\n", consdata->colindex, SCIPconsGetName(cons));
2547 }
2548 conshdlrdata->scaled = FALSE;
2549
2550 return SCIP_OKAY;
2551 }
2552
2553
2554 /** update upper bound in alternative LP */
2555 static
2556 SCIP_RETCODE updateObjUpperbound(
2557 SCIP* scip, /**< SCIP pointer */
2558 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
2559 SCIP_CONSHDLRDATA* conshdlrdata /**< constraint handler data */
2560 )
2561 {
2562 SCIP_Real objbnd;
2563
2564 assert( scip != NULL );
2565 assert( conshdlrdata != NULL );
2566
2567 if ( ! conshdlrdata->useobjectivecut )
2568 return SCIP_OKAY;
2569
2570 if ( conshdlrdata->altlp == NULL )
2571 return SCIP_OKAY;
2572
2573 /* first check whether we can improve the upper bound */
2574 objbnd = SCIPgetUpperbound(scip);
2575 if ( ! SCIPisInfinity(scip, objbnd) )
2576 {
2577 if ( SCIPisObjIntegral(scip) )
2578 objbnd = SCIPfeasCeil(scip, objbnd) - (1.0 - SCIPcutoffbounddelta(scip));
2579 else
2580 objbnd -= SCIPcutoffbounddelta(scip);
2581
2582 if ( SCIPisLT(scip, objbnd, conshdlrdata->objupperbound) )
2583 conshdlrdata->objupperbound = objbnd;
2584 }
2585
2586 if ( SCIPisInfinity(scip, conshdlrdata->objupperbound) )
2587 return SCIP_OKAY;
2588
2589 /* if we can improve on the bound stored in the alternative LP */
2590 if ( SCIPisLT(scip, conshdlrdata->objupperbound, conshdlrdata->objaltlpbound) )
2591 {
2592 SCIPdebugMsg(scip, "Update objective bound to %g.\n", conshdlrdata->objupperbound);
2593
2594 /* possibly add column for objective cut */
2595 if ( conshdlrdata->objcutindex < 0 )
2596 {
2597 SCIP_CALL( addObjcut(scip, conshdlr) );
2598 }
2599 else
2600 {
2601 #ifndef NDEBUG
2602 SCIP_Real oldbnd;
2603 SCIP_CALL( SCIPlpiGetCoef(conshdlrdata->altlp, 0, conshdlrdata->objcutindex, &oldbnd) );
2604 assert( SCIPisEQ(scip, oldbnd, conshdlrdata->objaltlpbound) );
2605 #endif
2606
2607 /* update bound */
2608 SCIP_CALL( SCIPlpiChgCoef(conshdlrdata->altlp, 0, conshdlrdata->objcutindex, conshdlrdata->objupperbound) );
2609 conshdlrdata->objaltlpbound = conshdlrdata->objupperbound;
2610
2611 #ifdef SCIP_OUTPUT
2612 SCIP_CALL( SCIPlpiWriteLP(conshdlrdata->altlp, "alt.lp") );
2613 #endif
2614 }
2615 }
2616
2617 return SCIP_OKAY;
2618 }
2619
2620
2621 /** check whether the given LP is infeasible
2622 *
2623 * If @a primal is false we assume that the problem is <em>dual feasible</em>, e.g., the problem
2624 * was only changed by fixing bounds!
2625 *
2626 * This is the workhorse for all methods that have to solve the alternative LP. We try in several
2627 * ways to recover from possible stability problems.
2628 *
2629 * @pre It is assumed that all parameters for the alternative LP are set.
2630 */
2631 static
2632 SCIP_RETCODE checkAltLPInfeasible(
2633 SCIP* scip, /**< SCIP pointer */
2634 SCIP_LPI* lp, /**< LP */
2635 SCIP_Real maxcondition, /**< maximal allowed condition of LP solution basis matrix */
2636 SCIP_Bool primal, /**< whether we are using the primal or dual simplex */
2637 SCIP_Bool* infeasible, /**< output: whether the LP is infeasible */
2638 SCIP_Bool* error /**< output: whether an error occurred */
2639 )
2640 {
2641 SCIP_RETCODE retcode;
2642 SCIP_Real condition;
2643
2644 assert( scip != NULL );
2645 assert( lp != NULL );
2646 assert( infeasible != NULL );
2647 assert( error != NULL );
2648
2649 *error = FALSE;
2650
2651 /* solve LP */
2652 if ( primal )
2653 retcode = SCIPlpiSolvePrimal(lp); /* use primal simplex */
2654 else
2655 retcode = SCIPlpiSolveDual(lp); /* use dual simplex */
2656 if ( retcode == SCIP_LPERROR )
2657 {
2658 *error = TRUE;
2659 return SCIP_OKAY;
2660 }
2661 SCIP_CALL( retcode );
2662
2663 /* resolve if LP is not stable */
2664 if ( ! SCIPlpiIsStable(lp) )
2665 {
2666 SCIP_CALL_PARAM( SCIPlpiSetIntpar(lp, SCIP_LPPAR_FROMSCRATCH, TRUE) );
2667 SCIP_CALL_PARAM( SCIPlpiSetIntpar(lp, SCIP_LPPAR_PRESOLVING, FALSE) );
2668 SCIPwarningMessage(scip, "Numerical problems, retrying ...\n");
2669
2670 /* re-solve LP */
2671 if ( primal )
2672 retcode = SCIPlpiSolvePrimal(lp); /* use primal simplex */
2673 else
2674 retcode = SCIPlpiSolveDual(lp); /* use dual simplex */
2675
2676 /* reset parameters */
2677 SCIP_CALL_PARAM( SCIPlpiSetIntpar(lp, SCIP_LPPAR_FROMSCRATCH, FALSE) );
2678 SCIP_CALL_PARAM( SCIPlpiSetIntpar(lp, SCIP_LPPAR_PRESOLVING, TRUE) );
2679
2680 if ( retcode == SCIP_LPERROR )
2681 {
2682 *error = TRUE;
2683 return SCIP_OKAY;
2684 }
2685 SCIP_CALL( retcode );
2686 }
2687
2688 /* check whether we want to ignore the result, because the condition number is too large */
2689 if ( maxcondition > 0.0 )
2690 {
2691 /* check estimated condition number of basis matrix */
2692 SCIP_CALL( SCIPlpiGetRealSolQuality(lp, SCIP_LPSOLQUALITY_ESTIMCONDITION, &condition) );
2693 if ( condition != SCIP_INVALID && condition > maxcondition ) /*lint !e777*/
2694 {
2695 SCIPdebugMsg(scip, "Estimated condition number of basis matrix (%e) exceeds maximal allowance (%e).\n", condition, maxcondition);
2696
2697 *error = TRUE;
2698
2699 return SCIP_OKAY;
2700 }
2701 else if ( condition != SCIP_INVALID ) /*lint !e777*/
2702 {
2703 SCIPdebugMsg(scip, "Estimated condition number of basis matrix (%e) is below maximal allowance (%e).\n", condition, maxcondition);
2704 }
2705 else
2706 {
2707 SCIPdebugMsg(scip, "Estimated condition number of basis matrix not available.\n");
2708 }
2709 }
2710
2711 /* check whether we are in the paradoxical situation that
2712 * - the primal is not infeasible
2713 * - the primal is not unbounded
2714 * - the LP is not optimal
2715 * - we have a primal ray
2716 *
2717 * If we ran the dual simplex algorithm, then we run again with the primal simplex
2718 */
2719 if ( ! SCIPlpiIsPrimalInfeasible(lp) && ! SCIPlpiIsPrimalUnbounded(lp) &&
2720 ! SCIPlpiIsOptimal(lp) && SCIPlpiExistsPrimalRay(lp) && ! primal )
2721 {
2722 SCIPwarningMessage(scip, "The dual simplex produced a primal ray. Retrying with primal ...\n");
2723
2724 /* the following settings might be changed: */
2725 SCIP_CALL_PARAM( SCIPlpiSetIntpar(lp, SCIP_LPPAR_FROMSCRATCH, TRUE) );
2726 SCIP_CALL_PARAM( SCIPlpiSetIntpar(lp, SCIP_LPPAR_PRESOLVING, TRUE) );
2727 SCIP_CALL_PARAM( SCIPlpiSetIntpar(lp, SCIP_LPPAR_SCALING, 1) );
2728
2729 SCIP_CALL( SCIPlpiSolvePrimal(lp) ); /* use primal simplex */
2730
2731 /* reset parameters */
2732 SCIP_CALL_PARAM( SCIPlpiSetIntpar(lp, SCIP_LPPAR_FROMSCRATCH, FALSE) );
2733 SCIP_CALL_PARAM( SCIPlpiSetIntpar(lp, SCIP_LPPAR_PRESOLVING, TRUE) );
2734 SCIP_CALL_PARAM( SCIPlpiSetIntpar(lp, SCIP_LPPAR_SCALING, 1) );
2735 }
2736
2737 /* examine LP solution status */
2738 if ( SCIPlpiIsPrimalInfeasible(lp) ) /* the LP is provably infeasible */
2739 {
2740 assert( ! SCIPlpiIsPrimalUnbounded(lp) ); /* can't be unbounded or optimal */
2741 assert( ! SCIPlpiIsOptimal(lp) ); /* if it is infeasible! */
2742
2743 *infeasible = TRUE; /* LP is infeasible */
2744 return SCIP_OKAY;
2745 }
2746 else
2747 {
2748 /* By assumption the dual is feasible if the dual simplex is run, therefore
2749 * the status has to be primal unbounded or optimal. */
2750 if ( ! SCIPlpiIsPrimalUnbounded(lp) && ! SCIPlpiIsOptimal(lp) )
2751 {
2752 /* We have a status different from unbounded or optimal. This should not be the case ... */
2753 if (primal)
2754 SCIPwarningMessage(scip, "Primal simplex returned with unknown status: %d\n", SCIPlpiGetInternalStatus(lp));
2755 else
2756 SCIPwarningMessage(scip, "Dual simplex returned with unknown status: %d\n", SCIPlpiGetInternalStatus(lp));
2757
2758 /* SCIP_CALL( SCIPlpiWriteLP(lp, "debug.lp") ); */
2759 *error = TRUE;
2760 return SCIP_OKAY;
2761 }
2762 }
2763
2764 /* at this point we have a feasible solution */
2765 *infeasible = FALSE;
2766 return SCIP_OKAY;
2767 }
2768
2769
2770 /** tries to extend a given set of variables to a cover
2771 *
2772 * At each step we include a variable which covers a new IIS. The corresponding IIS inequalities are added to the LP,
2773 * if this not already happened.
2774 *
2775 * @pre It is assumed that all parameters for the alternative LP are set and that the variables
2776 * corresponding to @a S are fixed. Furthermore @c xVal_ should contain the current LP solution.
2777 */
2778 static
2779 SCIP_RETCODE extendToCover(
2780 SCIP* scip, /**< SCIP pointer */
2781 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
2782 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
2783 SCIP_LPI* lp, /**< LP */
2784 SCIP_SOL* sol, /**< solution to be separated */
2785 SCIP_ENFOSEPATYPE enfosepatype, /**< type of enforcing/separating type */
2786 SCIP_Bool removable, /**< whether cuts should be removable */
2787 SCIP_Bool genlogicor, /**< should logicor constraints be generated? */
2788 int nconss, /**< number of constraints */
2789 SCIP_CONS** conss, /**< indicator constraints */
2790 SCIP_Bool* S, /**< bitset of variables */
2791 int* size, /**< size of S */
2792 SCIP_Real* value, /**< objective value of S */
2793 SCIP_Bool* error, /**< output: whether an error occurred */
2794 SCIP_Bool* cutoff, /**< whether we detected a cutoff by an infeasible inequality */
2795 int* nGen /**< number of generated cuts */
2796 )
2797 {
2798 #ifdef SCIP_DEBUG
2799 char name[SCIP_MAXSTRLEN];
2800 #endif
2801 SCIP_Real* primsol;
2802 int nnonviolated = 0;
2803 int step = 0;
2804 int nCols;
2805
2806 assert( scip != NULL );
2807 assert( lp != NULL );
2808 assert( conss != NULL );
2809 assert( S != NULL );
2810 assert( size != NULL );
2811 assert( value != NULL );
2812 assert( error != NULL );
2813 assert( cutoff != NULL );
2814 assert( nGen != NULL );
2815
2816 *error = FALSE;
2817 *cutoff = FALSE;
2818 *nGen = 0;
2819
2820 SCIP_CALL( SCIPlpiGetNCols(lp, &nCols) );
2821 SCIP_CALL( SCIPallocBufferArray(scip, &primsol, nCols) );
2822 assert( nconss <= nCols );
2823
2824 do
2825 {
2826 SCIP_Bool infeasible;
2827 SCIP_Real sum = 0.0;
2828 SCIP_Real candobj = -1.0;
2829 SCIP_Real candval = 2.0;
2830 SCIP_Real norm = 1.0;
2831 int sizeIIS = 0;
2832 int candidate = -1;
2833 int candindex = -1;
2834 int j;
2835
2836 if ( step == 0 )
2837 {
2838 /* the first LP is solved without warm start, after that we use a warmstart. */
2839 SCIP_CALL_PARAM( SCIPlpiSetIntpar(lp, SCIP_LPPAR_FROMSCRATCH, TRUE) );
2840 SCIP_CALL( checkAltLPInfeasible(scip, lp, conshdlrdata->maxconditionaltlp, TRUE, &infeasible, error) );
2841 SCIP_CALL_PARAM( SCIPlpiSetIntpar(lp, SCIP_LPPAR_FROMSCRATCH, FALSE) );
2842 }
2843 else
2844 SCIP_CALL( checkAltLPInfeasible(scip, lp, conshdlrdata->maxconditionaltlp, FALSE, &infeasible, error) );
2845
2846 if ( *error )
2847 break;
2848
2849 /* if the alternative polyhedron is infeasible, we found a cover */
2850 if ( infeasible )
2851 {
2852 /* Note: checking for a primal solution is done in extendToCover(). */
2853 SCIPdebugMsg(scip, " size: %4d produced possible cover with indicator variable objective value %f.\n", *size, *value);
2854
2855 /* we currently cannot call probing if there are cuts in the sepastore; @todo fix this */
2856 if ( conshdlrdata->trysolfromcover )
2857 {
2858 /* Check whether we want to try to construct a feasible solution: there should be no integer/binary variables
2859 * except the indicator variables. Thus, there should be no integral variables and the number of indicator
2860 * variables should be at least (actually equal to) the number of binary variables. */
2861 if ( SCIPgetNIntVars(scip) == 0 && nconss >= SCIPgetNBinVars(scip) )
2862 {
2863 SCIP_HEUR* heurindicator;
2864
2865 heurindicator = SCIPfindHeur(scip, "indicator");
2866 if ( heurindicator == NULL )
2867 {
2868 SCIPerrorMessage("Could not find heuristic \"indicator\".\n");
2869 return SCIP_PLUGINNOTFOUND;
2870 }
2871
2872 SCIP_CALL( SCIPheurPassIndicator(scip, heurindicator, nconss, conss, S, -*value) );
2873 SCIPdebugMsg(scip, "Passed feasible solution to indicator heuristic.\n");
2874 }
2875 }
2876 break;
2877 }
2878
2879 /* get solution of alternative LP */
2880 SCIP_CALL( SCIPlpiGetSol(lp, NULL, primsol, NULL, NULL, NULL) );
2881
2882 /* get value of cut and find candidate for variable to add */
2883 for (j = 0; j < nconss; ++j)
2884 {
2885 SCIP_CONSDATA* consdata;
2886 int ind;
2887
2888 consdata = SCIPconsGetData(conss[j]);
2889 assert( consdata != NULL );
2890 ind = consdata->colindex;
2891
2892 if ( ind >= 0 )
2893 {
2894 assert( ind < nCols );
2895
2896 /* check support of the solution, i.e., the corresponding IIS */
2897 if ( ! SCIPisFeasZero(scip, primsol[ind]) )
2898 {
2899 SCIP_Real val;
2900
2901 assert( ! S[j] );
2902 ++sizeIIS;
2903 val = SCIPgetSolVal(scip, sol, consdata->binvar);
2904 sum += val;
2905
2906 /* take element with smallest relaxation value */
2907 if ( val < candval )
2908 {
2909 candidate = j;
2910 candindex = ind;
2911 candval = val;
2912 candobj = varGetObjDelta(consdata->binvar);
2913 }
2914 }
2915 }
2916 }
2917
2918 /* check for error */
2919 if ( candidate < 0 )
2920 {
2921 /* Because of numerical problems it might happen that the solution primsol above is zero
2922 * within the tolerances. In this case we quit. */
2923 break;
2924 }
2925 assert( candidate >= 0 );
2926 assert( ! S[candidate] );
2927 assert( sizeIIS > 0 );
2928
2929 /* get the type of norm to use for efficacy calculations */
2930 switch ( conshdlrdata->normtype )
2931 {
2932 case 'e':
2933 norm = sqrt((SCIP_Real) sizeIIS);
2934 break;
2935 case 'm':
2936 norm = 1.0;
2937 break;
2938 case 's':
2939 norm = (SCIP_Real) sizeIIS;
2940 break;
2941 case 'd':
2942 norm = 1.0;
2943 break;
2944 default:
2945 SCIPerrorMessage("Invalid efficacy norm parameter '%c'.\n", conshdlrdata->normtype);
2946 SCIPABORT();
2947 norm = 1.0; /*lint !e527*/
2948 }
2949
2950 SCIPdebugMsg(scip, " size: %4d, add var. %4d (obj: %-6g, alt-LP sol: %-8.4f); IIS size: %4d, eff.: %g.\n",
2951 *size, candidate, candobj, primsol[SCIPconsGetData(conss[candidate])->colindex], sizeIIS, (sum - (SCIP_Real) (sizeIIS - 1))/norm);
2952
2953 /* update new set S */
2954 S[candidate] = TRUE;
2955 ++(*size);
2956 *value += candobj;
2957
2958 /* fix chosen variable to 0 */
2959 SCIP_CALL( fixAltLPVariable(lp, candindex) );
2960
2961 /* if cut is violated, i.e., sum - sizeIIS + 1 > 0 */
2962 if ( SCIPisEfficacious(scip, (sum - (SCIP_Real) (sizeIIS - 1))/norm) )
2963 {
2964 SCIP_Bool isLocal = FALSE;
2965
2966 #ifdef SCIP_ENABLE_IISCHECK
2967 /* check whether we really have an infeasible subsystem */
2968 SCIP_CALL( checkIIS(scip, nconss, conss, primsol) );
2969 #endif
2970
2971 /* check whether IIS corresponds to a local cut */
2972 if ( conshdlrdata->updatebounds )
2973 {
2974 SCIP_CALL( checkIISlocal(scip, conshdlrdata, primsol, &isLocal) );
2975 }
2976
2977 if ( genlogicor )
2978 {
2979 SCIP_RESULT result;
2980 SCIP_CONS* cons;
2981 SCIP_VAR** vars;
2982 int cnt = 0;
2983
2984 SCIP_CALL( SCIPallocBufferArray(scip, &vars, nconss) );
2985
2986 /* collect variables corresponding to support to cut */
2987 for (j = 0; j < nconss; ++j)
2988 {
2989 SCIP_CONSDATA* consdata;
2990 int ind;
2991
2992 consdata = SCIPconsGetData(conss[j]);
2993 ind = consdata->colindex;
2994
2995 if ( ind >= 0 )
2996 {
2997 assert( ind < nCols );
2998 assert( consdata->binvar != NULL );
2999
3000 /* check support of the solution, i.e., the corresponding IIS */
3001 if ( ! SCIPisFeasZero(scip, primsol[ind]) )
3002 {
3003 SCIP_VAR* var;
3004 SCIP_CALL( SCIPgetNegatedVar(scip, consdata->binvar, &var) );
3005 vars[cnt++] = var;
3006 }
3007 }
3008 }
3009 assert( cnt == sizeIIS );
3010
3011 #ifdef SCIP_DEBUG
3012 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "iis%d", conshdlrdata->niiscutsgen + *nGen);
3013 SCIP_CALL( SCIPcreateConsLogicor(scip, &cons, name, cnt, vars, FALSE, TRUE, TRUE, TRUE, TRUE, isLocal, FALSE, TRUE, removable, FALSE) );
3014 #else
3015 SCIP_CALL( SCIPcreateConsLogicor(scip, &cons, "", cnt, vars, FALSE, TRUE, TRUE, TRUE, TRUE, isLocal, FALSE, TRUE, removable, FALSE) );
3016 #endif
3017
3018 #ifdef SCIP_OUTPUT
3019 SCIP_CALL( SCIPprintCons(scip, cons, NULL) );
3020 SCIPinfoMessage(scip, NULL, ";\n");
3021 #endif
3022
3023 /* enforce or separate logicor constraint to make sure that this has an effect in this round */
3024 switch ( enfosepatype )
3025 {
3026 case SCIP_TYPE_ENFOLP:
3027 SCIP_CALL( SCIPenfolpCons(scip, cons, FALSE, &result) );
3028 break;
3029 case SCIP_TYPE_ENFOPS:
3030 SCIP_CALL( SCIPenfopsCons(scip, cons, FALSE, FALSE, &result) );
3031 break;
3032 case SCIP_TYPE_ENFORELAX:
3033 SCIP_CALL( SCIPenforelaxCons(scip, cons, sol, FALSE, &result) );
3034 break;
3035 case SCIP_TYPE_SEPALP:
3036 SCIP_CALL( SCIPsepalpCons(scip, cons, &result) );
3037 break;
3038 case SCIP_TYPE_SEPARELAX:
3039 case SCIP_TYPE_SEPASOL:
3040 SCIP_CALL( SCIPsepasolCons(scip, cons, sol, &result) );
3041 break;
3042 default:
3043 SCIPerrorMessage("Wrong enforcing/separation type.\n");
3044 SCIPABORT();
3045 }
3046
3047 SCIP_CALL( SCIPaddCons(scip, cons) );
3048 SCIP_CALL( SCIPreleaseCons(scip, &cons) );
3049
3050 SCIPfreeBufferArray(scip, &vars);
3051 ++(*nGen);
3052 }
3053 else
3054 {
3055 SCIP_ROW* row;
3056
3057 /* create row */
3058 #ifdef SCIP_DEBUG
3059 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "iis%d", conshdlrdata->niiscutsgen + *nGen);
3060 SCIP_CALL( SCIPcreateEmptyRowConshdlr(scip, &row, conshdlr, name, -SCIPinfinity(scip), (SCIP_Real) (sizeIIS - 1), isLocal, FALSE, removable) );
3061 #else
3062 SCIP_CALL( SCIPcreateEmptyRowConshdlr(scip, &row, conshdlr, "", -SCIPinfinity(scip), (SCIP_Real) (sizeIIS - 1), isLocal, FALSE, removable) );
3063 #endif
3064 SCIP_CALL( SCIPcacheRowExtensions(scip, row) );
3065
3066 /* add variables corresponding to support to cut */
3067 for (j = 0; j < nconss; ++j)
3068 {
3069 int ind;
3070 SCIP_CONSDATA* consdata;
3071
3072 consdata = SCIPconsGetData(conss[j]);
3073 ind = consdata->colindex;
3074
3075 if ( ind >= 0 )
3076 {
3077 assert( ind < nCols );
3078 assert( consdata->binvar != NULL );
3079
3080 /* check support of the solution, i.e., the corresponding IIS */
3081 if ( ! SCIPisFeasZero(scip, primsol[ind]) )
3082 {
3083 SCIP_VAR* var = consdata->binvar;
3084 SCIP_CALL( SCIPaddVarToRow(scip, row, var, 1.0) );
3085 }
3086 }
3087 }
3088 SCIP_CALL( SCIPflushRowExtensions(scip, row) );
3089 #ifdef SCIP_OUTPUT
3090 SCIP_CALL( SCIPprintRow(scip, row, NULL) );
3091 #endif
3092 SCIP_CALL( SCIPaddRow(scip, row, FALSE, cutoff) );
3093 if ( *cutoff )
3094 {
3095 SCIPfreeBufferArray(scip, &primsol);
3096 return SCIP_OKAY;
3097 }
3098
3099 /* cut should be violated: */
3100 assert( SCIPisFeasNegative(scip, SCIPgetRowSolFeasibility(scip, row, sol)) );
3101
3102 /* add cuts to pool if they are globally valid */
3103 if ( ! isLocal )
3104 SCIP_CALL( SCIPaddPoolCut(scip, row) );
3105 SCIP_CALL( SCIPreleaseRow(scip, &row));
3106 ++(*nGen);
3107 }
3108 nnonviolated = 0;
3109 }
3110 else
3111 ++nnonviolated;
3112 ++step;
3113
3114 if ( nnonviolated > conshdlrdata->maxsepanonviolated )
3115 {
3116 SCIPdebugMsg(scip, "Stop separation after %d non violated IISs.\n", nnonviolated);
3117 break;
3118 }
3119 }
3120 while (step < nconss);
3121
3122 SCIPfreeBufferArray(scip, &primsol);
3123
3124 return SCIP_OKAY;
3125 }
3126
3127
3128 /* ---------------------------- constraint handler local methods ----------------------*/
3129
3130 /** creates and initializes consdata */
3131 static
3132 SCIP_RETCODE consdataCreate(
3133 SCIP* scip, /**< SCIP data structure */
3134 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
3135 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
3136 const char* consname, /**< name of constraint (or NULL) */
3137 SCIP_CONSDATA** consdata, /**< pointer to linear constraint data */
3138 SCIP_EVENTHDLR* eventhdlrbound, /**< event handler for bound change events */
3139 SCIP_EVENTHDLR* eventhdlrrestart, /**< event handler for handling restarts */
3140 SCIP_VAR* binvar, /**< binary variable (or NULL) */
3141 SCIP_Bool activeone, /**< whether the constraint is active on 1 or not */
3142 SCIP_Bool lessthanineq, /**< whether the original linear constraint is a less-than-rhs (TRUE) or not */
3143 SCIP_VAR* slackvar, /**< slack variable */
3144 SCIP_CONS* lincons, /**< linear constraint (or NULL) */
3145 SCIP_Bool linconsactive /**< whether the linear constraint is active */
3146 )
3147 {
3148 SCIP_VAR* binvarinternal;
3149
3150 assert( scip != NULL );
3151 assert( conshdlr != NULL );
3152 assert( conshdlrdata != NULL );
3153 assert( consdata != NULL );
3154 assert( slackvar != NULL );
3155 assert( eventhdlrbound != NULL );
3156 assert( eventhdlrrestart != NULL );
3157
3158 /* if active on 0, the binary variable is reversed */
3159 if ( activeone )
3160 {
3161 binvarinternal = binvar;
3162 }
3163 else
3164 {
3165 SCIP_CALL ( SCIPgetNegatedVar(scip, binvar, &binvarinternal) );
3166 }
3167
3168 /* create constraint data */
3169 SCIP_CALL( SCIPallocBlockMemory(scip, consdata) );
3170 (*consdata)->nfixednonzero = 0;
3171 (*consdata)->colindex = -1;
3172 (*consdata)->linconsactive = linconsactive;
3173 (*consdata)->binvar = binvarinternal;
3174 (*consdata)->slackvar = slackvar;
3175 (*consdata)->activeone = activeone;
3176 (*consdata)->lessthanineq = lessthanineq;
3177 (*consdata)->lincons = lincons;
3178 (*consdata)->implicationadded = FALSE;
3179 (*consdata)->slacktypechecked = FALSE;
3180
3181 /* if we are transformed, obtain transformed variables and catch events */
3182 if ( SCIPisTransformed(scip) )
3183 {
3184 SCIP_VAR* var;
3185
3186 /* handle binary variable */
3187 if ( binvarinternal != NULL )
3188 {
3189 SCIP_CALL( SCIPgetTransformedVar(scip, binvarinternal, &var) );
3190 assert( var != NULL );
3191 (*consdata)->binvar = var;
3192
3193 /* check type */
3194 if ( SCIPvarGetType(var) != SCIP_VARTYPE_BINARY )
3195 {
3196 SCIPerrorMessage("Indicator variable <%s> is not binary %d.\n", SCIPvarGetName(var), SCIPvarGetType(var));
3197 return SCIP_ERROR;
3198 }
3199
3200 /* the indicator variable must not be multi-aggregated because the constraint handler propagation tries
3201 * to tighten its bounds, which is not allowed for multi-aggregated variables
3202 */
3203 SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, var) );
3204
3205 /* catch local bound change events on binary variable */
3206 if ( linconsactive )
3207 {
3208 SCIP_CALL( SCIPcatchVarEvent(scip, var, SCIP_EVENTTYPE_BOUNDCHANGED, eventhdlrbound, (SCIP_EVENTDATA*)*consdata, NULL) );
3209 }
3210
3211 /* catch global bound change events on binary variable */
3212 if ( conshdlrdata->forcerestart )
3213 {
3214 SCIPdebugMsg(scip, "Catching GBDCHANGED event for <%s>.\n", SCIPvarGetName(var));
3215 SCIP_CALL( SCIPcatchVarEvent(scip, var, SCIP_EVENTTYPE_GBDCHANGED, eventhdlrrestart, (SCIP_EVENTDATA*) conshdlrdata, NULL) );
3216 }
3217
3218 /* if binary variable is fixed to be nonzero */
3219 if ( SCIPvarGetLbLocal(var) > 0.5 )
3220 ++((*consdata)->nfixednonzero);
3221 }
3222
3223 /* handle slack variable */
3224 SCIP_CALL( SCIPgetTransformedVar(scip, slackvar, &var) );
3225 assert( var != NULL );
3226 (*consdata)->slackvar = var;
3227
3228 /* catch bound change events on slack variable and adjust nfixednonzero */
3229 if ( linconsactive )
3230 {
3231 SCIP_CALL( SCIPcatchVarEvent(scip, var, SCIP_EVENTTYPE_BOUNDCHANGED, eventhdlrbound, (SCIP_EVENTDATA*)*consdata, NULL) );
3232
3233 /* if slack variable is fixed to be nonzero */
3234 if ( SCIPisFeasPositive(scip, SCIPvarGetLbLocal(var)) )
3235 ++((*consdata)->nfixednonzero);
3236 }
3237
3238 /* add corresponding column to alternative LP if the constraint is new */
3239 if ( conshdlrdata->sepaalternativelp && SCIPgetStage(scip) >= SCIP_STAGE_INITSOLVE && lincons != NULL )
3240 {
3241 assert( lincons != NULL );
3242 assert( consname != NULL );
3243
3244 SCIP_CALL( addAltLPConstraint(scip, conshdlr, lincons, var, 1.0, &(*consdata)->colindex) );
3245
3246 SCIPdebugMsg(scip, "Added column for <%s> to alternative LP with column index %d.\n", consname, (*consdata)->colindex);
3247 #ifdef SCIP_OUTPUT
3248 SCIP_CALL( SCIPprintCons(scip, lincons, NULL) );
3249 SCIPinfoMessage(scip, NULL, ";\n");
3250 #endif
3251 }
3252
3253 #ifdef SCIP_DEBUG
3254 if ( (*consdata)->nfixednonzero > 0 )
3255 {
3256 SCIPdebugMsg(scip, "Constraint <%s> has %d variables fixed to be nonzero.\n", consname, (*consdata)->nfixednonzero);
3257 }
3258 #endif
3259 }
3260
3261 return SCIP_OKAY;
3262 }
3263
3264
3265 /** create variable upper bounds for constraints */
3266 static
3267 SCIP_RETCODE createVarUbs(
3268 SCIP* scip, /**< SCIP pointer */
3269 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
3270 SCIP_CONS** conss, /**< constraints */
3271 int nconss, /**< number of constraints */
3272 int* ngen /**< number of successful operations */
3273 )
3274 {
3275 char name[50];
3276 int c;
3277
3278 assert( scip != NULL );
3279 assert( conshdlrdata != NULL );
3280 assert( ngen != NULL );
3281
3282 *ngen = 0;
3283
3284 /* check each constraint */
3285 for (c = 0; c < nconss; ++c)
3286 {
3287 SCIP_CONSDATA* consdata;
3288 SCIP_Real ub;
3289
3290 consdata = SCIPconsGetData(conss[c]);
3291 assert( consdata != NULL );
3292
3293 ub = SCIPvarGetUbGlobal(consdata->slackvar);
3294 assert( ! SCIPisNegative(scip, ub) );
3295
3296 /* insert corresponding row if helpful and coefficient is not too large */
3297 if ( ub <= conshdlrdata->maxcouplingvalue )
3298 {
3299 SCIP_CONS* cons;
3300
3301 #ifndef NDEBUG
3302 (void) SCIPsnprintf(name, 50, "couple%d", c);
3303 #else
3304 name[0] = '\0';
3305 #endif
3306
3307 SCIPdebugMsg(scip, "Insert coupling varbound constraint for indicator constraint <%s> (coeff: %f).\n", SCIPconsGetName(conss[c]), ub);
3308
3309 /* add variable upper bound:
3310 * - check constraint if we remove the indicator constraint afterwards
3311 * - constraint is dynamic if we do not remove indicator constraints
3312 * - constraint is removable if we do not remove indicator constraints
3313 */
3314 SCIP_CALL( SCIPcreateConsVarbound(scip, &cons, name, consdata->slackvar, consdata->binvar, ub, -SCIPinfinity(scip), ub,
3315 TRUE, TRUE, TRUE, conshdlrdata->removeindicators, TRUE, FALSE, FALSE,
3316 !conshdlrdata->removeindicators, !conshdlrdata->removeindicators, FALSE) );
3317
3318 SCIP_CALL( SCIPaddCons(scip, cons) );
3319 SCIP_CALL( SCIPreleaseCons(scip, &cons) );
3320
3321 /* remove indicator constraint if required */
3322 if ( conshdlrdata->removeindicators )
3323 {
3324 SCIPdebugMsg(scip, "Removing indicator constraint <%s>.\n", SCIPconsGetName(conss[c]));
3325 assert( ! SCIPconsIsModifiable(conss[c]) );
3326
3327 /* mark linear constraint to be upgrade-able */
3328 if ( SCIPconsIsActive(consdata->lincons) )
3329 {
3330 SCIPconsAddUpgradeLocks(consdata->lincons, -1);
3331 assert( SCIPconsGetNUpgradeLocks(consdata->lincons) == 0 );
3332 }
3333
3334 SCIP_CALL( SCIPdelCons(scip, conss[c]) );
3335 }
3336
3337 ++(*ngen);
3338 }
3339 }
3340
3341 return SCIP_OKAY;
3342 }
3343
3344
3345 /** perform one presolving round */
3346 static
3347 SCIP_RETCODE presolRoundIndicator(
3348 SCIP* scip, /**< SCIP pointer */
3349 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
3350 SCIP_CONS* cons, /**< constraint */
3351 SCIP_CONSDATA* consdata, /**< constraint data */
3352 SCIP_Bool dualreductions, /**< should dual reductions be performed? */
3353 SCIP_Bool* cutoff, /**< whether a cutoff happened */
3354 SCIP_Bool* success, /**< whether we performed a successful reduction */
3355 int* ndelconss, /**< number of deleted constraints */
3356 int* nfixedvars /**< number of fixed variables */
3357 )
3358 {
3359 SCIP_Bool infeasible;
3360 SCIP_Bool fixed;
3361
3362 assert( scip != NULL );
3363 assert( cons != NULL );
3364 assert( consdata != NULL );
3365 assert( cutoff != NULL );
3366 assert( success != NULL );
3367 assert( ndelconss != NULL );
3368 assert( nfixedvars != NULL );
3369 assert( consdata->binvar != NULL );
3370 assert( consdata->slackvar != NULL );
3371
3372 *cutoff = FALSE;
3373 *success = FALSE;
3374
3375 /* if the binary variable is fixed to nonzero */
3376 if ( SCIPvarGetLbLocal(consdata->binvar) > 0.5 )
3377 {
3378 SCIPdebugMsg(scip, "Presolving <%s>: Binary variable fixed to 1.\n", SCIPconsGetName(cons));
3379
3380 /* if slack variable is fixed to nonzero, we are infeasible */
3381 if ( SCIPisFeasPositive(scip, SCIPvarGetLbLocal(consdata->slackvar)) )
3382 {
3383 SCIPdebugMsg(scip, "The problem is infeasible: binary and slack variable are fixed to be nonzero.\n");
3384 *cutoff = TRUE;
3385 return SCIP_OKAY;
3386 }
3387
3388 /* otherwise fix slack variable to 0 */
3389 SCIPdebugMsg(scip, "Fix slack variable to 0 and delete constraint.\n");
3390 SCIP_CALL( SCIPfixVar(scip, consdata->slackvar, 0.0, &infeasible, &fixed) );
3391 assert( ! infeasible );
3392 if ( fixed )
3393 ++(*nfixedvars);
3394
3395 /* mark linear constraint to be update-able */
3396 if ( SCIPconsIsActive(consdata->lincons) )
3397 {
3398 SCIPconsAddUpgradeLocks(consdata->lincons, -1);
3399 assert( SCIPconsGetNUpgradeLocks(consdata->lincons) == 0 );
3400 }
3401
3402 /* delete indicator constraint (leave linear constraint) */
3403 assert( ! SCIPconsIsModifiable(cons) );
3404 SCIP_CALL( SCIPdelCons(scip, cons) );
3405 ++(*ndelconss);
3406 *success = TRUE;
3407 return SCIP_OKAY;
3408 }
3409
3410 /* if the binary variable is fixed to zero */
3411 if ( SCIPvarGetUbLocal(consdata->binvar) < 0.5 )
3412 {
3413 SCIPdebugMsg(scip, "Presolving <%s>: Binary variable <%s> fixed to 0, deleting indicator constraint.\n", SCIPconsGetName(cons), SCIPvarGetName(consdata->binvar));
3414
3415 /* mark linear constraint to be update-able */
3416 if ( SCIPconsIsActive(consdata->lincons) )
3417 {
3418 SCIPconsAddUpgradeLocks(consdata->lincons, -1);
3419 assert( SCIPconsGetNUpgradeLocks(consdata->lincons) == 0 );
3420 }
3421
3422 /* delete indicator constraint */
3423 assert( ! SCIPconsIsModifiable(cons) );
3424 SCIP_CALL( SCIPdelCons(scip, cons) );
3425 ++(*ndelconss);
3426 *success = TRUE;
3427 return SCIP_OKAY;
3428 }
3429
3430 /* if the slack variable is fixed to nonzero */
3431 if ( SCIPisFeasPositive(scip, SCIPvarGetLbLocal(consdata->slackvar)) )
3432 {
3433 SCIPdebugMsg(scip, "Presolving <%s>: Slack variable fixed to nonzero.\n", SCIPconsGetName(cons));
3434
3435 /* if binary variable is fixed to nonzero, we are infeasible */
3436 if ( SCIPvarGetLbLocal(consdata->binvar) > 0.5 )
3437 {
3438 SCIPdebugMsg(scip, "The problem is infeasible: binary and slack variable are fixed to be nonzero.\n");
3439 *cutoff = TRUE;
3440 return SCIP_OKAY;
3441 }
3442
3443 /* otherwise fix binary variable to 0 */
3444 SCIPdebugMsg(scip, "Fix binary variable to 0 and delete indicator constraint.\n");
3445 SCIP_CALL( SCIPfixVar(scip, consdata->binvar, 0.0, &infeasible, &fixed) );
3446 assert( ! infeasible );
3447 if ( fixed )
3448 ++(*nfixedvars);
3449
3450 /* mark linear constraint to be update-able */
3451 if ( SCIPconsIsActive(consdata->lincons) )
3452 {
3453 SCIPconsAddUpgradeLocks(consdata->lincons, -1);
3454 assert( SCIPconsGetNUpgradeLocks(consdata->lincons) == 0 );
3455 }
3456
3457 /* delete constraint */
3458 assert( ! SCIPconsIsModifiable(cons) );
3459 SCIP_CALL( SCIPdelCons(scip, cons) );
3460 ++(*ndelconss);
3461 *success = TRUE;
3462 return SCIP_OKAY;
3463 }
3464
3465 /* if the slack variable is fixed to zero */
3466 if ( SCIPisFeasZero(scip, SCIPvarGetUbLocal(consdata->slackvar)) )
3467 {
3468 /* perform dual reductions - if required */
3469 if ( dualreductions )
3470 {
3471 SCIP_VAR* binvar;
3472 SCIP_Real obj;
3473
3474 /* check objective of binary variable */
3475 binvar = consdata->binvar;
3476 obj = varGetObjDelta(binvar);
3477
3478 /* if obj = 0, we prefer fixing the binary variable to 1 (if possible) */
3479 if ( obj <= 0.0 )
3480 {
3481 /* In this case we would like to fix the binary variable to 1, if it is not locked up
3482 except by this indicator constraint. If more than one indicator constraint is
3483 effected, we have to hope that they are all fulfilled - in this case the last
3484 constraint will fix the binary variable to 1. */
3485 if ( SCIPvarGetNLocksUpType(binvar, SCIP_LOCKTYPE_MODEL) <= 1 )
3486 {
3487 if ( SCIPvarGetUbGlobal(binvar) > 0.5 )
3488 {
3489 SCIPdebugMsg(scip, "Presolving <%s> - dual reduction: Slack variable fixed to 0, fix binary variable to 1.\n", SCIPconsGetName(cons));
3490 SCIP_CALL( SCIPfixVar(scip, binvar, 1.0, &infeasible, &fixed) );
3491 assert( ! infeasible );
3492 if ( fixed )
3493 ++(*nfixedvars);
3494 /* make sure that the other case does not occur */
3495 obj = -1.0;
3496 }
3497 }
3498 }
3499 if ( obj >= 0.0 )
3500 {
3501 /* In this case we would like to fix the binary variable to 0, if it is not locked down
3502 (should also have been performed by other dual reductions). */
3503 if ( SCIPvarGetNLocksDownType(binvar, SCIP_LOCKTYPE_MODEL) == 0 )
3504 {
3505 if ( SCIPvarGetLbGlobal(binvar) < 0.5 )
3506 {
3507 SCIPdebugMsg(scip, "Presolving <%s> - dual reduction: Slack variable fixed to 0, fix binary variable to 0.\n", SCIPconsGetName(cons));
3508 SCIP_CALL( SCIPfixVar(scip, binvar, 0.0, &infeasible, &fixed) );
3509 assert( ! infeasible );
3510 if ( fixed )
3511 ++(*nfixedvars);
3512 }
3513 }
3514 }
3515 }
3516
3517 SCIPdebugMsg(scip, "Presolving <%s>: Slack variable fixed to zero, delete redundant indicator constraint.\n", SCIPconsGetName(cons));
3518
3519 /* mark linear constraint to be upgrade-able */
3520 if ( SCIPconsIsActive(consdata->lincons) )
3521 {
3522 SCIPconsAddUpgradeLocks(consdata->lincons, -1);
3523 assert( SCIPconsGetNUpgradeLocks(consdata->lincons) == 0 );
3524 }
3525
3526 /* delete constraint */
3527 assert( ! SCIPconsIsModifiable(cons) );
3528 SCIP_CALL( SCIPdelCons(scip, cons) );
3529 ++(*ndelconss);
3530 *success = TRUE;
3531 return SCIP_OKAY;
3532 }
3533
3534 /* check whether indicator variable is aggregated */
3535 if ( SCIPvarGetStatus(consdata->binvar) == SCIP_VARSTATUS_AGGREGATED )
3536 {
3537 SCIP_Bool negated = FALSE;
3538 SCIP_VAR* var;
3539
3540 /* possibly get representation of indicator variable by active variable */
3541 var = consdata->binvar;
3542 SCIP_CALL( SCIPvarGetProbvarBinary(&var, &negated) );
3543 assert( var == consdata->binvar || SCIPvarIsActive(var) || SCIPvarIsNegated(var) );
3544
3545 /* we can replace the binary variable by the active variable if it is not negated */
3546 if ( var != consdata->binvar && ! negated )
3547 {
3548 SCIPdebugMsg(scip, "Indicator variable <%s> is aggregated and replaced by active/negated variable <%s>.\n", SCIPvarGetName(consdata->binvar), SCIPvarGetName(var) );
3549
3550 /* we need to update the events and locks */
3551 assert( conshdlrdata->eventhdlrbound != NULL );
3552 SCIP_CALL( SCIPdropVarEvent(scip, consdata->binvar, SCIP_EVENTTYPE_BOUNDCHANGED, conshdlrdata->eventhdlrbound, (SCIP_EVENTDATA*) consdata, -1) );
3553 SCIP_CALL( SCIPcatchVarEvent(scip, var, SCIP_EVENTTYPE_BOUNDCHANGED, conshdlrdata->eventhdlrbound, (SCIP_EVENTDATA*) consdata, NULL) );
3554
3555 /* We also need to update the events and locks if restart is forced, since global bound change events on binary
3556 * variables are also caught in this case. If it would not be updated and forcerestart = TRUE, then an event
3557 * might be dropped on a wrong variable. */
3558 if ( conshdlrdata->forcerestart )
3559 {
3560 assert( conshdlrdata->eventhdlrrestart != NULL );
3561 SCIP_CALL( SCIPdropVarEvent(scip, consdata->binvar, SCIP_EVENTTYPE_GBDCHANGED,
3562 conshdlrdata->eventhdlrrestart, (SCIP_EVENTDATA*) conshdlrdata, -1) );
3563 SCIP_CALL( SCIPcatchVarEvent(scip, var, SCIP_EVENTTYPE_GBDCHANGED, conshdlrdata->eventhdlrrestart,
3564 (SCIP_EVENTDATA*) conshdlrdata, NULL) );
3565 }
3566
3567 SCIP_CALL( SCIPaddVarLocksType(scip, consdata->binvar, SCIP_LOCKTYPE_MODEL, 0, -1) );
3568 SCIP_CALL( SCIPaddVarLocksType(scip, var, SCIP_LOCKTYPE_MODEL, 0, 1) );
3569
3570 /* change binvary variable */
3571 consdata->binvar = var;
3572 }
3573 }
3574 else if ( SCIPvarGetStatus(consdata->binvar) == SCIP_VARSTATUS_NEGATED )
3575 {
3576 SCIP_VAR* var;
3577
3578 var = SCIPvarGetNegatedVar(consdata->binvar);
3579 assert( var != NULL );
3580
3581 /* if the binary variable is the negated slack variable, we have 1 - s = 1 -> s = 0, i.e., the constraint is redundant */
3582 if ( var == consdata->slackvar )
3583 {
3584 /* delete constraint */
3585 assert( ! SCIPconsIsModifiable(cons) );
3586 SCIP_CALL( SCIPdelCons(scip, cons) );
3587 ++(*ndelconss);
3588 *success = TRUE;
3589 return SCIP_OKAY;
3590 }
3591 }
3592
3593 /* check whether slack variable is aggregated */
3594 if ( SCIPvarGetStatus(consdata->slackvar) == SCIP_VARSTATUS_AGGREGATED || SCIPvarGetStatus(consdata->slackvar) == SCIP_VARSTATUS_NEGATED )
3595 {
3596 SCIP_BOUNDTYPE boundtype = SCIP_BOUNDTYPE_LOWER;
3597 SCIP_Real bound;
3598 SCIP_VAR* var;
3599
3600 /* possibly get representation of slack variable by active variable */
3601 var = consdata->slackvar;
3602 bound = SCIPvarGetLbGlobal(var);
3603
3604 SCIP_CALL( SCIPvarGetProbvarBound(&var, &bound, &boundtype) );
3605 assert( var != consdata->slackvar );
3606
3607 /* we can replace the slack variable by the active variable if it is also a >= variable */
3608 if ( var != consdata->binvar && boundtype == SCIP_BOUNDTYPE_LOWER && SCIPisEQ(scip, bound, 0.0) )
3609 {
3610 assert( SCIPvarIsActive(var) );
3611 SCIPdebugMsg(scip, "Slack variable <%s> is aggregated or negated and replaced by active variable <%s>.\n", SCIPvarGetName(consdata->slackvar), SCIPvarGetName(var) );
3612
3613 /* we need to update the events, locks, and captures */
3614 assert( conshdlrdata->eventhdlrbound != NULL );
3615 SCIP_CALL( SCIPdropVarEvent(scip, consdata->slackvar, SCIP_EVENTTYPE_BOUNDCHANGED, conshdlrdata->eventhdlrbound, (SCIP_EVENTDATA*) consdata, -1) );
3616 SCIP_CALL( SCIPcatchVarEvent(scip, var, SCIP_EVENTTYPE_BOUNDCHANGED, conshdlrdata->eventhdlrbound, (SCIP_EVENTDATA*) consdata, NULL) );
3617
3618 SCIP_CALL( SCIPunlockVarCons(scip, consdata->slackvar, cons, FALSE, TRUE) );
3619 SCIP_CALL( SCIPlockVarCons(scip, var, cons, FALSE, TRUE) );
3620
3621 SCIP_CALL( SCIPreleaseVar(scip, &consdata->slackvar) );
3622 SCIP_CALL( SCIPcaptureVar(scip, var) );
3623
3624 /* change slack variable */
3625 consdata->slackvar = var;
3626 }
3627 else if ( var == consdata->binvar )
3628 {
3629 /* check special case that aggregating variable is equal to the indicator variable */
3630 assert( SCIPisEQ(scip, bound, 0.0) || SCIPisEQ(scip, bound, 1.0) );
3631
3632 /* if the lower bound is transformed to an upper bound, we have "y = 1 -> 1 - y = 0", i.e., the constraint is redundant */
3633 if ( boundtype == SCIP_BOUNDTYPE_UPPER )
3634 {
3635 SCIPdebugMsg(scip, "Slack variable <%s> is aggregated to negated indicator variable <%s> -> constraint redundant.\n",
3636 SCIPvarGetName(consdata->slackvar), SCIPvarGetName(consdata->binvar));
3637 assert( SCIPisEQ(scip, bound, 1.0) );
3638
3639 /* delete constraint */
3640 assert( ! SCIPconsIsModifiable(cons) );
3641 SCIP_CALL( SCIPdelCons(scip, cons) );
3642 ++(*ndelconss);
3643 *success = TRUE;
3644 return SCIP_OKAY;
3645 }
3646 else
3647 {
3648 /* if the lower bound is transformed to a lower bound, we have "y = 1 -> y = 0", i.e., we can fix the binary variable to 0 */
3649 SCIPdebugMsg(scip, "Slack variable <%s> is aggregated to the indicator variable <%s> -> fix indicator variable to 0.\n",
3650 SCIPvarGetName(consdata->slackvar), SCIPvarGetName(consdata->binvar));
3651 assert( boundtype == SCIP_BOUNDTYPE_LOWER );
3652 assert( SCIPisEQ(scip, bound, 0.0) );
3653
3654 SCIP_CALL( SCIPfixVar(scip, consdata->binvar, 0.0, &infeasible, &fixed) );
3655 assert( ! infeasible );
3656
3657 if ( fixed )
3658 ++(*nfixedvars);
3659
3660 SCIP_CALL( SCIPdelCons(scip, cons) );
3661
3662 ++(*ndelconss);
3663 *success = TRUE;
3664
3665 return SCIP_OKAY;
3666 }
3667 }
3668 }
3669
3670 /* Note that because of possible multi-aggregation we cannot simply remove the indicator
3671 * constraint if the linear constraint is not active or disabled - see the note in @ref
3672 * PREPROC. */
3673
3674 return SCIP_OKAY;
3675 }
3676
3677
3678 /** propagate indicator constraint */
3679 static
3680 SCIP_RETCODE propIndicator(
3681 SCIP* scip, /**< SCIP pointer */
3682 SCIP_CONS* cons, /**< constraint */
3683 SCIP_CONSDATA* consdata, /**< constraint data */
3684 SCIP_Bool dualreductions, /**< should dual reductions be performed? */
3685 SCIP_Bool addopposite, /**< add opposite inequalities if binary var = 0? */
3686 SCIP_Bool* cutoff, /**< whether a cutoff happened */
3687 int* nGen /**< number of domain changes */
3688 )
3689 {
3690 SCIP_Bool infeasible;
3691 SCIP_Bool tightened;
3692
3693 assert( scip != NULL );
3694 assert( cons != NULL );
3695 assert( consdata != NULL );
3696 assert( cutoff != NULL );
3697 assert( nGen != NULL );
3698
3699 *cutoff = FALSE;
3700 *nGen = 0;
3701
3702 /* if the linear constraint has not been generated, we do nothing */
3703 if ( ! consdata->linconsactive )
3704 return SCIP_OKAY;
3705
3706 assert( consdata->slackvar != NULL );
3707 assert( consdata->binvar != NULL );
3708 assert( SCIPisFeasGE(scip, SCIPvarGetLbLocal(consdata->slackvar), 0.0) );
3709
3710 /* if both slackvar and binvar are fixed to be nonzero */
3711 if ( consdata->nfixednonzero > 1 )
3712 {
3713 SCIPdebugMsg(scip, "The node is infeasible, both the slack variable and the binary variable are fixed to be nonzero.\n");
3714 *cutoff = TRUE;
3715
3716 SCIP_CALL( SCIPresetConsAge(scip, cons) );
3717 assert( SCIPvarGetLbLocal(consdata->binvar) > 0.5 );
3718 assert( SCIPisPositive(scip, SCIPvarGetLbLocal(consdata->slackvar)) );
3719
3720 /* check if conflict analysis is turned on */
3721 if ( ! SCIPisConflictAnalysisApplicable(scip) )
3722 return SCIP_OKAY;
3723
3724 /* conflict analysis can only be applied in solving stage */
3725 assert( SCIPgetStage(scip) == SCIP_STAGE_SOLVING || SCIPinProbing(scip) );
3726
3727 /* perform conflict analysis */
3728 SCIP_CALL( SCIPinitConflictAnalysis(scip, SCIP_CONFTYPE_PROPAGATION, FALSE) );
3729
3730 SCIP_CALL( SCIPaddConflictBinvar(scip, consdata->binvar) );
3731 SCIP_CALL( SCIPaddConflictLb(scip, consdata->slackvar, NULL) );
3732 SCIP_CALL( SCIPanalyzeConflictCons(scip, cons, NULL) );
3733
3734 return SCIP_OKAY;
3735 }
3736
3737 /* if exactly one of the variables is fixed to be nonzero */
3738 if ( consdata->nfixednonzero == 1 )
3739 {
3740 /* increase age of constraint; age is reset to zero, if a conflict or a propagation was found */
3741 if ( ! SCIPinRepropagation(scip) )
3742 SCIP_CALL( SCIPincConsAge(scip, cons) );
3743
3744 /* if binvar is fixed to be nonzero */
3745 if ( SCIPvarGetLbLocal(consdata->binvar) > 0.5 )
3746 {
3747 assert( SCIPvarGetStatus(consdata->slackvar) != SCIP_VARSTATUS_MULTAGGR );
3748
3749 /* if slack variable is not already fixed to 0 */
3750 if ( ! SCIPisZero(scip, SCIPvarGetUbLocal(consdata->slackvar)) )
3751 {
3752 SCIPdebugMsg(scip, "Binary variable <%s> is fixed to be nonzero, fixing slack variable <%s> to 0.\n",
3753 SCIPvarGetName(consdata->binvar), SCIPvarGetName(consdata->slackvar));
3754
3755 /* fix slack variable to 0 */
3756 SCIP_CALL( SCIPinferVarUbCons(scip, consdata->slackvar, 0.0, cons, 0, FALSE, &infeasible, &tightened) );
3757 assert( ! infeasible );
3758 if ( tightened )
3759 ++(*nGen);
3760 }
3761 }
3762
3763 /* if slackvar is fixed to be nonzero */
3764 if ( SCIPisFeasPositive(scip, SCIPvarGetLbLocal(consdata->slackvar)) )
3765 {
3766 /* if binary variable is not yet fixed to 0 */
3767 if ( SCIPvarGetUbLocal(consdata->binvar) > 0.5 )
3768 {
3769 SCIPdebugMsg(scip, "Slack variable <%s> is fixed to be nonzero, fixing binary variable <%s> to 0.\n",
3770 SCIPvarGetName(consdata->slackvar), SCIPvarGetName(consdata->binvar));
3771
3772 /* fix binary variable to 0 */
3773 SCIP_CALL( SCIPinferVarUbCons(scip, consdata->binvar, 0.0, cons, 1, FALSE, &infeasible, &tightened) );
3774 assert( ! infeasible );
3775 if ( tightened )
3776 ++(*nGen);
3777 }
3778 }
3779
3780 /* reset constraint age counter */
3781 if ( *nGen > 0 )
3782 SCIP_CALL( SCIPresetConsAge(scip, cons) );
3783
3784 /* remove constraint if we are not in probing */
3785 if ( ! SCIPinProbing(scip) )
3786 {
3787 /* mark linear constraint to be update-able */
3788 if ( SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING && SCIPconsIsActive(consdata->lincons) )
3789 {
3790 SCIPconsAddUpgradeLocks(consdata->lincons, -1);
3791 assert( SCIPconsGetNUpgradeLocks(consdata->lincons) == 0 );
3792 }
3793
3794 /* delete constraint locally */
3795 assert( ! SCIPconsIsModifiable(cons) );
3796 SCIP_CALL( SCIPdelConsLocal(scip, cons) );
3797 }
3798 }
3799 else
3800 {
3801 /* if the binary variable is fixed to zero */
3802 if ( SCIPvarGetUbLocal(consdata->binvar) < 0.5 )
3803 {
3804 if ( addopposite && consdata->linconsactive )
3805 {
3806 char name[SCIP_MAXSTRLEN];
3807 SCIP_CONS* reversecons;
3808 SCIP_VAR** linvars;
3809 SCIP_Real* linvals;
3810 SCIP_Bool allintegral = TRUE;
3811 SCIP_VAR* slackvar;
3812 SCIP_VAR** vars;
3813 SCIP_Real* vals;
3814 SCIP_Real lhs;
3815 SCIP_Real rhs;
3816 int nlinvars;
3817 int nvars = 0;
3818 int j;
3819
3820 /* determine lhs/rhs (first exchange lhs/rhs) */
3821 lhs = SCIPgetRhsLinear(scip, consdata->lincons);
3822 if ( SCIPisInfinity(scip, lhs) )
3823 lhs = -SCIPinfinity(scip);
3824 rhs = SCIPgetLhsLinear(scip, consdata->lincons);
3825 if ( SCIPisInfinity(scip, -rhs) )
3826 rhs = SCIPinfinity(scip);
3827
3828 assert( ! SCIPisInfinity(scip, lhs) );
3829 assert( ! SCIPisInfinity(scip, -rhs) );
3830
3831 /* consider only finite lhs/rhs */
3832 if ( ! SCIPisInfinity(scip, -lhs) || ! SCIPisInfinity(scip, rhs) )
3833 {
3834 /* ignore equations (cannot add opposite constraint) */
3835 if ( ! SCIPisEQ(scip, lhs, rhs) )
3836 {
3837 assert( consdata->lincons != NULL );
3838 nlinvars = SCIPgetNVarsLinear(scip, consdata->lincons);
3839 linvars = SCIPgetVarsLinear(scip, consdata->lincons);
3840 linvals = SCIPgetValsLinear(scip, consdata->lincons);
3841 slackvar = consdata->slackvar;
3842 assert( slackvar != NULL );
3843
3844 SCIP_CALL( SCIPallocBufferArray(scip, &vars, nlinvars) );
3845 SCIP_CALL( SCIPallocBufferArray(scip, &vals, nlinvars) );
3846
3847 /* copy data and check whether the linear constraint is integral */
3848 for (j = 0; j < nlinvars; ++j)
3849 {
3850 if ( linvars[j] != slackvar )
3851 {
3852 if (! SCIPvarIsIntegral(linvars[j]) || ! SCIPisIntegral(scip, linvals[j]) )
3853 allintegral = FALSE;
3854
3855 vars[nvars] = linvars[j];
3856 vals[nvars++] = linvals[j];
3857 }
3858 }
3859 assert( nlinvars == nvars + 1 );
3860
3861 /* possibly adjust lhs/rhs */
3862 if ( allintegral && ! SCIPisInfinity(scip, REALABS(lhs)) )
3863 lhs += 1.0;
3864
3865 if ( allintegral && ! SCIPisInfinity(scip, REALABS(rhs)) )
3866 rhs -= 1.0;
3867
3868 /* create reverse constraint */
3869 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "reverse_%s", SCIPconsGetName(consdata->lincons));
3870
3871 /* constraint is initial, separated, not enforced, not checked, propagated, local, not modifiable, dynamic, removable */
3872 SCIP_CALL( SCIPcreateConsLinear(scip, &reversecons, name, nvars, vars, vals, lhs, rhs,
3873 TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE) );
3874
3875 SCIPdebugMsg(scip, "Binary variable <%s> fixed to 0. Adding opposite linear inequality.\n", SCIPvarGetName(consdata->binvar));
3876 SCIPdebugPrintCons(scip, reversecons, NULL);
3877
3878 /* add constraint */
3879 SCIP_CALL( SCIPaddCons(scip, reversecons) );
3880 SCIP_CALL( SCIPreleaseCons(scip, &reversecons) );
3881
3882 SCIPfreeBufferArray(scip, &vals);
3883 SCIPfreeBufferArray(scip, &vars);
3884 }
3885 }
3886 }
3887
3888 SCIP_CALL( SCIPdelConsLocal(scip, cons) );
3889 }
3890
3891 /* if the slack variable is fixed to zero */
3892 if ( SCIPisFeasZero(scip, SCIPvarGetUbLocal(consdata->slackvar)) )
3893 {
3894 /* perform dual reduction - if required */
3895 if ( dualreductions )
3896 {
3897 SCIP_VAR* binvar;
3898 SCIP_Real obj;
3899
3900 /* check objective of binary variable */
3901 binvar = consdata->binvar;
3902 obj = varGetObjDelta(binvar);
3903
3904 /* if obj = 0, we prefer setting the binary variable to 1 (if possible) */
3905 if ( obj <= 0.0 )
3906 {
3907 /* In this case we would like to fix the binary variable to 1, if it is not locked up
3908 except by this indicator constraint. If more than one indicator constraint is
3909 affected, we have to hope that they are all fulfilled - in this case the last
3910 constraint will fix the binary variable to 1. */
3911 if ( SCIPvarGetNLocksUpType(binvar, SCIP_LOCKTYPE_MODEL) <= 1 )
3912 {
3913 if ( SCIPvarGetUbLocal(binvar) > 0.5 )
3914 {
3915 SCIPdebugMsg(scip, "Propagating <%s> - dual reduction: Slack variable fixed to 0, fix binary variable to 1.\n", SCIPconsGetName(cons));
3916 SCIP_CALL( SCIPinferVarLbCons(scip, binvar, 1.0, cons, 2, FALSE, &infeasible, &tightened) );
3917 assert( ! infeasible );
3918 if ( tightened )
3919 ++(*nGen);
3920 /* Make sure that the other case does not occur, since we are not sure whether SCIPinferVarLbCons() directly changes the bounds. */
3921 obj = -1.0;
3922 }
3923 }
3924 }
3925 if ( obj >= 0.0 )
3926 {
3927 /* In this case we would like to fix the binary variable to 0, if it is not locked down
3928 (should also have been performed by other dual reductions). */
3929 if ( SCIPvarGetNLocksDownType(binvar, SCIP_LOCKTYPE_MODEL) == 0 )
3930 {
3931 if ( SCIPvarGetLbLocal(binvar) < 0.5 )
3932 {
3933 SCIPdebugMsg(scip, "Propagating <%s> - dual reduction: Slack variable fixed to 0, fix binary variable to 0.\n", SCIPconsGetName(cons));
3934 SCIP_CALL( SCIPinferVarUbCons(scip, binvar, 0.0, cons, 2, FALSE, &infeasible, &tightened) );
3935 assert( ! infeasible );
3936 if ( tightened )
3937 ++(*nGen);
3938 }
3939 }
3940 }
3941 }
3942
3943 SCIPdebugMsg(scip, "Slack variable fixed to zero, delete redundant indicator constraint <%s>.\n", SCIPconsGetName(cons));
3944
3945 /* delete constraint */
3946 assert( ! SCIPconsIsModifiable(cons) );
3947
3948 /* remove constraint if we are not in probing */
3949 if ( ! SCIPinProbing(scip) )
3950 {
3951 /* mark linear constraint to be update-able */
3952 if ( SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING && SCIPconsIsActive(consdata->lincons) )
3953 {
3954 SCIPconsAddUpgradeLocks(consdata->lincons, -1);
3955 assert( SCIPconsGetNUpgradeLocks(consdata->lincons) == 0 );
3956 }
3957
3958 SCIP_CALL( SCIPdelConsLocal(scip, cons) );
3959 }
3960 SCIP_CALL( SCIPresetConsAge(scip, cons) );
3961 ++(*nGen);
3962 }
3963
3964 /* Note that because of possible multi-aggregation we cannot simply remove the indicator
3965 * constraint if the linear constraint is not active or disabled - see the note in @ref
3966 * PREPROC and consPresolIndicator(). Moreover, it would drastically increase memory
3967 * consumption, because the linear constraints have to be stored in each node. */
3968 }
3969
3970 return SCIP_OKAY;
3971 }
3972
3973
3974 /** enforcement method that produces cuts if possible
3975 *
3976 * This is a variant of the enforcement method that generates cuts/constraints via the alternative
3977 * LP, if possible.
3978 */
3979 static
3980 SCIP_RETCODE enforceCuts(
3981 SCIP* scip, /**< SCIP pointer */
3982 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
3983 int nconss, /**< number of constraints */
3984 SCIP_CONS** conss, /**< indicator constraints */
3985 SCIP_SOL* sol, /**< solution to be enforced */
3986 SCIP_ENFOSEPATYPE enfosepatype, /**< type of enforcing/separating type */
3987 SCIP_Bool genlogicor, /**< whether logicor constraint should be generated */
3988 SCIP_Bool* cutoff, /**< whether we detected a cutoff by an infeasible inequality */
3989 int* nGen /**< number of cuts generated */
3990 )
3991 {
3992 SCIP_CONSHDLRDATA* conshdlrdata;
3993 SCIP_LPI* lp;
3994 SCIP_Bool* S;
3995 SCIP_Real value = 0.0;
3996 SCIP_Bool error;
3997 int size = 0;
3998 int nCuts;
3999 int j;
4000
4001 assert( scip != NULL );
4002 assert( conshdlr != NULL );
4003 assert( conss != NULL );
4004 assert( cutoff != NULL );
4005 assert( nGen != NULL );
4006
4007 SCIPdebugMsg(scip, "Enforcing via cuts ...\n");
4008 *cutoff = FALSE;
4009 *nGen = 0;
4010
4011 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4012 assert( conshdlrdata != NULL );
4013 lp = conshdlrdata->altlp;
4014 assert( lp != NULL );
4015
4016 #ifndef NDEBUG
4017 SCIP_CALL( checkLPBoundsClean(scip, lp, nconss, conss) );
4018 #endif
4019
4020 /* change coefficients of bounds in alternative LP */
4021 if ( conshdlrdata->updatebounds )
4022 SCIP_CALL( updateFirstRowGlobal(scip, conshdlrdata) );
4023
4024 /* possibly update upper bound */
4025 SCIP_CALL( updateObjUpperbound(scip, conshdlr, conshdlrdata) );
4026
4027 /* scale first row if necessary */
4028 SCIP_CALL( scaleFirstRow(scip, conshdlrdata) );
4029
4030 /* set objective function to current solution */
4031 SCIP_CALL( setAltLPObjZero(scip, lp, nconss, conss) );
4032
4033 SCIP_CALL( SCIPallocBufferArray(scip, &S, nconss) );
4034
4035 /* set up variables fixed to 1 */
4036 for (j = 0; j < nconss; ++j)
4037 {
4038 SCIP_CONSDATA* consdata;
4039
4040 assert( conss[j] != NULL );
4041 consdata = SCIPconsGetData(conss[j]);
4042 assert( consdata != NULL );
4043
4044 assert( SCIPisFeasIntegral(scip, SCIPgetSolVal(scip, sol, consdata->binvar)) );
4045 if ( SCIPisFeasZero(scip, SCIPgetSolVal(scip, sol, consdata->binvar)) )
4046 {
4047 ++size;
4048 value += varGetObjDelta(consdata->binvar);
4049 S[j] = TRUE;
4050 }
4051 else
4052 S[j] = FALSE;
4053 }
4054
4055 /* fix the variables in S */
4056 SCIP_CALL( fixAltLPVariables(scip, lp, nconss, conss, S) );
4057
4058 /* extend set S to a cover and generate cuts */
4059 error = FALSE;
4060 SCIP_CALL( extendToCover(scip, conshdlr, conshdlrdata, lp, sol, enfosepatype, conshdlrdata->removable, genlogicor, nconss, conss, S, &size, &value, &error, cutoff, &nCuts) );
4061 *nGen = nCuts;
4062
4063 /* return with an error if no cuts have been produced and and error occurred in extendToCover() */
4064 if ( nCuts == 0 && error )
4065 return SCIP_LPERROR;
4066
4067 SCIPdebugMsg(scip, "Generated %d IIS-cuts.\n", nCuts);
4068
4069 /* reset bounds */
4070 SCIP_CALL( unfixAltLPVariables(scip, lp, nconss, conss, S) );
4071
4072 #ifndef NDEBUG
4073 SCIP_CALL( checkLPBoundsClean(scip, lp, nconss, conss) );
4074 #endif
4075
4076 SCIPfreeBufferArray(scip, &S);
4077
4078 return SCIP_OKAY;
4079 }
4080
4081
4082 /** enforcement method
4083 *
4084 * We check whether the current solution is feasible, i.e., if binvar = 1
4085 * implies that slackvar = 0. If not, we branch as follows:
4086 *
4087 * In one branch we fix binvar = 1 and slackvar = 0. In the other branch
4088 * we fix binvar = 0 and leave slackvar unchanged.
4089 */
4090 static
4091 SCIP_RETCODE enforceIndicators(
4092 SCIP* scip, /**< SCIP pointer */
4093 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
4094 int nconss, /**< number of constraints */
4095 SCIP_CONS** conss, /**< indicator constraints */
4096 SCIP_SOL* sol, /**< solution to be enforced (NULL for LP solution) */
4097 SCIP_ENFOSEPATYPE enfosepatype, /**< type of enforcing/separating type */
4098 SCIP_Bool genlogicor, /**< whether logicor constraint should be generated */
4099 SCIP_RESULT* result /**< result */
4100 )
4101 {
4102 SCIP_CONSDATA* consdata;
4103 SCIP_CONSHDLRDATA* conshdlrdata;
4104 SCIP_NODE* node1;
4105 SCIP_NODE* node2;
4106 SCIP_VAR* slackvar;
4107 SCIP_VAR* binvar;
4108 SCIP_CONS* branchCons = NULL;
4109 SCIP_Real maxSlack = -1.0;
4110 SCIP_Bool someLinconsNotActive = FALSE;
4111 int c;
4112
4113 assert( scip != NULL );
4114 assert( conshdlr != NULL );
4115 assert( conss != NULL );
4116 assert( result != NULL );
4117
4118 *result = SCIP_FEASIBLE;
4119
4120 SCIPdebugMsg(scip, "Enforcing indicator constraints for <%s> ...\n", SCIPconshdlrGetName(conshdlr) );
4121
4122 /* get constraint handler data */
4123 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4124 assert( conshdlrdata != NULL );
4125
4126 #ifdef SCIP_OUTPUT
4127 SCIP_CALL( SCIPwriteTransProblem(scip, "ind.cip", "cip", FALSE) );
4128 #endif
4129
4130 /* check each constraint */
4131 for (c = 0; c < nconss; ++c)
4132 {
4133 SCIP_Bool cutoff;
4134 SCIP_Real valSlack;
4135 int cnt;
4136
4137 assert( conss[c] != NULL );
4138 consdata = SCIPconsGetData(conss[c]);
4139 assert( consdata != NULL );
4140 assert( consdata->lincons != NULL );
4141
4142 /* if the linear constraint has not been generated, we do nothing */
4143 if ( ! consdata->linconsactive )
4144 {
4145 someLinconsNotActive = TRUE;
4146 continue;
4147 }
4148
4149 /* first perform propagation (it might happen that standard propagation is turned off) */
4150 SCIP_CALL( propIndicator(scip, conss[c], consdata,
4151 conshdlrdata->dualreductions && SCIPallowStrongDualReds(scip), conshdlrdata->addopposite,
4152 &cutoff, &cnt) );
4153 if ( cutoff )
4154 {
4155 SCIPdebugMsg(scip, "Propagation in enforcing <%s> detected cutoff.\n", SCIPconsGetName(conss[c]));
4156 *result = SCIP_CUTOFF;
4157 return SCIP_OKAY;
4158 }
4159 if ( cnt > 0 )
4160 {
4161 SCIPdebugMsg(scip, "Propagation in enforcing <%s> reduced domains: %d.\n", SCIPconsGetName(conss[c]), cnt);
4162 *result = SCIP_REDUCEDDOM;
4163 return SCIP_OKAY;
4164 }
4165
4166 /* check whether constraint is infeasible */
4167 binvar = consdata->binvar;
4168 valSlack = SCIPgetSolVal(scip, sol, consdata->slackvar);
4169 assert( ! SCIPisFeasNegative(scip, valSlack) );
4170 if ( ! SCIPisFeasZero(scip, SCIPgetSolVal(scip, sol, binvar)) && ! SCIPisFeasZero(scip, valSlack) )
4171 {
4172 /* binary variable is not fixed - otherwise we would not be infeasible */
4173 assert( SCIPvarGetLbLocal(binvar) < 0.5 && SCIPvarGetUbLocal(binvar) > 0.5 );
4174
4175 if ( valSlack > maxSlack )
4176 {
4177 maxSlack = valSlack;
4178 branchCons = conss[c];
4179 #ifdef SCIP_OUTPUT
4180 SCIPinfoMessage(scip, NULL, "Violated indicator constraint:\n");
4181 SCIP_CALL( SCIPprintCons(scip, conss[c], NULL) );
4182 SCIPinfoMessage(scip, NULL, ";\n");
4183 SCIPinfoMessage(scip, NULL, "Corresponding linear constraint:\n");
4184 SCIP_CALL( SCIPprintCons(scip, consdata->lincons, NULL) );
4185 SCIPinfoMessage(scip, NULL, ";\n");
4186 #endif
4187 }
4188 }
4189 }
4190
4191 /* if some constraint has a linear constraint that is not active, we need to check feasibility via the alternative polyhedron */
4192 if ( (someLinconsNotActive || conshdlrdata->enforcecuts) && conshdlrdata->sepaalternativelp )
4193 {
4194 SCIP_Bool cutoff;
4195 int ngen;
4196
4197 SCIP_CALL( enforceCuts(scip, conshdlr, nconss, conss, sol, enfosepatype, genlogicor, &cutoff, &ngen) );
4198 if ( cutoff )
4199 {
4200 conshdlrdata->niiscutsgen += ngen;
4201 *result = SCIP_CUTOFF;
4202 return SCIP_OKAY;
4203 }
4204
4205 if ( ngen > 0 )
4206 {
4207 conshdlrdata->niiscutsgen += ngen;
4208 if ( genlogicor )
4209 {
4210 SCIPdebugMsg(scip, "Generated %d constraints.\n", ngen);
4211 *result = SCIP_CONSADDED;
4212 }
4213 else
4214 {
4215 SCIPdebugMsg(scip, "Generated %d cuts.\n", ngen);
4216 *result = SCIP_SEPARATED;
4217 }
4218 return SCIP_OKAY;
4219 }
4220 SCIPdebugMsg(scip, "Enforcing produced no cuts.\n");
4221
4222 assert( ! someLinconsNotActive || branchCons == NULL );
4223 }
4224
4225 /* if all constraints are feasible */
4226 if ( branchCons == NULL )
4227 {
4228 SCIPdebugMsg(scip, "All indicator constraints are feasible.\n");
4229 return SCIP_OKAY;
4230 }
4231
4232 /* skip branching if required */
4233 if ( ! conshdlrdata->branchindicators )
4234 {
4235 *result = SCIP_INFEASIBLE;
4236 return SCIP_OKAY;
4237 }
4238
4239 /* otherwise create branches */
4240 SCIPdebugMsg(scip, "Branching on constraint <%s> (slack value: %f).\n", SCIPconsGetName(branchCons), maxSlack);
4241 consdata = SCIPconsGetData(branchCons);
4242 assert( consdata != NULL );
4243 binvar = consdata->binvar;
4244 slackvar = consdata->slackvar;
4245
4246 /* node1: binvar = 1, slackvar = 0 */
4247 SCIP_CALL( SCIPcreateChild(scip, &node1, 0.0, SCIPcalcChildEstimate(scip, binvar, 1.0) ) );
4248
4249 if ( SCIPvarGetLbLocal(binvar) < 0.5 )
4250 {
4251 SCIP_CALL( SCIPchgVarLbNode(scip, node1, binvar, 1.0) );
4252 }
4253
4254 /* if slack-variable is multi-aggregated */
4255 assert( SCIPvarGetStatus(slackvar) != SCIP_VARSTATUS_MULTAGGR );
4256 if ( ! SCIPisFeasZero(scip, SCIPvarGetUbLocal(slackvar)) )
4257 {
4258 SCIP_CALL( SCIPchgVarUbNode(scip, node1, slackvar, 0.0) );
4259 }
4260
4261 /* node2: binvar = 0, no restriction on slackvar */
4262 SCIP_CALL( SCIPcreateChild(scip, &node2, 0.0, SCIPcalcChildEstimate(scip, binvar, 0.0) ) );
4263
4264 if ( SCIPvarGetUbLocal(binvar) > 0.5 )
4265 {
4266 SCIP_CALL( SCIPchgVarUbNode(scip, node2, binvar, 0.0) );
4267 }
4268
4269 SCIP_CALL( SCIPresetConsAge(scip, branchCons) );
4270 *result = SCIP_BRANCHED;
4271
4272 return SCIP_OKAY;
4273 }
4274
4275
4276 /** separate IIS-cuts via rounding
4277 *
4278 * @todo Check whether the cover produced at the end is a feasible solution.
4279 */
4280 static
4281 SCIP_RETCODE separateIISRounding(
4282 SCIP* scip, /**< SCIP pointer */
4283 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
4284 SCIP_SOL* sol, /**< solution to be separated */
4285 SCIP_ENFOSEPATYPE enfosepatype, /**< type of enforcing/separating type */
4286 int nconss, /**< number of constraints */
4287 SCIP_CONS** conss, /**< indicator constraints */
4288 int maxsepacuts, /**< maximal number of cuts to be generated */
4289 SCIP_Bool* cutoff, /**< whether we detected a cutoff by an infeasible inequality */
4290 int* nGen /**< number of domain changes */
4291 )
4292 { /*lint --e{850}*/
4293 SCIP_CONSHDLRDATA* conshdlrdata;
4294 SCIP_LPI* lp;
4295 int rounds;
4296 SCIP_Real threshold;
4297 SCIP_Bool* S;
4298 SCIP_Bool error;
4299 int oldsize = -1;
4300 SCIPdebug( int nGenOld = *nGen; )
4301
4302 assert( scip != NULL );
4303 assert( conshdlr != NULL );
4304 assert( conss != NULL );
4305 assert( cutoff != NULL );
4306 assert( nGen != NULL );
4307
4308 if ( *nGen >= maxsepacuts )
4309 return SCIP_OKAY;
4310
4311 *cutoff = FALSE;
4312 rounds = 0;
4313
4314 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4315 assert( conshdlrdata != NULL );
4316 lp = conshdlrdata->altlp;
4317 assert( lp != NULL );
4318
4319 SCIPdebugMsg(scip, "Separating IIS-cuts by rounding ...\n");
4320
4321 #ifndef NDEBUG
4322 SCIP_CALL( checkLPBoundsClean(scip, lp, nconss, conss) );
4323 #endif
4324
4325 /* change coefficients of bounds in alternative LP */
4326 if ( conshdlrdata->updatebounds )
4327 {
4328 /* update to local bounds */
4329 SCIP_CALL( updateFirstRow(scip, conshdlrdata) );
4330 }
4331
4332 /* possibly update upper bound */
4333 SCIP_CALL( updateObjUpperbound(scip, conshdlr, conshdlrdata) );
4334
4335 /* scale first row if necessary */
4336 SCIP_CALL( scaleFirstRow(scip, conshdlrdata) );
4337
4338 /* set objective function to current solution */
4339 SCIP_CALL( setAltLPObj(scip, lp, sol, nconss, conss) );
4340
4341 SCIP_CALL( SCIPallocBufferArray(scip, &S, nconss) );
4342
4343 /* loop through the possible thresholds */
4344 for (threshold = conshdlrdata->roundingmaxthres;
4345 rounds < conshdlrdata->maxroundingrounds && threshold >= conshdlrdata->roundingminthres && *nGen < maxsepacuts && ! (*cutoff);
4346 threshold -= conshdlrdata->roundingoffset )
4347 {
4348 SCIP_Real value = 0.0;
4349 int size = 0;
4350 int nCuts = 0;
4351 int j;
4352 #ifdef SCIP_DEBUG
4353 int nvarsone = 0;
4354 int nvarszero = 0;
4355 int nvarsfrac = 0;
4356 #endif
4357
4358 SCIPdebugMsg(scip, "Threshold: %g.\n", threshold);
4359
4360 /* choose variables that have a value < current threshold value */
4361 for (j = 0; j < nconss; ++j)
4362 {
4363 SCIP_CONSDATA* consdata;
4364 SCIP_Real binvarval;
4365 SCIP_VAR* binvarneg;
4366
4367 assert( conss[j] != NULL );
4368 consdata = SCIPconsGetData(conss[j]);
4369 assert( consdata != NULL );
4370
4371 binvarval = SCIPgetVarSol(scip, consdata->binvar);
4372
4373 #ifdef SCIP_DEBUG
4374 if ( SCIPisFeasEQ(scip, binvarval, 1.0) )
4375 ++nvarsone;
4376 else if ( SCIPisFeasZero(scip, binvarval) )
4377 ++nvarszero;
4378 else
4379 ++nvarsfrac;
4380 #endif
4381
4382 /* check whether complementary (negated) variable is present as well */
4383 binvarneg = SCIPvarGetNegatedVar(consdata->binvar);
4384 assert( binvarneg != NULL );
4385
4386 /* negated variable is present as well */
4387 assert( conshdlrdata->binvarhash != NULL );
4388 if ( SCIPhashmapExists(conshdlrdata->binvarhash, (void*) binvarneg) )
4389 {
4390 SCIP_Real binvarnegval = SCIPgetVarSol(scip, binvarneg);
4391
4392 /* take larger one */
4393 if ( binvarval > binvarnegval )
4394 S[j] = TRUE;
4395 else
4396 S[j] = FALSE;
4397 continue;
4398 }
4399
4400 /* check for threshold */
4401 if ( SCIPisFeasLT(scip, SCIPgetVarSol(scip, consdata->binvar), threshold) )
4402 {
4403 S[j] = TRUE;
4404 value += varGetObjDelta(consdata->binvar);
4405 ++size;
4406 }
4407 else
4408 S[j] = FALSE;
4409 }
4410
4411 if ( size == nconss )
4412 {
4413 SCIPdebugMsg(scip, "All variables in the set. Continue ...\n");
4414 continue;
4415 }
4416
4417 /* skip computation if size has not changed (computation is likely the same) */
4418 if ( size == oldsize )
4419 {
4420 SCIPdebugMsg(scip, "Skipping computation: size support has not changed.\n");
4421 continue;
4422 }
4423 oldsize = size;
4424
4425 #ifdef SCIP_DEBUG
4426 SCIPdebugMsg(scip, " Vars with value 1: %d 0: %d and fractional: %d.\n", nvarsone, nvarszero, nvarsfrac);
4427 #endif
4428
4429 /* fix the variables in S */
4430 SCIP_CALL( fixAltLPVariables(scip, lp, nconss, conss, S) );
4431
4432 /* extend set S to a cover and generate cuts */
4433 SCIP_CALL( extendToCover(scip, conshdlr, conshdlrdata, lp, sol, enfosepatype, conshdlrdata->removable, conshdlrdata->genlogicor,
4434 nconss, conss, S, &size, &value, &error, cutoff, &nCuts) );
4435
4436 /* we ignore errors in extendToCover */
4437 if ( nCuts > 0 )
4438 {
4439 *nGen += nCuts;
4440 ++rounds;
4441 }
4442 else
4443 {
4444 /* possibly update upper bound */
4445 SCIP_CALL( updateObjUpperbound(scip, conshdlr, conshdlrdata) );
4446 }
4447
4448 /* reset bounds */
4449 SCIP_CALL( unfixAltLPVariables(scip, lp, nconss, conss, S) );
4450 }
4451 SCIPdebug( SCIPdebugMsg(scip, "Generated %d IISs.\n", *nGen - nGenOld); )
4452
4453 #ifndef NDEBUG
4454 SCIP_CALL( checkLPBoundsClean(scip, lp, nconss, conss) );
4455 #endif
4456
4457 SCIPfreeBufferArray(scip, &S);
4458
4459 return SCIP_OKAY;
4460 }
4461
4462
4463
4464 /** separate cuts based on perspective formulation
4465 *
4466 * Hijazi, Bonami, and Ouorou (2014) introduced the following cuts: We consider an indicator constraint
4467 * \f[
4468 * y = 1 \rightarrow \alpha^T x \leq \beta
4469 * \f]
4470 * and assume finite bounds \f$\ell \leq x \leq u\f$. Then for \f$I \subseteq \{1, \dots, n\}\f$ define
4471 * \f[
4472 * \Sigma(I,x,y) = \sum_{i \notin I} \alpha_i x_i +
4473 * y \Big(\sum_{i \in I, \alpha_i < 0} \alpha_i u_i + \sum_{i \in I, \alpha_i > 0} \alpha_i \ell_i +
4474 * \sum_{i \notin I, \alpha_i < 0} \alpha_i \ell_i + \sum_{i \notin I, \alpha_i > 0} \alpha_i u_i - \beta\Big).
4475 * \f]
4476 * Then the cuts
4477 * \f[
4478 * \Sigma(I,x,y) \leq \sum_{i \notin I, \alpha_i < 0} \alpha_i \ell_i + \sum_{i \notin I, \alpha_i > 0} \alpha_i u_i
4479 * \f]
4480 * are valid for the disjunction
4481 * \f[
4482 * \{y = 0,\; \ell \leq x \leq u\} \cup \{y = 1,\; \ell \leq x \leq u,\; \alpha^T x \leq \beta\}.
4483 * \f]
4484 * These cuts can easily be separated for a given point \f$(x^*, y^*)\f$ by checking for each \f$i \in \{1, \dots, n\}\f$ whether
4485 * \f[
4486 * y^*(\alpha_i\, u_i\, [\alpha_i < 0] + \alpha_i\, \ell_i\, [\alpha_i > 0]) >
4487 * \alpha_i x_i^* + y^* )\alpha_i \ell_i [\alpha_i < 0] + \alpha_i u_i [\alpha_i > 0]),
4488 * \f]
4489 * where \f$[C] = 1\f$ if condition \f$C\f$ is satisfied, otherwise it is 0.
4490 * If the above inequality holds, \f$i\f$ is included in \f$I\f$, otherwise not.
4491 */
4492 static
4493 SCIP_RETCODE separatePerspective(
4494 SCIP* scip, /**< SCIP pointer */
4495 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
4496 SCIP_SOL* sol, /**< solution to be separated */
4497 int nconss, /**< number of constraints */
4498 SCIP_CONS** conss, /**< indicator constraints */
4499 int maxsepacuts, /**< maximal number of cuts to be generated */
4500 int* nGen /**< number of generated cuts */
4501 )
4502 { /*lint --e{850}*/
4503 SCIP_CONSHDLRDATA* conshdlrdata;
4504 SCIP_VAR** cutvars;
4505 SCIP_Real* cutvals;
4506 int nvars;
4507 int c;
4508
4509 assert( scip != NULL );
4510 assert( conshdlr != NULL );
4511 assert( conss != NULL );
4512 assert( nGen != NULL );
4513
4514 if ( *nGen >= maxsepacuts )
4515 return SCIP_OKAY;
4516
4517 nvars = SCIPgetNVars(scip);
4518 SCIP_CALL( SCIPallocBufferArray(scip, &cutvars, nvars+1) );
4519 SCIP_CALL( SCIPallocBufferArray(scip, &cutvals, nvars+1) );
4520
4521 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4522 assert( conshdlrdata != NULL );
4523
4524 /* loop through constraints */
4525 for (c = 0; c < nconss; ++c)
4526 {
4527 SCIP_CONSDATA* consdata;
4528 SCIP_CONS* lincons;
4529 SCIP_VAR* slackvar;
4530 SCIP_VAR* binvar;
4531 SCIP_Real binval;
4532
4533 assert( conss[c] != NULL );
4534 consdata = SCIPconsGetData(conss[c]);
4535 assert( consdata != NULL );
4536 slackvar = consdata->slackvar;
4537
4538 lincons = consdata->lincons;
4539 assert( lincons != NULL );
4540
4541 binvar = consdata->binvar;
4542 assert( binvar != NULL );
4543 binval = SCIPgetSolVal(scip, sol, binvar);
4544
4545 if ( SCIPconsIsActive(lincons) )
4546 {
4547 SCIP_VAR** linvars;
4548 SCIP_Real* linvals;
4549 SCIP_Real linrhs;
4550 SCIP_Bool finitebound = TRUE;
4551 SCIP_Real cutrhs = 0.0;
4552 SCIP_Real cutval;
4553 SCIP_Real signfactor = 1.0;
4554 SCIP_Real ypart;
4555 SCIP_Bool islocal = FALSE;
4556 int nlinvars;
4557 int cnt = 0;
4558 int j;
4559
4560 linvars = SCIPgetVarsLinear(scip, lincons);
4561 linvals = SCIPgetValsLinear(scip, lincons);
4562 nlinvars = SCIPgetNVarsLinear(scip, lincons);
4563
4564 linrhs = SCIPgetRhsLinear(scip, lincons);
4565 if ( SCIPisInfinity(scip, linrhs) )
4566 {
4567 if ( ! SCIPisInfinity(scip, SCIPgetLhsLinear(scip, lincons)) )
4568 {
4569 linrhs = -SCIPgetLhsLinear(scip, lincons);
4570 signfactor = -1.0;
4571 }
4572 else
4573 continue;
4574 }
4575 ypart = -linrhs;
4576 cutval = binval * ypart;
4577
4578 for (j = 0; j < nlinvars; ++j)
4579 {
4580 SCIP_Real linval;
4581 SCIP_Real lb;
4582 SCIP_Real ub;
4583 SCIP_Real din = 0.0;
4584 SCIP_Real dout = 0.0;
4585 SCIP_Real xpart;
4586 SCIP_Real xval;
4587
4588 if ( linvars[j] == slackvar )
4589 continue;
4590
4591 if ( conshdlrdata->sepapersplocal )
4592 {
4593 lb = SCIPvarGetLbLocal(linvars[j]);
4594 ub = SCIPvarGetUbLocal(linvars[j]);
4595
4596 if ( lb > SCIPvarGetLbGlobal(linvars[j]) )
4597 islocal = TRUE;
4598 if ( ub < SCIPvarGetUbGlobal(linvars[j]) )
4599 islocal = TRUE;
4600 }
4601 else
4602 {
4603 lb = SCIPvarGetLbGlobal(linvars[j]);
4604 ub = SCIPvarGetUbGlobal(linvars[j]);
4605 }
4606
4607 /* skip cases with unbounded variables */
4608 if ( SCIPisInfinity(scip, -lb) || SCIPisInfinity(scip, ub) )
4609 {
4610 finitebound = FALSE;
4611 break;
4612 }
4613
4614 /* compute rest parts for i in the set (din) or not in the set (dout) */
4615 linval = signfactor * linvals[j];
4616 if ( SCIPisNegative(scip, linval) )
4617 {
4618 din += linval * ub;
4619 dout += linval * lb;
4620 }
4621 else if ( SCIPisPositive(scip, linval) )
4622 {
4623 din += linval * lb;
4624 dout += linval * ub;
4625 }
4626
4627 xval = SCIPgetSolVal(scip, sol, linvars[j]);
4628 xpart = linval * xval;
4629
4630 /* if din > dout, we want to include i in the set */
4631 if ( SCIPisGT(scip, binval * din, binval * dout + xpart) )
4632 {
4633 ypart += din;
4634 cutval += binval * din;
4635 }
4636 else
4637 {
4638 /* otherwise i is not in the set */
4639 ypart += dout;
4640
4641 cutrhs += dout;
4642 cutval += binval * dout + xpart;
4643
4644 cutvars[cnt] = linvars[j];
4645 cutvals[cnt++] = linval;
4646 }
4647 }
4648
4649 if ( ! finitebound )
4650 continue;
4651
4652 if ( SCIPisEfficacious(scip, cutval - cutrhs) )
4653 {
4654 SCIP_ROW* row;
4655 SCIP_Bool infeasible;
4656 char name[50];
4657
4658 /* add y-variable */
4659 cutvars[cnt] = binvar;
4660 cutvals[cnt] = ypart;
4661 ++cnt;
4662
4663 SCIPdebugMsg(scip, "Found cut of lhs value %f > %f.\n", cutval, cutrhs);
4664 (void) SCIPsnprintf(name, 50, "persp%d", conshdlrdata->nperspcutsgen + *nGen);
4665 SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, conss[c], name, -SCIPinfinity(scip), cutrhs, islocal, FALSE, conshdlrdata->removable) );
4666 SCIP_CALL( SCIPaddVarsToRow(scip, row, cnt, cutvars, cutvals) );
4667 #ifdef SCIP_OUTPUT
4668 SCIP_CALL( SCIPprintRow(scip, row, NULL) );
4669 #endif
4670 SCIP_CALL( SCIPaddRow(scip, row, FALSE, &infeasible) );
4671 assert( ! infeasible );
4672 SCIP_CALL( SCIPreleaseRow(scip, &row));
4673 SCIP_CALL( SCIPresetConsAge(scip, conss[c]) );
4674 ++(*nGen);
4675 }
4676 }
4677 if ( *nGen >= maxsepacuts )
4678 break;
4679 }
4680
4681 SCIPfreeBufferArray(scip, &cutvals);
4682 SCIPfreeBufferArray(scip, &cutvars);
4683
4684 return SCIP_OKAY;
4685 }
4686
4687
4688 /** separation method
4689 *
4690 * We first check whether coupling inequalities can be separated (if required). If not enough of
4691 * these could be generated, we check whether IIS inequalities can be separated.
4692 */
4693 static
4694 SCIP_RETCODE separateIndicators(
4695 SCIP* scip, /**< SCIP pointer */
4696 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
4697 int nconss, /**< number of constraints */
4698 int nusefulconss, /**< number of useful constraints */
4699 SCIP_CONS** conss, /**< indicator constraints */
4700 SCIP_SOL* sol, /**< solution to be separated */
4701 SCIP_ENFOSEPATYPE enfosepatype, /**< type of enforcing/separating type */
4702 SCIP_RESULT* result /**< result */
4703 )
4704 {
4705 SCIP_CONSHDLRDATA* conshdlrdata;
4706 int maxsepacuts;
4707 int ncuts;
4708
4709 assert( scip != NULL );
4710 assert( conshdlr != NULL );
4711 assert( conss != NULL );
4712 assert( result != NULL );
4713
4714 *result = SCIP_DIDNOTRUN;
4715
4716 if ( nconss == 0 )
4717 return SCIP_OKAY;
4718
4719 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4720 assert( conshdlrdata != NULL );
4721 ncuts = 0;
4722
4723 /* get the maximal number of cuts allowed in a separation round */
4724 if ( SCIPgetDepth(scip) == 0 )
4725 maxsepacuts = conshdlrdata->maxsepacutsroot;
4726 else
4727 maxsepacuts = conshdlrdata->maxsepacuts;
4728
4729 /* first separate coupling inequalities (if required) */
4730 if ( conshdlrdata->sepacouplingcuts )
4731 {
4732 int c;
4733
4734 *result = SCIP_DIDNOTFIND;
4735
4736 /* check each constraint */
4737 for (c = 0; c < nusefulconss && ncuts < maxsepacuts; ++c)
4738 {
4739 SCIP_CONSDATA* consdata;
4740 SCIP_Bool islocal;
4741 SCIP_Real ub;
4742
4743 assert( conss != NULL );
4744 assert( conss[c] != NULL );
4745 consdata = SCIPconsGetData(conss[c]);
4746 assert( consdata != NULL );
4747 assert( consdata->slackvar != NULL );
4748 assert( consdata->binvar != NULL );
4749
4750 /* get upper bound for slack variable in linear constraint */
4751 islocal = FALSE;
4752 if ( conshdlrdata->sepacouplinglocal )
4753 {
4754 ub = SCIPvarGetUbLocal(consdata->slackvar);
4755 if ( ub < SCIPvarGetUbGlobal(consdata->slackvar) )
4756 islocal = TRUE;
4757 }
4758 else
4759 ub = SCIPvarGetUbGlobal(consdata->slackvar);
4760 assert( ! SCIPisFeasNegative(scip, ub) );
4761
4762 /* only use coefficients that are not too large */
4763 if ( ub <= conshdlrdata->sepacouplingvalue )
4764 {
4765 SCIP_Real activity;
4766
4767 activity = SCIPgetSolVal(scip, sol, consdata->slackvar) + ub * SCIPgetSolVal(scip, sol, consdata->binvar) - ub;
4768 if ( SCIPisEfficacious(scip, activity) )
4769 {
4770 SCIP_ROW* row;
4771 SCIP_Bool infeasible;
4772 #ifndef NDEBUG
4773 char name[50];
4774
4775 (void) SCIPsnprintf(name, 50, "couple%d", c);
4776 SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, conss[c], name, -SCIPinfinity(scip), ub, islocal, FALSE, conshdlrdata->removable) );
4777 #else
4778 SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, conss[c], "", -SCIPinfinity(scip), ub, islocal, FALSE, conshdlrdata->removable) );
4779 #endif
4780 SCIP_CALL( SCIPcacheRowExtensions(scip, row) );
4781
4782 SCIP_CALL( SCIPaddVarToRow(scip, row, consdata->slackvar, 1.0) );
4783 SCIP_CALL( SCIPaddVarToRow(scip, row, consdata->binvar, ub) );
4784 SCIP_CALL( SCIPflushRowExtensions(scip, row) );
4785
4786 SCIPdebugMsg(scip, "Separated coupling inequality for indicator constraint <%s> (coeff: %f).\n", SCIPconsGetName(conss[c]), ub);
4787 #ifdef SCIP_OUTPUT
4788 SCIP_CALL( SCIPprintRow(scip, row, NULL) );
4789 #endif
4790 SCIP_CALL( SCIPaddRow(scip, row, FALSE, &infeasible) );
4791 assert( ! infeasible );
4792 SCIP_CALL( SCIPreleaseRow(scip, &row));
4793
4794 SCIP_CALL( SCIPresetConsAge(scip, conss[c]) );
4795 *result = SCIP_SEPARATED;
4796
4797 ++ncuts;
4798 }
4799 }
4800 }
4801 SCIPdebugMsg(scip, "Number of separated coupling inequalities: %d.\n", ncuts);
4802 }
4803
4804 /* separate cuts from the alternative lp (if required) */
4805 if ( conshdlrdata->sepaalternativelp && ncuts < SEPAALTTHRESHOLD )
4806 {
4807 SCIP_Bool cutoff;
4808 int noldcuts;
4809
4810 SCIPdebugMsg(scip, "Separating inequalities for indicator constraints.\n");
4811
4812 noldcuts = ncuts;
4813 if ( *result == SCIP_DIDNOTRUN )
4814 *result = SCIP_DIDNOTFIND;
4815
4816 /* start separation */
4817 SCIP_CALL( separateIISRounding(scip, conshdlr, sol, enfosepatype, nconss, conss, maxsepacuts, &cutoff, &ncuts) );
4818 SCIPdebugMsg(scip, "Separated %d cuts from indicator constraints.\n", ncuts - noldcuts);
4819
4820 if ( cutoff )
4821 *result = SCIP_CUTOFF;
4822 else if ( ncuts > noldcuts )
4823 {
4824 conshdlrdata->niiscutsgen += ncuts;
4825
4826 /* possibly overwrite result from separation above */
4827 if ( conshdlrdata->genlogicor )
4828 *result = SCIP_CONSADDED;
4829 else
4830 *result = SCIP_SEPARATED;
4831 }
4832 }
4833
4834 /* separate cuts based on perspective formulation */
4835 if ( conshdlrdata->sepaperspective && ncuts < SEPAALTTHRESHOLD )
4836 {
4837 int noldcuts;
4838
4839 SCIPdebugMsg(scip, "Separating inequalities based on perspective formulation.\n");
4840
4841 noldcuts = ncuts;
4842 if ( *result == SCIP_DIDNOTRUN )
4843 *result = SCIP_DIDNOTFIND;
4844
4845 /* start separation */
4846 SCIP_CALL( separatePerspective(scip, conshdlr, sol, nconss, conss, maxsepacuts, &ncuts) );
4847 SCIPdebugMsg(scip, "Separated %d cuts from perspective formulation.\n", ncuts - noldcuts);
4848
4849 if ( ncuts > noldcuts )
4850 {
4851 conshdlrdata->nperspcutsgen += ncuts;
4852
4853 /* possibly overwrite result from separation above */
4854 *result = SCIP_SEPARATED;
4855 }
4856 }
4857
4858 return SCIP_OKAY;
4859 }
4860
4861
4862 /** initializes the constraint handler data */
4863 static
4864 void initConshdlrData(
4865 SCIP* scip, /**< SCIP pointer */
4866 SCIP_CONSHDLRDATA* conshdlrdata /**< constraint handler data */
4867 )
4868 {
4869 assert( conshdlrdata != NULL );
4870
4871 conshdlrdata->removable = TRUE;
4872 conshdlrdata->scaled = FALSE;
4873 conshdlrdata->altlp = NULL;
4874 conshdlrdata->nrows = 0;
4875 conshdlrdata->varhash = NULL;
4876 conshdlrdata->slackhash = NULL;
4877 conshdlrdata->lbhash = NULL;
4878 conshdlrdata->ubhash = NULL;
4879 conshdlrdata->nlbbounds = 0;
4880 conshdlrdata->nubbounds = 0;
4881 conshdlrdata->nslackvars = 0;
4882 conshdlrdata->objcutindex = -1;
4883 conshdlrdata->objupperbound = SCIPinfinity(scip);
4884 conshdlrdata->objaltlpbound = SCIPinfinity(scip);
4885 conshdlrdata->roundingminthres = 0.1;
4886 conshdlrdata->roundingmaxthres = 0.6;
4887 conshdlrdata->maxroundingrounds = MAXROUNDINGROUNDS;
4888 conshdlrdata->roundingoffset = 0.1;
4889 conshdlrdata->addedcouplingcons = FALSE;
4890 conshdlrdata->ninitconss = 0;
4891 conshdlrdata->nbinvarszero = 0;
4892 conshdlrdata->performedrestart = FALSE;
4893 conshdlrdata->objindicatoronly = FALSE;
4894 conshdlrdata->objothervarsonly = FALSE;
4895 conshdlrdata->minabsobj = 0.0;
4896 conshdlrdata->normtype = 'e';
4897 conshdlrdata->niiscutsgen = 0;
4898 conshdlrdata->nperspcutsgen = 0;
4899 }
4900
4901
4902 /* ---------------------------- upgrading methods -----------------------------------*/
4903
4904 /** tries to upgrade a linear constraint into an indicator constraint
4905 *
4906 * For some linear constraint of the form \f$a^T x + \alpha\, y \geq \beta\f$ with \f$y \in \{0,1\}\f$, we can upgrade
4907 * it to an indicator constraint if for the residual value \f$a^T x \geq \gamma\f$, we have \f$\alpha + \gamma \geq
4908 * \beta\f$: in this case, the constraint is always satisfied if \f$y = 1\f$.
4909 *
4910 * Similarly, for a linear constraint in the form \f$a^T x + \alpha\, y \leq \beta\f$ with \f$y \in \{0,1\}\f$, we can
4911 * upgrade it to an indicator constraint if for the residual value \f$a^T x \leq \gamma\f$, we have \f$\alpha + \gamma
4912 * \leq \beta\f$.
4913 */
4914 static
4915 SCIP_DECL_LINCONSUPGD(linconsUpgdIndicator)
4916 { /*lint --e{715}*/
4917 SCIP_CONSHDLRDATA* conshdlrdata;
4918 SCIP_CONSHDLR* conshdlr;
4919 SCIP_Real minactivity = 0.0;
4920 SCIP_Real maxactivity = 0.0;
4921 SCIP_Real maxabsval = -1.0;
4922 SCIP_Real secabsval = -1.0;
4923 int maxabsvalidx = -1;
4924 int j;
4925
4926 assert( scip != NULL );
4927 assert( upgdcons != NULL );
4928 assert( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), "linear") == 0 );
4929 assert( ! SCIPconsIsModifiable(cons) );
4930
4931 /* do not upgrade if there are at most 2 variables (2 variables should be upgraded to a varbound constraint) */
4932 if ( nvars <= 2 )
4933 return SCIP_OKAY;
4934
4935 /* cannot currently ranged constraints, since we can only return one constraint (and we would need one for each side each) */
4936 if ( ! SCIPisInfinity(scip, -lhs) && ! SCIPisInfinity(scip, rhs) )
4937 return SCIP_OKAY;
4938
4939 /* check whether upgrading is turned on */
4940 conshdlr = SCIPfindConshdlr(scip, "indicator");
4941 assert( conshdlr != NULL );
4942 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4943 assert( conshdlrdata != NULL );
4944
4945 if ( ! conshdlrdata->upgradelinear )
4946 return SCIP_OKAY;
4947
4948 /* calculate activities */
4949 for (j = 0; j < nvars; ++j)
4950 {
4951 SCIP_VAR* var;
4952 SCIP_Real val;
4953 SCIP_Real lb;
4954 SCIP_Real ub;
4955
4956 val = vals[j];
4957 assert( ! SCIPisZero(scip, val) );
4958
4959 var = vars[j];
4960 assert( var != NULL );
4961
4962 /* store maximal (and second to largest) value of coefficients */
4963 if ( SCIPisGE(scip, REALABS(val), maxabsval) )
4964 {
4965 secabsval = maxabsval;
4966 maxabsval = REALABS(val);
4967 maxabsvalidx = j;
4968 }
4969
4970 if ( ! SCIPvarIsBinary(var) )
4971 {
4972 if ( val > 0.0 )
4973 {
4974 lb = SCIPvarGetLbGlobal(var);
4975 ub = SCIPvarGetUbGlobal(var);
4976 }
4977 else
4978 {
4979 ub = SCIPvarGetLbGlobal(var);
4980 lb = SCIPvarGetUbGlobal(var);
4981 }
4982
4983 /* compute minimal activity */
4984 if ( SCIPisInfinity(scip, -lb) )
4985 minactivity = -SCIPinfinity(scip);
4986 else
4987 {
4988 if ( ! SCIPisInfinity(scip, -minactivity) )
4989 minactivity += val * lb;
4990 }
4991
4992 /* compute maximal activity */
4993 if ( SCIPisInfinity(scip, ub) )
4994 maxactivity = SCIPinfinity(scip);
4995 else
4996 {
4997 if ( ! SCIPisInfinity(scip, maxactivity) )
4998 maxactivity += val * ub;
4999 }
5000 }
5001 }
5002 assert( maxabsval >= 0.0 );
5003 assert( 0 <= maxabsvalidx && maxabsvalidx < nvars );
5004
5005 /* exit if largest coefficient does not belong to binary variable */
5006 if ( ! SCIPvarIsBinary(vars[maxabsvalidx]) )
5007 return SCIP_OKAY;
5008
5009 /* exit if the second largest coefficient is as large as largest */
5010 if ( SCIPisEQ(scip, secabsval, maxabsval) )
5011 return SCIP_OKAY;
5012
5013 /* cannot upgrade if all activities are infinity */
5014 if ( SCIPisInfinity(scip, -minactivity) && SCIPisInfinity(scip, maxactivity) )
5015 return SCIP_OKAY;
5016
5017 /* check each variable as indicator variable */
5018 for (j = 0; j < nvars; ++j)
5019 {
5020 SCIP_VAR** indconsvars;
5021 SCIP_Real* indconsvals;
5022 SCIP_Bool upgdlhs = FALSE;
5023 SCIP_Bool upgdrhs = FALSE;
5024 SCIP_Bool indneglhs = FALSE;
5025 SCIP_Bool indnegrhs = FALSE;
5026 SCIP_VAR* indvar;
5027 SCIP_Real indval;
5028 int l;
5029
5030 indvar = vars[j];
5031 indval = vals[j];
5032 assert( ! SCIPisZero(scip, indval) );
5033
5034 if ( ! SCIPvarIsBinary(indvar) )
5035 continue;
5036
5037 /* check for upgrading of lhs */
5038 if ( ! SCIPisInfinity(scip, -minactivity) && ! SCIPisInfinity(scip, -lhs) )
5039 {
5040 /* upgrading is possible with binary variable */
5041 if ( SCIPisGE(scip, minactivity, lhs) )
5042 upgdlhs = TRUE;
5043
5044 /* upgrading is possible with negated binary variable */
5045 if ( SCIPisGE(scip, minactivity + indval, lhs) )
5046 {
5047 upgdlhs = TRUE;
5048 indneglhs = TRUE;
5049 }
5050 }
5051
5052 /* check for upgrading of rhs */
5053 if ( ! SCIPisInfinity(scip, maxactivity) && ! SCIPisInfinity(scip, rhs) )
5054 {
5055 /* upgrading is possible with binary variable */
5056 if ( SCIPisLE(scip, maxactivity, rhs) )
5057 upgdrhs = TRUE;
5058
5059 /* upgrading is possible with negated binary variable */
5060 if ( SCIPisLE(scip, maxactivity + indval, rhs) )
5061 {
5062 upgdrhs = TRUE;
5063 indnegrhs = TRUE;
5064 }
5065 }
5066
5067 /* upgrade constraint */
5068 if ( upgdlhs || upgdrhs )
5069 {
5070 SCIP_VAR* indvar2;
5071 SCIP_Real bnd;
5072 int cnt = 0;
5073
5074 assert( ! upgdlhs || ! upgdrhs ); /* cannot treat ranged rows */
5075 SCIPdebugMsg(scip, "upgrading constraint <%s> to an indicator constraint.\n", SCIPconsGetName(cons));
5076
5077 SCIP_CALL( SCIPallocBufferArray(scip, &indconsvars, nvars - 1) );
5078 SCIP_CALL( SCIPallocBufferArray(scip, &indconsvals, nvars - 1) );
5079
5080 /* create constraint */
5081 for (l = 0; l < nvars; ++l)
5082 {
5083 if ( vars[l] == indvar )
5084 continue;
5085 indconsvars[cnt] = vars[l];
5086 if ( upgdlhs )
5087 indconsvals[cnt] = -vals[l];
5088 else
5089 indconsvals[cnt] = vals[l];
5090 ++cnt;
5091 }
5092
5093 if ( indneglhs || indnegrhs )
5094 {
5095 SCIP_CALL( SCIPgetNegatedVar(scip, indvar, &indvar2) );
5096 }
5097 else
5098 indvar2 = indvar;
5099
5100 if ( upgdlhs )
5101 {
5102 bnd = -lhs;
5103 if ( ! indneglhs )
5104 bnd -= indval;
5105 SCIP_CALL( SCIPcreateConsIndicator(scip, upgdcons, SCIPconsGetName(cons), indvar2, nvars-1, indconsvars, indconsvals, bnd,
5106 SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), SCIPconsIsEnforced(cons), SCIPconsIsChecked(cons), SCIPconsIsPropagated(cons),
5107 SCIPconsIsLocal(cons), SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons), SCIPconsIsStickingAtNode(cons)) );
5108 }
5109 else
5110 {
5111 bnd = rhs;
5112 if ( ! indnegrhs )
5113 bnd -= indval;
5114 SCIP_CALL( SCIPcreateConsIndicator(scip, upgdcons, SCIPconsGetName(cons), indvar2, nvars-1, indconsvars, indconsvals, bnd,
5115 SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), SCIPconsIsEnforced(cons), SCIPconsIsChecked(cons), SCIPconsIsPropagated(cons),
5116 SCIPconsIsLocal(cons), SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons), SCIPconsIsStickingAtNode(cons)) );
5117 }
5118
5119 #ifdef SCIP_DEBUG
5120 SCIPinfoMessage(scip, NULL, "upgrade: \n");
5121 SCIP_CALL( SCIPprintCons(scip, cons, NULL) );
5122 SCIPinfoMessage(scip, NULL, "\n");
5123 SCIP_CALL( SCIPprintCons(scip, *upgdcons, NULL) );
5124 SCIPinfoMessage(scip, NULL, "\n");
5125 SCIP_CALL( SCIPprintCons(scip, SCIPgetLinearConsIndicator(*upgdcons), NULL) );
5126 SCIPinfoMessage(scip, NULL, " (minact: %f, maxact: %f)\n", minactivity, maxactivity);
5127 #endif
5128
5129 SCIPfreeBufferArray(scip, &indconsvars);
5130 SCIPfreeBufferArray(scip, &indconsvals);
5131
5132 return SCIP_OKAY;
5133 }
5134 }
5135
5136 return SCIP_OKAY;
5137 }
5138
5139
5140 /* ---------------------------- constraint handler callback methods ----------------------*/
5141
5142 /** copy method for constraint handler plugins (called when SCIP copies plugins) */
5143 static
5144 SCIP_DECL_CONSHDLRCOPY(conshdlrCopyIndicator)
5145 { /*lint --e{715}*/
5146 assert( scip != NULL );
5147 assert( conshdlr != NULL );
5148 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
5149 assert( valid != NULL );
5150
5151 /* call inclusion method of constraint handler */
5152 SCIP_CALL( SCIPincludeConshdlrIndicator(scip) );
5153
5154 *valid = TRUE;
5155
5156 return SCIP_OKAY;
5157 }
5158
5159
5160 /** initialization method of constraint handler (called after problem was transformed) */
5161 static
5162 SCIP_DECL_CONSINIT(consInitIndicator)
5163 { /*lint --e{715}*/
5164 SCIP_CONSHDLRDATA* conshdlrdata;
5165
5166 assert( scip != NULL );
5167 assert( conshdlr != NULL );
5168 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
5169
5170 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5171 assert( conshdlrdata != NULL );
5172
5173 initConshdlrData(scip, conshdlrdata);
5174
5175 /* find trysol heuristic */
5176 if ( conshdlrdata->trysolutions && conshdlrdata->heurtrysol == NULL )
5177 {
5178 conshdlrdata->heurtrysol = SCIPfindHeur(scip, "trysol");
5179 }
5180
5181 return SCIP_OKAY;
5182 }
5183
5184
5185 /** deinitialization method of constraint handler (called before transformed problem is freed) */
5186 static
5187 SCIP_DECL_CONSEXIT(consExitIndicator)
5188 { /*lint --e{715}*/
5189 SCIP_CONSHDLRDATA* conshdlrdata;
5190
5191 assert( scip != NULL );
5192 assert( conshdlr != NULL );
5193 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
5194
5195 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5196
5197 if ( conshdlrdata->binvarhash != NULL )
5198 SCIPhashmapFree(&conshdlrdata->binvarhash);
5199
5200 SCIPfreeBlockMemoryArrayNull(scip, &conshdlrdata->addlincons, conshdlrdata->maxaddlincons);
5201 conshdlrdata->maxaddlincons = 0;
5202 conshdlrdata->naddlincons = 0;
5203 conshdlrdata->nrows = 0;
5204
5205 return SCIP_OKAY;
5206 }
5207
5208
5209 /** destructor of constraint handler to free constraint handler data (called when SCIP is exiting) */
5210 static
5211 SCIP_DECL_CONSFREE(consFreeIndicator)
5212 {
5213 SCIP_CONSHDLRDATA* conshdlrdata;
5214
5215 assert( scip != NULL );
5216 assert( conshdlr != NULL );
5217 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
5218
5219 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5220 assert( conshdlrdata != NULL );
5221 assert( conshdlrdata->altlp == NULL );
5222 assert( conshdlrdata->varhash == NULL );
5223 assert( conshdlrdata->lbhash == NULL );
5224 assert( conshdlrdata->ubhash == NULL );
5225 assert( conshdlrdata->slackhash == NULL );
5226
5227 if ( conshdlrdata->maxaddlincons > 0 )
5228 {
5229 /* if problem was not yet transformed the array may need to be freed, because we did not call the EXIT callback */
5230 SCIPfreeBlockMemoryArrayNull(scip, &conshdlrdata->addlincons, conshdlrdata->maxaddlincons);
5231 }
5232 assert( conshdlrdata->addlincons == NULL );
5233 conshdlrdata->naddlincons = 0;
5234 conshdlrdata->maxaddlincons = 0;
5235
5236 SCIPfreeBlockMemory(scip, &conshdlrdata);
5237
5238 return SCIP_OKAY;
5239 }
5240
5241
5242 /** solving process initialization method of constraint handler (called when branch and bound process is about to begin) */
5243 static
5244 SCIP_DECL_CONSINITSOL(consInitsolIndicator)
5245 {
5246 SCIP_CONSHDLRDATA* conshdlrdata;
5247 int c;
5248
5249 assert( scip != NULL );
5250 assert( conshdlr != NULL );
5251 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
5252
5253 if ( SCIPgetStatus(scip) == SCIP_STATUS_OPTIMAL || SCIPgetStatus(scip) == SCIP_STATUS_INFEASIBLE ||
5254 SCIPgetStatus(scip) == SCIP_STATUS_UNBOUNDED || SCIPgetStatus(scip) == SCIP_STATUS_INFORUNBD )
5255 return SCIP_OKAY;
5256
5257 SCIPdebugMsg(scip, "Initsol for indicator constraints.\n");
5258
5259 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5260 assert( conshdlrdata != NULL );
5261 assert( conshdlrdata->slackhash == NULL );
5262
5263 SCIP_CALL( SCIPgetCharParam(scip, "separating/efficacynorm", &conshdlrdata->normtype) );
5264
5265 if ( conshdlrdata->sepaalternativelp )
5266 {
5267 /* generate hash for storing all slack variables (size is just a guess) */
5268 SCIP_CALL( SCIPhashmapCreate(&conshdlrdata->slackhash, SCIPblkmem(scip), SCIPgetNVars(scip)) );
5269 assert( conshdlrdata->slackhash != NULL );
5270
5271 /* first initialize slack hash */
5272 for (c = 0; c < nconss; ++c)
5273 {
5274 SCIP_CONSDATA* consdata;
5275
5276 assert( conss != NULL );
5277 assert( conss[c] != NULL );
5278 assert( SCIPconsIsTransformed(conss[c]) );
5279
5280 consdata = SCIPconsGetData(conss[c]);
5281 assert( consdata != NULL );
5282
5283 assert( consdata->slackvar != NULL );
5284
5285 /* insert slack variable into hash */
5286 SCIP_CALL( SCIPhashmapInsertInt(conshdlrdata->slackhash, consdata->slackvar, INT_MAX) );
5287 assert( SCIPhashmapExists(conshdlrdata->slackhash, consdata->slackvar) );
5288 ++conshdlrdata->nslackvars;
5289 }
5290
5291 if ( conshdlrdata->genlogicor )
5292 {
5293 SCIP_CONSHDLR* logicorconshdlr;
5294 int logicorsepafreq;
5295 int sepafreq;
5296
5297 /* If we generate logicor constraints, but the separation frequency is not 1, output warning */
5298 logicorconshdlr = SCIPfindConshdlr(scip, "logicor");
5299 if ( logicorconshdlr == NULL )
5300 {
5301 SCIPerrorMessage("Logicor constraint handler not included, cannot generate constraints.\n");
5302 return SCIP_ERROR;
5303 }
5304 logicorsepafreq = SCIPconshdlrGetSepaFreq(logicorconshdlr);
5305 sepafreq = SCIPconshdlrGetSepaFreq(conshdlr);
5306 if ( (sepafreq != -1 || conshdlrdata->enforcecuts) && logicorsepafreq != 1 )
5307 {
5308 SCIPwarningMessage(scip, "For better performance set parameter 'constraints/logicor/sepafreq' to 1 if 'constraints/included/genlogicor' is true.\n");
5309 }
5310 }
5311 }
5312
5313 /* check each constraint */
5314 conshdlrdata->objothervarsonly = TRUE;
5315 for (c = 0; c < nconss; ++c)
5316 {
5317 SCIP_CONSDATA* consdata;
5318
5319 assert( conss != NULL );
5320 assert( conss[c] != NULL );
5321 assert( SCIPconsIsTransformed(conss[c]) );
5322
5323 consdata = SCIPconsGetData(conss[c]);
5324 assert( consdata != NULL );
5325 assert( consdata->binvar != NULL );
5326 assert( consdata->slackvar != NULL );
5327
5328 /* Presolving might replace a slack variable by an active variable. Thus, the objective of a slack variables might
5329 * be nonzero. However, we do not need to check slack variables here. */
5330 if ( ! SCIPisZero(scip, varGetObjDelta(consdata->binvar)) )
5331 conshdlrdata->objothervarsonly = FALSE;
5332
5333 /* deactivate */
5334 if ( ! consdata->linconsactive )
5335 {
5336 SCIP_CALL( SCIPdisableCons(scip, consdata->lincons) );
5337 }
5338 else
5339 {
5340 /* add constraint to alternative LP if not already done */
5341 if ( conshdlrdata->sepaalternativelp && consdata->colindex < 0 )
5342 {
5343 SCIP_CALL( addAltLPConstraint(scip, conshdlr, consdata->lincons, consdata->slackvar, 1.0, &consdata->colindex) );
5344 SCIPdebugMsg(scip, "Added column for <%s> to alternative LP with column index %d.\n", SCIPconsGetName(conss[c]),consdata->colindex);
5345 #ifdef SCIP_OUTPUT
5346 SCIP_CALL( SCIPprintCons(scip, consdata->lincons, NULL) );
5347 SCIPinfoMessage(scip, NULL, ";\n");
5348 #endif
5349 }
5350 }
5351
5352 /* add nlrow representation to NLP, if NLP had been constructed
5353 *
5354 * Note, that we did not tell SCIP in exitpre that we have something to add to the NLP, thus
5355 * indicators are only available in the NLP for MINLPs, but not for MIPs with indicators.
5356 */
5357 if ( SCIPisNLPConstructed(scip) && SCIPconsIsChecked(conss[c]) )
5358 {
5359 /* create nonlinear row binary variable * slack variable = 0 */
5360 SCIP_NLROW* nlrow;
5361 SCIP_EXPR* quadexpr;
5362 SCIP_EXPR* varexprs[2];
5363
5364 SCIP_CALL( SCIPcreateExprVar(scip, &varexprs[0], consdata->binvar, NULL, NULL) );
5365 SCIP_CALL( SCIPcreateExprVar(scip, &varexprs[1], consdata->slackvar, NULL, NULL) );
5366 SCIP_CALL( SCIPcreateExprProduct(scip, &quadexpr, 2, varexprs, 1.0, NULL, NULL) );
5367
5368 SCIP_CALL( SCIPcreateNlRow(scip, &nlrow, SCIPconsGetName(conss[c]), 0.0, 0, NULL, NULL, quadexpr, 0.0, 0.0, SCIP_EXPRCURV_UNKNOWN) );
5369
5370 SCIP_CALL( SCIPreleaseExpr(scip, &quadexpr) );
5371 SCIP_CALL( SCIPreleaseExpr(scip, &varexprs[1]) );
5372 SCIP_CALL( SCIPreleaseExpr(scip, &varexprs[0]) );
5373
5374 /* add row to NLP and forget about it */
5375 SCIP_CALL( SCIPaddNlRow(scip, nlrow) );
5376 SCIP_CALL( SCIPreleaseNlRow(scip, &nlrow) );
5377 }
5378 }
5379
5380 SCIPdebugMsg(scip, "Initialized %d indicator constraints.\n", nconss);
5381
5382 /* check additional constraints */
5383 if ( conshdlrdata->sepaalternativelp )
5384 {
5385 SCIP_CONS* cons;
5386 int colindex;
5387 int cnt = 0;
5388
5389 /* add stored linear constraints if they exist */
5390 if ( conshdlrdata->naddlincons > 0 )
5391 {
5392 for (c = 0; c < conshdlrdata->naddlincons; ++c)
5393 {
5394 cons = conshdlrdata->addlincons[c];
5395
5396 /* get transformed constraint - since it is needed only here, we do not store the information */
5397 if ( ! SCIPconsIsTransformed(cons) )
5398 {
5399 SCIP_CALL( SCIPgetTransformedCons(scip, conshdlrdata->addlincons[c], &cons) );
5400
5401 /* @todo check when exactly the transformed constraint does not exist - SCIPisActive() does not suffice */
5402 if ( cons == NULL )
5403 continue;
5404 }
5405 SCIP_CALL( addAltLPConstraint(scip, conshdlr, cons, NULL, 0.0, &colindex) );
5406 ++cnt;
5407
5408 #ifdef SCIP_OUTPUT
5409 SCIP_CALL( SCIPprintCons(scip, cons, NULL) );
5410 SCIPinfoMessage(scip, NULL, ";\n");
5411 #endif
5412 }
5413 SCIPdebugMsg(scip, "Added %d additional columns to alternative LP.\n", cnt);
5414 }
5415 else
5416 {
5417 /* if no stored linear constraints are available, possibly collect other linear constraints; we only use linear
5418 * constraints, since most other constraints involve integral variables, and in this context we will likely
5419 * benefit much more from continuous variables. */
5420 if ( conshdlrdata->useotherconss )
5421 {
5422 const char* conshdlrname;
5423 SCIP_CONS** allconss;
5424 int nallconss;
5425
5426 nallconss = SCIPgetNConss(scip);
5427 allconss = SCIPgetConss(scip);
5428
5429 /* loop through all constraints */
5430 for (c = 0; c < nallconss; ++c)
5431 {
5432 /* get constraint */
5433 cons = allconss[c];
5434 assert( cons != NULL );
5435 assert( SCIPconsIsTransformed(cons) );
5436
5437 /* get constraint handler name */
5438 conshdlrname = SCIPconshdlrGetName(SCIPconsGetHdlr(cons));
5439
5440 /* check type of constraint (only take modifiable linear constraints) */
5441 if ( strcmp(conshdlrname, "linear") == 0 && ! SCIPconsIsModifiable(cons) )
5442 {
5443 /* avoid adding linear constraints that correspond to indicator constraints */
5444 if ( strncmp(SCIPconsGetName(cons), "indlin", 6) != 0 )
5445 {
5446 SCIP_CALL( addAltLPConstraint(scip, conshdlr, cons, NULL, 0.0, &colindex) );
5447 SCIPdebugMsg(scip, "Added column for linear constraint <%s> to alternative LP with column index %d.\n", SCIPconsGetName(cons), colindex);
5448 ++cnt;
5449 }
5450 }
5451 }
5452 SCIPdebugMsg(scip, "Added %d additional columns from linear constraints to alternative LP.\n", cnt);
5453 }
5454 }
5455 }
5456
5457 /* initialize event handler if restart should be forced */
5458 if ( conshdlrdata->forcerestart )
5459 {
5460 SCIP_Bool* covered;
5461 SCIP_VAR** vars;
5462 int nvars;
5463 int j;
5464
5465 assert( conshdlrdata->eventhdlrrestart != NULL );
5466
5467 /* store number of initial constraints */
5468 conshdlrdata->ninitconss = SCIPconshdlrGetNActiveConss(conshdlr);
5469
5470 /* reset number of fixed binary variables */
5471 conshdlrdata->nbinvarszero = 0;
5472
5473 /* loop through variables */
5474 nvars = SCIPgetNVars(scip);
5475 vars = SCIPgetVars(scip);
5476
5477 conshdlrdata->objindicatoronly = FALSE;
5478 conshdlrdata->minabsobj = SCIP_REAL_MAX;
5479
5480 /* unmark all variables */
5481 SCIP_CALL( SCIPallocBufferArray(scip, &covered, nvars) );
5482 for (j = 0; j < nvars; ++j)
5483 covered[j] = FALSE;
5484
5485 /* mark indicator variables */
5486 for (c = 0; c < nconss; ++c)
5487 {
5488 SCIP_CONSDATA* consdata;
5489 int probindex;
5490
5491 assert( conss != NULL );
5492 assert( conss[c] != NULL );
5493
5494 /* avoid non-active indicator constraints */
5495 if ( ! SCIPconsIsActive(conss[c]) )
5496 continue;
5497
5498 consdata = SCIPconsGetData(conss[c]);
5499 assert( consdata != NULL );
5500 assert( consdata->binvar != NULL );
5501
5502 if ( SCIPvarIsNegated(consdata->binvar) )
5503 {
5504 assert( SCIPvarGetNegatedVar(consdata->binvar) != NULL );
5505 probindex = SCIPvarGetProbindex(SCIPvarGetNegatedVar(consdata->binvar));
5506 }
5507 else
5508 probindex = SCIPvarGetProbindex(consdata->binvar);
5509
5510 /* if presolving detected infeasibility it might be that the binary variables are not active */
5511 if ( probindex < 0 )
5512 continue;
5513
5514 assert( 0 <= probindex && probindex < nvars );
5515 covered[probindex] = TRUE;
5516 }
5517
5518 /* check all variables */
5519 for (j = 0; j < nvars; ++j)
5520 {
5521 SCIP_Real obj;
5522
5523 obj = SCIPvarGetObj(vars[j]);
5524 if ( ! SCIPisZero(scip, obj) )
5525 {
5526 if ( ! covered[j] )
5527 break;
5528 if ( ! SCIPisIntegral(scip, obj) )
5529 break;
5530 if ( REALABS(obj) < conshdlrdata->minabsobj )
5531 conshdlrdata->minabsobj = REALABS(obj);
5532 }
5533 }
5534
5535 /* if all variables have integral objective and only indicator variables have nonzero objective */
5536 if ( j >= nvars )
5537 {
5538 /* if there are variables with nonzero objective */
5539 if ( conshdlrdata->minabsobj < SCIP_REAL_MAX )
5540 {
5541 assert( SCIPisIntegral(scip, conshdlrdata->minabsobj) );
5542 assert( SCIPisGE(scip, conshdlrdata->minabsobj, 1.0) );
5543
5544 conshdlrdata->objindicatoronly = TRUE;
5545
5546 assert( conshdlrdata->eventhdlrrestart != NULL );
5547 SCIP_CALL( SCIPcatchEvent(scip, SCIP_EVENTTYPE_BESTSOLFOUND, conshdlrdata->eventhdlrrestart, (SCIP_EVENTDATA*) conshdlrdata, NULL) );
5548 }
5549 }
5550
5551 SCIPfreeBufferArray(scip, &covered);
5552 }
5553
5554 return SCIP_OKAY;
5555 }
5556
5557
5558 /** solving process deinitialization method of constraint handler (called before branch and bound process data is freed) */
5559 static
5560 SCIP_DECL_CONSEXITSOL(consExitsolIndicator)
5561 { /*lint --e{715}*/
5562 SCIP_CONSHDLRDATA* conshdlrdata;
5563 int c;
5564
5565 assert( scip != NULL );
5566 assert( conshdlr != NULL );
5567 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
5568
5569 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5570 assert( conshdlrdata != NULL );
5571
5572 if ( conshdlrdata->sepaalternativelp )
5573 {
5574 if ( conshdlrdata->slackhash != NULL )
5575 {
5576 #ifdef SCIP_DEBUG
5577 SCIPinfoMessage(scip, NULL, "\nStatistics for cons_indicator slack hash:\n");
5578 SCIPhashmapPrintStatistics(conshdlrdata->slackhash, SCIPgetMessagehdlr(scip));
5579 #endif
5580 SCIPhashmapFree(&conshdlrdata->slackhash);
5581 }
5582
5583 if ( conshdlrdata->altlp != NULL )
5584 {
5585 assert( conshdlrdata->varhash != NULL );
5586 assert( conshdlrdata->lbhash != NULL );
5587 assert( conshdlrdata->ubhash != NULL );
5588
5589 #ifdef SCIP_DEBUG
5590 SCIPinfoMessage(scip, NULL, "\nStatistics for cons_indicator var hash:\n");
5591 SCIPhashmapPrintStatistics(conshdlrdata->varhash, SCIPgetMessagehdlr(scip));
5592 SCIPinfoMessage(scip, NULL, "\nStatistics for cons_indicator lower bound hash:\n");
5593 SCIPhashmapPrintStatistics(conshdlrdata->lbhash, SCIPgetMessagehdlr(scip));
5594 SCIPinfoMessage(scip, NULL, "\nStatistics for cons_indicator upper bound hash:\n");
5595 SCIPhashmapPrintStatistics(conshdlrdata->ubhash, SCIPgetMessagehdlr(scip));
5596 #endif
5597
5598 SCIPhashmapFree(&conshdlrdata->varhash);
5599 SCIPhashmapFree(&conshdlrdata->lbhash);
5600 SCIPhashmapFree(&conshdlrdata->ubhash);
5601
5602 SCIP_CALL( SCIPlpiFree(&conshdlrdata->altlp) );
5603
5604 /* save the information that the columns have been deleted */
5605 for (c = 0; c < nconss; ++c)
5606 {
5607 SCIP_CONSDATA* consdata;
5608
5609 assert( conss != NULL );
5610 assert( conss[c] != NULL );
5611
5612 consdata = SCIPconsGetData(conss[c]);
5613 assert( consdata != NULL );
5614 consdata->colindex = -1;
5615 }
5616 }
5617 }
5618 else
5619 {
5620 assert( conshdlrdata->slackhash == NULL );
5621 assert( conshdlrdata->varhash == NULL );
5622 assert( conshdlrdata->lbhash == NULL );
5623 assert( conshdlrdata->ubhash == NULL );
5624 }
5625
5626 return SCIP_OKAY;
5627 }
5628
5629
5630 /** frees specific constraint data */
5631 static
5632 SCIP_DECL_CONSDELETE(consDeleteIndicator)
5633 {
5634 assert( scip != NULL );
5635 assert( conshdlr != NULL );
5636 assert( cons != NULL );
5637 assert( consdata != NULL );
5638 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
5639
5640 #ifdef SCIP_MORE_DEBUG
5641 SCIPdebugMsg(scip, "Deleting indicator constraint <%s>.\n", SCIPconsGetName(cons) );
5642 #endif
5643
5644 /* drop events on transformed variables */
5645 if ( SCIPconsIsTransformed(cons) )
5646 {
5647 SCIP_CONSHDLRDATA* conshdlrdata;
5648
5649 /* get constraint handler data */
5650 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5651 assert( conshdlrdata != NULL );
5652
5653 if ( conshdlrdata->sepaalternativelp )
5654 {
5655 SCIP_CALL( deleteAltLPConstraint(scip, conshdlr, cons) );
5656 }
5657
5658 assert( (*consdata)->slackvar != NULL );
5659 assert( (*consdata)->binvar != NULL );
5660
5661 /* free events only in correct stages */
5662 if ( SCIPgetStage(scip) >= SCIP_STAGE_TRANSFORMING && SCIPgetStage(scip) <= SCIP_STAGE_EXITSOLVE )
5663 {
5664 if ( (*consdata)->linconsactive )
5665 {
5666 assert( conshdlrdata->eventhdlrbound != NULL );
5667 SCIP_CALL( SCIPdropVarEvent(scip, (*consdata)->binvar, SCIP_EVENTTYPE_BOUNDCHANGED, conshdlrdata->eventhdlrbound,
5668 (SCIP_EVENTDATA*)*consdata, -1) );
5669 SCIP_CALL( SCIPdropVarEvent(scip, (*consdata)->slackvar, SCIP_EVENTTYPE_BOUNDCHANGED, conshdlrdata->eventhdlrbound,
5670 (SCIP_EVENTDATA*)*consdata, -1) );
5671 }
5672 if ( conshdlrdata->forcerestart )
5673 {
5674 assert( conshdlrdata->eventhdlrrestart != NULL );
5675 SCIP_CALL( SCIPdropVarEvent(scip, (*consdata)->binvar, SCIP_EVENTTYPE_GBDCHANGED, conshdlrdata->eventhdlrrestart,
5676 (SCIP_EVENTDATA*) conshdlrdata, -1) );
5677 }
5678 }
5679 }
5680
5681 /* Can there be cases where lincons is NULL, e.g., if presolve found the problem infeasible? */
5682 assert( (*consdata)->lincons != NULL );
5683
5684 /* release linear constraint and slack variable */
5685 SCIP_CALL( SCIPreleaseVar(scip, &(*consdata)->slackvar) );
5686 SCIP_CALL( SCIPreleaseCons(scip, &(*consdata)->lincons) );
5687
5688 SCIPfreeBlockMemory(scip, consdata);
5689
5690 return SCIP_OKAY;
5691 }
5692
5693
5694 /** transforms constraint data into data belonging to the transformed problem */
5695 static
5696 SCIP_DECL_CONSTRANS(consTransIndicator)
5697 {
5698 SCIP_CONSDATA* consdata;
5699 SCIP_CONSHDLRDATA* conshdlrdata;
5700 SCIP_CONSDATA* sourcedata;
5701 char s[SCIP_MAXSTRLEN];
5702
5703 assert( scip != NULL );
5704 assert( conshdlr != NULL );
5705 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
5706 assert( sourcecons != NULL );
5707 assert( targetcons != NULL );
5708
5709 /* get constraint handler data */
5710 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5711 assert( conshdlrdata != NULL );
5712 assert( conshdlrdata->eventhdlrbound != NULL );
5713
5714 #ifdef SCIP_MORE_DEBUG
5715 SCIPdebugMsg(scip, "Transforming indicator constraint: <%s>.\n", SCIPconsGetName(sourcecons) );
5716 #endif
5717
5718 /* get data of original constraint */
5719 sourcedata = SCIPconsGetData(sourcecons);
5720 assert( sourcedata != NULL );
5721 assert( sourcedata->binvar != NULL );
5722
5723 /* check for slackvar */
5724 if ( sourcedata->slackvar == NULL )
5725 {
5726 SCIPerrorMessage("The indicator constraint <%s> needs a slack variable.\n", SCIPconsGetName(sourcecons));
5727 return SCIP_INVALIDDATA;
5728 }
5729
5730 /* check for linear constraint */
5731 if ( sourcedata->lincons == NULL )
5732 {
5733 SCIPerrorMessage("The indicator constraint <%s> needs a linear constraint variable.\n", SCIPconsGetName(sourcecons));
5734 return SCIP_INVALIDDATA;
5735 }
5736 assert( sourcedata->lincons != NULL );
5737 assert( sourcedata->slackvar != NULL );
5738
5739 /* create constraint data */
5740 consdata = NULL;
5741 /* Note that the constraint has activeone = TRUE, since the binary variable has been negated already if needed. */
5742 SCIP_CALL( consdataCreate(scip, conshdlr, conshdlrdata, SCIPconsGetName(sourcecons), &consdata, conshdlrdata->eventhdlrbound,
5743 conshdlrdata->eventhdlrrestart, sourcedata->binvar, TRUE, sourcedata->lessthanineq, sourcedata->slackvar, sourcedata->lincons, sourcedata->linconsactive) );
5744 consdata->activeone = sourcedata->activeone;
5745 assert( consdata != NULL );
5746
5747 /* capture slack variable and linear constraint */
5748 SCIP_CALL( SCIPcaptureVar(scip, consdata->slackvar) );
5749 SCIP_CALL( SCIPcaptureCons(scip, consdata->lincons) );
5750
5751 /* create transformed constraint with the same flags */
5752 (void) SCIPsnprintf(s, SCIP_MAXSTRLEN, "t_%s", SCIPconsGetName(sourcecons));
5753 SCIP_CALL( SCIPcreateCons(scip, targetcons, s, conshdlr, consdata,
5754 SCIPconsIsInitial(sourcecons), SCIPconsIsSeparated(sourcecons),
5755 SCIPconsIsEnforced(sourcecons), SCIPconsIsChecked(sourcecons),
5756 SCIPconsIsPropagated(sourcecons), SCIPconsIsLocal(sourcecons),
5757 SCIPconsIsModifiable(sourcecons), SCIPconsIsDynamic(sourcecons),
5758 SCIPconsIsRemovable(sourcecons), SCIPconsIsStickingAtNode(sourcecons)) );
5759
5760 /* make sure that binary variable hash exists */
5761 if ( conshdlrdata->sepaalternativelp )
5762 {
5763 if ( conshdlrdata->binvarhash == NULL )
5764 {
5765 SCIP_CALL( SCIPhashmapCreate(&conshdlrdata->binvarhash, SCIPblkmem(scip), SCIPgetNOrigVars(scip)) );
5766 }
5767
5768 /* check whether binary variable is present: note that a binary variable might appear several times, but this seldomly happens. */
5769 assert( conshdlrdata->binvarhash != NULL );
5770 if ( ! SCIPhashmapExists(conshdlrdata->binvarhash, (void*) consdata->binvar) )
5771 {
5772 SCIP_CALL( SCIPhashmapInsert(conshdlrdata->binvarhash, (void*) consdata->binvar, (void*) (*targetcons)) );
5773 }
5774 }
5775
5776 return SCIP_OKAY;
5777 }
5778
5779
5780 /** presolving initialization method of constraint handler (called when presolving is about to begin) */
5781 static
5782 SCIP_DECL_CONSINITPRE(consInitpreIndicator)
5783 { /*lint --e{715}*/
5784 SCIP_CONSHDLRDATA* conshdlrdata;
5785 int c;
5786
5787 assert( scip != NULL );
5788 assert( conshdlr != NULL );
5789 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
5790
5791 if ( SCIPgetStatus(scip) != SCIP_STATUS_UNKNOWN )
5792 return SCIP_OKAY;
5793
5794 SCIPdebugMsg(scip, "Initpre method for indicator constraints.\n");
5795
5796 /* check each constraint and get transformed linear constraint */
5797 for (c = 0; c < nconss; ++c)
5798 {
5799 SCIP_CONSDATA* consdata;
5800
5801 assert( conss != NULL );
5802 assert( conss[c] != NULL );
5803 assert( SCIPconsIsTransformed(conss[c]) );
5804
5805 consdata = SCIPconsGetData(conss[c]);
5806 assert( consdata != NULL );
5807
5808 /* if not happened already, get transformed linear constraint */
5809 assert( consdata->lincons != NULL );
5810 assert( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(consdata->lincons)), "linear") == 0 );
5811
5812 /* in a restart the linear constraint might already be transformed */
5813 if ( ! SCIPconsIsTransformed(consdata->lincons) )
5814 {
5815 SCIP_CONS* translincons;
5816
5817 SCIP_CALL( SCIPgetTransformedCons(scip, consdata->lincons, &translincons) );
5818 assert( translincons != NULL );
5819
5820 SCIP_CALL( SCIPreleaseCons(scip, &consdata->lincons) );
5821 SCIP_CALL( SCIPcaptureCons(scip, translincons) );
5822 consdata->lincons = translincons;
5823 }
5824 }
5825
5826 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5827 assert( conshdlrdata != NULL );
5828
5829 /* reset flag, in case presolve was called for some problem before */
5830 conshdlrdata->addedcouplingcons = FALSE;
5831
5832 return SCIP_OKAY;
5833 }
5834
5835
5836 /** presolving method of constraint handler
5837 *
5838 * For an indicator constraint with binary variable \f$y\f$ and slack variable \f$s\f$ the coupling
5839 * inequality \f$s \le M (1-y)\f$ (equivalently: \f$s + M y \le M\f$) is inserted, where \f$M\f$ is
5840 * an upper bound on the value of \f$s\f$. If \f$M\f$ is too large the inequality is not
5841 * inserted. Depending on the parameter @a addcouplingcons we add a variable upper bound or a row
5842 * (in consInitlpIndicator()).
5843 *
5844 * @warning We can never delete linear constraints, because we need them to get the right values
5845 * for the slack variables!
5846 */
5847 static
5848 SCIP_DECL_CONSPRESOL(consPresolIndicator)
5849 { /*lint --e{715}*/
5850 SCIP_CONSHDLRDATA* conshdlrdata;
5851 SCIP_Bool noReductions;
5852 SCIPdebug( int oldnfixedvars = *nfixedvars; )
5853 SCIPdebug( int oldndelconss = *ndelconss; )
5854 int c;
5855
5856 assert( scip != NULL );
5857 assert( conshdlr != NULL );
5858 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
5859 assert( result != NULL );
5860
5861 *result = SCIP_DIDNOTRUN;
5862 /* get constraint handler data */
5863 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5864 assert( conshdlrdata != NULL );
5865
5866 SCIPdebugMsg(scip, "Presolving indicator constraints.\n");
5867
5868 /* only run if success is possible */
5869 if ( nrounds == 0 || nnewfixedvars > 0 || nnewchgbds > 0 || nnewaggrvars > 0 )
5870 {
5871 *result = SCIP_DIDNOTFIND;
5872
5873 /* check each constraint */
5874 for (c = 0; c < nconss; ++c)
5875 {
5876 SCIP_CONSDATA* consdata;
5877 SCIP_CONS* cons;
5878 SCIP_Bool success;
5879 SCIP_Bool cutoff;
5880
5881 assert( conss != NULL );
5882 assert( conss[c] != NULL );
5883 cons = conss[c];
5884 consdata = SCIPconsGetData(cons);
5885 assert( consdata != NULL );
5886 assert( consdata->binvar != NULL );
5887 assert( ! SCIPconsIsModifiable(cons) );
5888
5889 #ifdef SCIP_MORE_DEBUG
5890 SCIPdebugMsg(scip, "Presolving indicator constraint <%s>.\n", SCIPconsGetName(cons) );
5891 #endif
5892
5893 /* do nothing if the linear constraint is not active */
5894 if ( ! consdata->linconsactive )
5895 continue;
5896
5897 assert( consdata->lincons != NULL );
5898 assert( consdata->slackvar != NULL );
5899 assert( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(consdata->lincons)), "linear") == 0 );
5900 assert( SCIPconsIsTransformed(consdata->lincons) );
5901
5902 /* add implications if not yet done */
5903 if ( ! consdata->implicationadded )
5904 {
5905 int nbnds = 0;
5906 SCIP_CALL( SCIPaddVarImplication(scip, consdata->binvar, TRUE, consdata->slackvar, SCIP_BOUNDTYPE_UPPER, 0.0,
5907 &cutoff, &nbnds) );
5908 *nchgbds += nbnds;
5909
5910 /* cutoff/infeasible might be true if preprocessing was truncated */
5911 /* note: nbdchgs == 0 is not necessarily true, because preprocessing might be truncated. */
5912 consdata->implicationadded = TRUE;
5913 if ( cutoff )
5914 {
5915 *result = SCIP_CUTOFF;
5916 return SCIP_OKAY;
5917 }
5918 }
5919
5920 /* check type of slack variable if not yet done */
5921 if ( ! consdata->slacktypechecked )
5922 {
5923 consdata->slacktypechecked = TRUE;
5924 /* check if slack variable can be made implicit integer. */
5925 if ( SCIPvarGetType(consdata->slackvar) == SCIP_VARTYPE_CONTINUOUS )
5926 {
5927 SCIP_Real* vals;
5928 SCIP_VAR** vars;
5929 SCIP_VAR* slackvar;
5930 SCIP_Bool foundslackvar = FALSE;
5931 int nvars;
5932 int j;
5933
5934 assert( consdata->lincons != NULL );
5935 vars = SCIPgetVarsLinear(scip, consdata->lincons);
5936 vals = SCIPgetValsLinear(scip, consdata->lincons);
5937 nvars = SCIPgetNVarsLinear(scip, consdata->lincons);
5938 slackvar = consdata->slackvar;
5939 assert( slackvar != NULL );
5940
5941 for (j = 0; j < nvars; ++j)
5942 {
5943 if ( vars[j] == slackvar )
5944 foundslackvar = TRUE;
5945 else
5946 {
5947 if ( ! SCIPvarIsIntegral(vars[j]) || ! SCIPisIntegral(scip, vals[j]))
5948 break;
5949 }
5950 }
5951 /* something is strange if the slack variable does not appear in the linear constraint (possibly because it is an artificial constraint) */
5952 if ( j == nvars && foundslackvar )
5953 {
5954 SCIP_Bool infeasible;
5955 SCIP_Real lb;
5956 SCIP_Real ub;
5957
5958 lb = SCIPvarGetLbGlobal(consdata->slackvar);
5959 ub = SCIPvarGetUbGlobal(consdata->slackvar);
5960 if ( (SCIPisInfinity(scip, -lb) || SCIPisIntegral(scip, lb)) && (SCIPisInfinity(scip, ub) || SCIPisIntegral(scip, ub)) )
5961 {
5962 SCIP_CALL( SCIPchgVarType(scip, consdata->slackvar, SCIP_VARTYPE_IMPLINT, &infeasible) );
5963 /* don't assert feasibility here because the presolver should detect infeasibility */
5964 }
5965 else
5966 {
5967 /* It can happen that the bounds of the slack variable have been changed to be non-integral in
5968 * previous presolving steps. We then might get a problem with tightening the bounds. In this case,
5969 * we just leave the slack variable to be continuous. */
5970 SCIPdebugMsg(scip, "Cannot change type of slack variable (<%s>) to IMPLINT, since global bound is non-integral: (%g, %g).\n",
5971 SCIPvarGetName(consdata->slackvar), SCIPvarGetLbGlobal(consdata->slackvar), SCIPvarGetUbGlobal(consdata->slackvar));
5972 }
5973 }
5974 }
5975 }
5976
5977 /* perform one presolving round */
5978 SCIP_CALL( presolRoundIndicator(scip, conshdlrdata, cons, consdata,
5979 conshdlrdata->dualreductions && SCIPallowStrongDualReds(scip), &cutoff, &success, ndelconss, nfixedvars) );
5980
5981 if ( cutoff )
5982 {
5983 *result = SCIP_CUTOFF;
5984 return SCIP_OKAY;
5985 }
5986 if ( success )
5987 *result = SCIP_SUCCESS;
5988 }
5989 }
5990
5991 /* determine whether other methods have found reductions */
5992 noReductions = nnewfixedvars == 0 && nnewaggrvars == 0 && nnewchgvartypes == 0 && nnewchgbds == 0
5993 && nnewdelconss == 0 && nnewchgcoefs == 0 && nnewchgsides == 0;
5994
5995 /* add variable upper bounds after bounds are likely to be strengthened */
5996 if ( noReductions && *result != SCIP_SUCCESS && conshdlrdata->addcouplingcons && ! conshdlrdata->addedcouplingcons )
5997 {
5998 int ngen;
5999
6000 /* create variable upper bounds, possibly removing indicator constraints */
6001 SCIP_CALL( createVarUbs(scip, conshdlrdata, conss, nconss, &ngen) );
6002
6003 if ( ngen > 0 )
6004 {
6005 *result = SCIP_SUCCESS;
6006 *nupgdconss += ngen;
6007 if ( conshdlrdata->removeindicators )
6008 *ndelconss += ngen;
6009 }
6010 conshdlrdata->addedcouplingcons = TRUE;
6011 }
6012
6013 SCIPdebug( SCIPdebugMsg(scip, "Presolved %d constraints (fixed %d variables, removed 0 variables, and deleted %d constraints).\n",
6014 nconss, *nfixedvars - oldnfixedvars, *ndelconss - oldndelconss); )
6015
6016 return SCIP_OKAY; /*lint !e438*/
6017 }
6018
6019
6020 /** LP initialization method of constraint handler (called before the initial LP relaxation at a node is solved)
6021 *
6022 * For an indicator constraint with binary variable \f$y\f$ and slack variable \f$s\f$ the coupling
6023 * inequality \f$s \le M (1-y)\f$ (equivalently: \f$s + M y \le M\f$) is inserted, where \f$M\f$ is
6024 * an upper bound on the value of \f$s\f$. If \f$M\f$ is too large the inequality is not inserted.
6025 */
6026 static
6027 SCIP_DECL_CONSINITLP(consInitlpIndicator)
6028 {
6029 int c;
6030 SCIP_CONSHDLRDATA* conshdlrdata;
6031
6032 assert( scip != NULL );
6033 assert( conshdlr != NULL );
6034 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
6035
6036 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6037 assert( conshdlrdata != NULL );
6038
6039 *infeasible = FALSE;
6040
6041 /* check whether coupling constraints should be added */
6042 if ( ! conshdlrdata->addcoupling )
6043 return SCIP_OKAY;
6044
6045 /* check whether coupling constraints have been added already */
6046 if ( conshdlrdata->addcouplingcons && conshdlrdata->addedcouplingcons )
6047 return SCIP_OKAY;
6048
6049 SCIPdebugMsg(scip, "Handle initial rows for %d indicator constraints.\n", nconss);
6050
6051 /* check each constraint */
6052 for (c = 0; c < nconss && !(*infeasible); ++c)
6053 {
6054 SCIP_CONSDATA* consdata;
6055 SCIP_Real ub;
6056
6057 assert( conss != NULL );
6058 assert( conss[c] != NULL );
6059 consdata = SCIPconsGetData(conss[c]);
6060 assert( consdata != NULL );
6061
6062 /* do not add inequalities if there are no linear constraints (no slack variable available) */
6063 if ( ! consdata->linconsactive )
6064 continue;
6065
6066 /* get upper bound for slack variable in linear constraint */
6067 ub = SCIPvarGetUbGlobal(consdata->slackvar);
6068 assert( ! SCIPisNegative(scip, ub) );
6069
6070 /* insert corresponding row if helpful and coefficient is not too large */
6071 if ( ub <= conshdlrdata->maxcouplingvalue )
6072 {
6073 char name[50];
6074
6075 #ifndef NDEBUG
6076 (void) SCIPsnprintf(name, 50, "couple%d", c);
6077 #else
6078 name[0] = '\0';
6079 #endif
6080
6081 /* add variable upper bound if required */
6082 if ( conshdlrdata->addcouplingcons )
6083 {
6084 SCIP_CONS* cons;
6085
6086 assert( ! conshdlrdata->addedcouplingcons );
6087
6088 SCIPdebugMsg(scip, "Insert coupling varbound constraint for indicator constraint <%s> (coeff: %f).\n", SCIPconsGetName(conss[c]), ub);
6089
6090 SCIP_CALL( SCIPcreateConsVarbound(scip, &cons, name, consdata->slackvar, consdata->binvar, ub, -SCIPinfinity(scip), ub,
6091 TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE) );
6092
6093 SCIP_CALL( SCIPaddCons(scip, cons) );
6094 SCIP_CALL( SCIPreleaseCons(scip, &cons) );
6095 }
6096 else
6097 {
6098 SCIP_ROW* row;
6099
6100 SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, conss[c], name, -SCIPinfinity(scip), ub, FALSE, FALSE, FALSE) );
6101 SCIP_CALL( SCIPcacheRowExtensions(scip, row) );
6102
6103 SCIP_CALL( SCIPaddVarToRow(scip, row, consdata->slackvar, 1.0) );
6104 SCIP_CALL( SCIPaddVarToRow(scip, row, consdata->binvar, ub) );
6105 SCIP_CALL( SCIPflushRowExtensions(scip, row) );
6106
6107 SCIPdebugMsg(scip, "Insert coupling inequality for indicator constraint <%s> (coeff: %f).\n", SCIPconsGetName(conss[c]), ub);
6108 #ifdef SCIP_OUTPUT
6109 SCIP_CALL( SCIPprintRow(scip, row, NULL) );
6110 #endif
6111 SCIP_CALL( SCIPaddRow(scip, row, FALSE, infeasible) );
6112 SCIP_CALL( SCIPreleaseRow(scip, &row));
6113 }
6114 }
6115 }
6116
6117 return SCIP_OKAY;
6118 }
6119
6120
6121 /** separation method of constraint handler for LP solutions */
6122 static
6123 SCIP_DECL_CONSSEPALP(consSepalpIndicator)
6124 { /*lint --e{715}*/
6125 assert( scip != NULL );
6126 assert( conshdlr != NULL );
6127 assert( conss != NULL );
6128 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
6129 assert( result != NULL );
6130
6131 /* perform separation */
6132 SCIP_CALL( separateIndicators(scip, conshdlr, nconss, nusefulconss, conss, NULL, SCIP_TYPE_SEPALP, result) );
6133
6134 return SCIP_OKAY;
6135 }
6136
6137
6138 /** separation method of constraint handler for arbitrary primal solutions */
6139 static
6140 SCIP_DECL_CONSSEPASOL(consSepasolIndicator)
6141 { /*lint --e{715}*/
6142 assert( scip != NULL );
6143 assert( conshdlr != NULL );
6144 assert( conss != NULL );
6145 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
6146 assert( result != NULL );
6147
6148 /* perform separation */
6149 SCIP_CALL( separateIndicators(scip, conshdlr, nconss, nusefulconss, conss, sol, SCIP_TYPE_SEPASOL, result) );
6150
6151 return SCIP_OKAY;
6152 }
6153
6154
6155 /** constraint enforcing method of constraint handler for LP solutions */
6156 static
6157 SCIP_DECL_CONSENFOLP(consEnfolpIndicator)
6158 { /*lint --e{715}*/
6159 SCIP_CONSHDLRDATA* conshdlrdata;
6160
6161 assert( scip != NULL );
6162 assert( conshdlr != NULL );
6163 assert( conss != NULL );
6164 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
6165 assert( result != NULL );
6166
6167 if ( solinfeasible )
6168 {
6169 *result = SCIP_FEASIBLE;
6170 return SCIP_OKAY;
6171 }
6172
6173 /* get constraint handler data */
6174 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6175 assert( conshdlrdata != NULL );
6176
6177 SCIP_CALL( enforceIndicators(scip, conshdlr, nconss, conss, NULL, SCIP_TYPE_ENFOLP, conshdlrdata->genlogicor, result) );
6178
6179 return SCIP_OKAY;
6180 }
6181
6182
6183 /** constraint enforcing method of constraint handler for relaxation solutions */
6184 static
6185 SCIP_DECL_CONSENFORELAX(consEnforelaxIndicator)
6186 { /*lint --e{715}*/
6187 SCIP_CONSHDLRDATA* conshdlrdata;
6188
6189 assert( scip != NULL );
6190 assert( conshdlr != NULL );
6191 assert( conss != NULL );
6192 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
6193 assert( result != NULL );
6194
6195 if ( solinfeasible )
6196 {
6197 *result = SCIP_FEASIBLE;
6198 return SCIP_OKAY;
6199 }
6200
6201 /* get constraint handler data */
6202 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6203 assert( conshdlrdata != NULL );
6204
6205 SCIP_CALL( enforceIndicators(scip, conshdlr, nconss, conss, sol, SCIP_TYPE_ENFORELAX, conshdlrdata->genlogicor, result) );
6206
6207 return SCIP_OKAY;
6208 }
6209
6210
6211 /** constraint enforcing method of constraint handler for pseudo solutions */
6212 static
6213 SCIP_DECL_CONSENFOPS(consEnfopsIndicator)
6214 { /*lint --e{715}*/
6215 assert( scip != NULL );
6216 assert( conshdlr != NULL );
6217 assert( conss != NULL );
6218 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
6219 assert( result != NULL );
6220
6221 if ( solinfeasible )
6222 {
6223 *result = SCIP_FEASIBLE;
6224 return SCIP_OKAY;
6225 }
6226
6227 if ( objinfeasible )
6228 {
6229 *result = SCIP_DIDNOTRUN;
6230 return SCIP_OKAY;
6231 }
6232
6233 SCIP_CALL( enforceIndicators(scip, conshdlr, nconss, conss, NULL, SCIP_TYPE_ENFOPS, TRUE, result) );
6234
6235 return SCIP_OKAY;
6236 }
6237
6238
6239 /** feasibility check method of constraint handler for integral solutions */
6240 static
6241 SCIP_DECL_CONSCHECK(consCheckIndicator)
6242 { /*lint --e{715}*/
6243 SCIP_SOL* trysol = NULL;
6244 SCIP_CONSHDLRDATA* conshdlrdata;
6245 SCIP_Bool someLinconsNotActive;
6246 SCIP_Bool changedSol;
6247 int c;
6248
6249 assert( scip != NULL );
6250 assert( conshdlr != NULL );
6251 assert( conss != NULL );
6252 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
6253 assert( result != NULL );
6254
6255 SCIPdebugMsg(scip, "Checking %d indicator constraints <%s>.\n", nconss, SCIPconshdlrGetName(conshdlr) );
6256
6257 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6258 assert( conshdlrdata != NULL );
6259
6260 /* try to repair solution below, if it makes sense (will send solution to trysol heuristic in any case (see below) */
6261 if ( SCIPgetStage(scip) > SCIP_STAGE_PROBLEM && SCIPgetStage(scip) < SCIP_STAGE_SOLVED && conshdlrdata->trysolutions && conshdlrdata->heurtrysol != NULL )
6262 {
6263 SCIP_CALL( SCIPcreateSolCopy(scip, &trysol, sol) );
6264 assert( trysol != NULL );
6265 }
6266
6267 /* check each constraint */
6268 *result = SCIP_FEASIBLE;
6269 changedSol = FALSE;
6270 someLinconsNotActive = FALSE;
6271 for (c = 0; c < nconss; ++c)
6272 {
6273 SCIP_CONSDATA* consdata;
6274
6275 assert( conss[c] != NULL );
6276 consdata = SCIPconsGetData(conss[c]);
6277 assert( consdata != NULL );
6278 assert( consdata->binvar != NULL );
6279
6280 /* if the linear constraint has not been generated, we do nothing */
6281 if ( ! consdata->linconsactive )
6282 {
6283 someLinconsNotActive = TRUE;
6284 continue;
6285 }
6286
6287 assert( consdata->slackvar != NULL );
6288 /* if printreason is true it can happen that non-integral solutions are checked */
6289 assert( checkintegrality || SCIPisFeasIntegral(scip, SCIPgetSolVal(scip, sol, consdata->binvar)) );
6290
6291 /* if constraint is infeasible */
6292 if ( ! SCIPisFeasZero(scip, SCIPgetSolVal(scip, sol, consdata->binvar)) &&
6293 ! SCIPisFeasZero(scip, SCIPgetSolVal(scip, sol, consdata->slackvar)) )
6294 {
6295 SCIP_Real absviol = REALABS(SCIPgetSolVal(scip, sol, consdata->slackvar));
6296 SCIP_Real relviol = SCIPrelDiff(absviol, 0.0);
6297 if( sol != NULL )
6298 SCIPupdateSolConsViolation(scip, sol, absviol, relviol);
6299
6300 SCIP_CALL( SCIPresetConsAge(scip, conss[c]) );
6301 *result = SCIP_INFEASIBLE;
6302
6303 if ( printreason )
6304 {
6305 SCIP_CALL( SCIPprintCons(scip, conss[c], NULL) );
6306 SCIPinfoMessage(scip, NULL, ";\nviolation: <%s> = %g and <%s> = %.15g\n",
6307 SCIPvarGetName(consdata->binvar), SCIPgetSolVal(scip, sol, consdata->binvar),
6308 SCIPvarGetName(consdata->slackvar), SCIPgetSolVal(scip, sol, consdata->slackvar));
6309 }
6310
6311 /* try to make solution feasible if it makes sense - otherwise exit */
6312 if ( trysol != NULL )
6313 {
6314 SCIP_Bool changed;
6315 SCIP_CALL( SCIPmakeIndicatorFeasible(scip, conss[c], trysol, &changed) );
6316 changedSol = changedSol || changed;
6317 }
6318 else
6319 {
6320 SCIPdebugMsg(scip, "Indicator constraint %s is not feasible.\n", SCIPconsGetName(conss[c]));
6321
6322 if( !completely )
6323 return SCIP_OKAY;
6324 }
6325 }
6326 else
6327 {
6328 if ( trysol != NULL )
6329 {
6330 SCIP_Bool changed;
6331 SCIP_CALL( SCIPmakeIndicatorFeasible(scip, conss[c], trysol, &changed) );
6332 changedSol = changedSol || changed;
6333 }
6334 }
6335 }
6336
6337 /* if some linear constraints are not active, we need to check feasibility via the alternative polyhedron */
6338 if ( someLinconsNotActive )
6339 {
6340 SCIP_LPI* lp;
6341 SCIP_Bool infeasible;
6342 SCIP_Bool error;
6343 SCIP_Bool* S;
6344
6345 lp = conshdlrdata->altlp;
6346 assert( conshdlrdata->sepaalternativelp );
6347
6348 /* the check maybe called before we have build the alternative polyhedron -> return SCIP_INFEASIBLE */
6349 if ( lp != NULL )
6350 {
6351 #ifndef NDEBUG
6352 SCIP_CALL( checkLPBoundsClean(scip, lp, nconss, conss) );
6353 #endif
6354
6355 /* change coefficients of bounds in alternative LP */
6356 if ( conshdlrdata->updatebounds )
6357 {
6358 SCIP_CALL( updateFirstRowGlobal(scip, conshdlrdata) );
6359 }
6360
6361 /* scale first row if necessary */
6362 SCIP_CALL( scaleFirstRow(scip, conshdlrdata) );
6363
6364 /* set objective function to current solution */
6365 SCIP_CALL( setAltLPObjZero(scip, lp, nconss, conss) );
6366
6367 SCIP_CALL( SCIPallocBufferArray(scip, &S, nconss) );
6368
6369 /* set up variables fixed to 1 */
6370 for (c = 0; c < nconss; ++c)
6371 {
6372 SCIP_CONSDATA* consdata;
6373
6374 assert( conss[c] != NULL );
6375 consdata = SCIPconsGetData(conss[c]);
6376 assert( consdata != NULL );
6377
6378 /* if printreason is true it can happen that non-integral solutions are checked */
6379 assert( checkintegrality || SCIPisFeasIntegral(scip, SCIPgetSolVal(scip, sol, consdata->binvar)) );
6380 if ( SCIPisFeasZero(scip, SCIPgetSolVal(scip, sol, consdata->binvar)) )
6381 S[c] = TRUE;
6382 else
6383 S[c] = FALSE;
6384 }
6385
6386 /* fix the variables in S */
6387 SCIP_CALL( fixAltLPVariables(scip, lp, nconss, conss, S) );
6388
6389 /* check feasibility */
6390 SCIP_CALL_PARAM( SCIPlpiSetIntpar(lp, SCIP_LPPAR_FROMSCRATCH, TRUE) );
6391 SCIP_CALL( checkAltLPInfeasible(scip, lp, conshdlrdata->maxconditionaltlp, TRUE, &infeasible, &error) );
6392 SCIP_CALL_PARAM( SCIPlpiSetIntpar(lp, SCIP_LPPAR_FROMSCRATCH, FALSE) );
6393
6394 if ( error )
6395 return SCIP_LPERROR;
6396
6397 if ( ! infeasible )
6398 *result = SCIP_INFEASIBLE;
6399
6400 /* reset bounds */
6401 SCIP_CALL( unfixAltLPVariables(scip, lp, nconss, conss, S) );
6402
6403 #ifndef NDEBUG
6404 SCIP_CALL( checkLPBoundsClean(scip, lp, nconss, conss) );
6405 #endif
6406
6407 SCIPfreeBufferArray(scip, &S);
6408 }
6409 else
6410 *result = SCIP_INFEASIBLE;
6411 }
6412 else
6413 {
6414 /* tell heur_trysol about solution - it will pass it to SCIP */
6415 if ( trysol != NULL && changedSol )
6416 {
6417 assert( conshdlrdata->heurtrysol != NULL );
6418 SCIP_CALL( SCIPheurPassSolTrySol(scip, conshdlrdata->heurtrysol, trysol) );
6419 }
6420 }
6421
6422 if ( trysol != NULL )
6423 SCIP_CALL( SCIPfreeSol(scip, &trysol) );
6424
6425 if ( *result == SCIP_INFEASIBLE )
6426 {
6427 SCIPdebugMsg(scip, "Indicator constraints are not feasible.\n");
6428 return SCIP_OKAY;
6429 }
6430
6431 /* at this point we are feasible */
6432 SCIPdebugMsg(scip, "Indicator constraints are feasible.\n");
6433
6434 return SCIP_OKAY;
6435 }
6436
6437
6438 /** domain propagation method of constraint handler */
6439 static
6440 SCIP_DECL_CONSPROP(consPropIndicator)
6441 { /*lint --e{715}*/
6442 SCIP_CONSHDLRDATA* conshdlrdata;
6443 int ngen;
6444 int c;
6445
6446 assert( scip != NULL );
6447 assert( conshdlr != NULL );
6448 assert( conss != NULL );
6449 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
6450 assert( result != NULL );
6451 *result = SCIP_DIDNOTRUN;
6452
6453 assert( SCIPisTransformed(scip) );
6454
6455 SCIPdebugMsg(scip, "Start propagation of constraint handler <%s>.\n", SCIPconshdlrGetName(conshdlr));
6456 ngen = 0;
6457
6458 /* get constraint handler data */
6459 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6460 assert( conshdlrdata != NULL );
6461
6462 /* check each constraint */
6463 for (c = 0; c < nconss; ++c)
6464 {
6465 SCIP_CONS* cons;
6466 SCIP_CONSDATA* consdata;
6467 SCIP_Bool cutoff;
6468 int cnt;
6469
6470 *result = SCIP_DIDNOTFIND;
6471 assert( conss[c] != NULL );
6472 cons = conss[c];
6473 consdata = SCIPconsGetData(cons);
6474 assert( consdata != NULL );
6475
6476 #ifdef SCIP_MORE_DEBUG
6477 SCIPdebugMsg(scip, "Propagating indicator constraint <%s>.\n", SCIPconsGetName(cons) );
6478 #endif
6479
6480 *result = SCIP_DIDNOTFIND;
6481
6482 SCIP_CALL( propIndicator(scip, cons, consdata, conshdlrdata->dualreductions && SCIPallowStrongDualReds(scip),
6483 conshdlrdata->addopposite, &cutoff, &cnt) );
6484
6485 if ( cutoff )
6486 {
6487 *result = SCIP_CUTOFF;
6488 return SCIP_OKAY;
6489 }
6490 ngen += cnt;
6491 }
6492 SCIPdebugMsg(scip, "Propagated %d domains in constraint handler <%s>.\n", ngen, SCIPconshdlrGetName(conshdlr));
6493 if ( ngen > 0 )
6494 *result = SCIP_REDUCEDDOM;
6495
6496 return SCIP_OKAY;
6497 }
6498
6499
6500 /** propagation conflict resolving method of constraint handler
6501 *
6502 * We check which bound changes were the reason for infeasibility. We use that @a inferinfo is 0 if
6503 * the binary variable has bounds that fix it to be nonzero (these bounds are the reason). Likewise
6504 * @a inferinfo is 1 if the slack variable has bounds that fix it to be nonzero.
6505 */
6506 static
6507 SCIP_DECL_CONSRESPROP(consRespropIndicator)
6508 { /*lint --e{715}*/
6509 SCIP_CONSDATA* consdata;
6510
6511 assert( scip != NULL );
6512 assert( cons != NULL );
6513 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
6514 assert( infervar != NULL );
6515 assert( bdchgidx != NULL );
6516 assert( result != NULL );
6517
6518 *result = SCIP_DIDNOTFIND;
6519 SCIPdebugMsg(scip, "Propagation resolution method of indicator constraint <%s>.\n", SCIPconsGetName(cons));
6520
6521 consdata = SCIPconsGetData(cons);
6522 assert( consdata != NULL );
6523 assert( inferinfo == 0 || inferinfo == 1 || inferinfo == 2 );
6524 assert( consdata->linconsactive );
6525
6526 /* if the binary variable was the reason */
6527 if ( inferinfo == 0 )
6528 {
6529 assert( SCIPgetVarLbAtIndex(scip, consdata->binvar, bdchgidx, FALSE) > 0.5 );
6530 assert( infervar != consdata->binvar );
6531
6532 SCIP_CALL( SCIPaddConflictLb(scip, consdata->binvar, bdchgidx) );
6533 }
6534 else if ( inferinfo == 1 )
6535 {
6536 /* if the slack variable fixed to a positive value was the reason */
6537 assert( infervar != consdata->slackvar );
6538 /* Use a weaker comparison to SCIPvarGetLbAtIndex here (i.e., SCIPisPositive instead of SCIPisFeasPositive),
6539 * because SCIPvarGetLbAtIndex might differ from the local bound at time bdchgidx by epsilon. */
6540 assert( SCIPisPositive(scip, SCIPgetVarLbAtIndex(scip, consdata->slackvar, bdchgidx, FALSE)) );
6541 SCIP_CALL( SCIPaddConflictLb(scip, consdata->slackvar, bdchgidx) );
6542 }
6543 else
6544 {
6545 assert( inferinfo == 2 );
6546 assert( SCIPisFeasZero(scip, SCIPgetVarUbAtIndex(scip, consdata->slackvar, bdchgidx, FALSE)) );
6547 assert( SCIPconshdlrGetData(conshdlr)->dualreductions && SCIPallowStrongDualReds(scip) && SCIPallowWeakDualReds(scip) );
6548 SCIP_CALL( SCIPaddConflictUb(scip, consdata->slackvar, bdchgidx) );
6549 }
6550 *result = SCIP_SUCCESS;
6551
6552 return SCIP_OKAY;
6553 }
6554
6555
6556 /** variable rounding lock method of constraint handler
6557 *
6558 * The up-rounding of the binary and slack variable may violate the constraint. If the linear
6559 * constraint is not active, we lock all variables in the depending constraint - otherwise they
6560 * will be fixed by dual presolving methods.
6561 */
6562 static
6563 SCIP_DECL_CONSLOCK(consLockIndicator)
6564 {
6565 SCIP_CONSDATA* consdata;
6566
6567 assert( scip != NULL );
6568 assert( conshdlr != NULL );
6569 assert( cons != NULL );
6570 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
6571 consdata = SCIPconsGetData(cons);
6572 assert( consdata != NULL );
6573 assert( consdata->binvar != NULL );
6574
6575 #ifdef SCIP_MORE_DEBUG
6576 SCIPdebugMsg(scip, "%socking constraint <%s>.\n", (nlocksneg < 0) || (nlockspos < 0) ? "Unl" : "L", SCIPconsGetName(cons));
6577 #endif
6578
6579 SCIP_CALL( SCIPaddVarLocksType(scip, consdata->binvar, locktype, nlocksneg, nlockspos) );
6580
6581 if ( consdata->linconsactive )
6582 {
6583 assert( consdata->slackvar != NULL );
6584
6585 SCIP_CALL( SCIPaddVarLocksType(scip, consdata->slackvar, locktype, nlocksneg, nlockspos) );
6586 }
6587 else
6588 {
6589 SCIP_VAR** linvars;
6590 SCIP_Real* linvals;
6591 SCIP_Bool haslhs;
6592 SCIP_Bool hasrhs;
6593 int nlinvars;
6594 int j;
6595
6596 assert( consdata->lincons != NULL );
6597 assert( consdata->slackvar == NULL );
6598
6599 nlinvars = SCIPgetNVarsLinear(scip, consdata->lincons);
6600 linvars = SCIPgetVarsLinear(scip, consdata->lincons);
6601 linvals = SCIPgetValsLinear(scip, consdata->lincons);
6602 haslhs = ! SCIPisInfinity(scip, REALABS(SCIPgetLhsLinear(scip, consdata->lincons)));
6603 hasrhs = ! SCIPisInfinity(scip, REALABS(SCIPgetRhsLinear(scip, consdata->lincons)));
6604
6605 for (j = 0; j < nlinvars; ++j)
6606 {
6607 assert( ! SCIPisZero(scip, linvals[j]) );
6608 if ( SCIPisPositive(scip, linvals[j]) )
6609 {
6610 if ( haslhs )
6611 {
6612 SCIP_CALL( SCIPaddVarLocksType(scip, linvars[j], locktype, nlockspos, nlocksneg) );
6613 }
6614 if ( hasrhs )
6615 {
6616 SCIP_CALL( SCIPaddVarLocksType(scip, linvars[j], locktype, nlocksneg, nlockspos) );
6617 }
6618 }
6619 else
6620 {
6621 if ( haslhs )
6622 {
6623 SCIP_CALL( SCIPaddVarLocksType(scip, linvars[j], locktype, nlocksneg, nlockspos) );
6624 }
6625 if ( hasrhs )
6626 {
6627 SCIP_CALL( SCIPaddVarLocksType(scip, linvars[j], locktype, nlockspos, nlocksneg) );
6628 }
6629 }
6630 }
6631 }
6632
6633 return SCIP_OKAY;
6634 }
6635
6636
6637 /** constraint display method of constraint handler */
6638 static
6639 SCIP_DECL_CONSPRINT(consPrintIndicator)
6640 {
6641 SCIP_CONSDATA* consdata;
6642 SCIP_VAR* binvar;
6643 int rhs;
6644
6645 assert( scip != NULL );
6646 assert( conshdlr != NULL );
6647 assert( cons != NULL );
6648 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
6649
6650 consdata = SCIPconsGetData(cons);
6651 assert( consdata != NULL );
6652 assert( consdata->binvar != NULL );
6653
6654 binvar = consdata->binvar;
6655 rhs = 1;
6656 if ( SCIPvarGetStatus(binvar) == SCIP_VARSTATUS_NEGATED )
6657 {
6658 rhs = 0;
6659 binvar = SCIPvarGetNegatedVar(binvar);
6660 }
6661 SCIPinfoMessage(scip, file, "<%s> = %d", SCIPvarGetName(binvar), rhs);
6662
6663 assert( consdata->slackvar != NULL );
6664 assert( consdata->lincons != NULL );
6665 SCIPinfoMessage(scip, file, " -> <%s> = 0", SCIPvarGetName(consdata->slackvar));
6666 SCIPinfoMessage(scip, file, " (<%s>)", SCIPconsGetName(consdata->lincons));
6667
6668 return SCIP_OKAY;
6669 }
6670
6671
6672 /** constraint copying method of constraint handler */
6673 static
6674 SCIP_DECL_CONSCOPY(consCopyIndicator)
6675 { /*lint --e{715}*/
6676 SCIP_CONSDATA* sourceconsdata;
6677 SCIP_CONS* targetlincons = NULL;
6678 SCIP_VAR* targetbinvar = NULL;
6679 SCIP_VAR* targetslackvar = NULL;
6680 SCIP_CONS* sourcelincons;
6681 SCIP_CONSHDLR* conshdlrlinear;
6682 const char* consname;
6683
6684 assert( scip != NULL );
6685 assert( sourcescip != NULL );
6686 assert( sourcecons != NULL );
6687 assert( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(sourcecons)), CONSHDLR_NAME) == 0 );
6688
6689 *valid = TRUE;
6690
6691 if ( name != NULL )
6692 consname = name;
6693 else
6694 consname = SCIPconsGetName(sourcecons);
6695
6696 #ifdef SCIP_MORE_DEBUG
6697 SCIPdebugMsg(scip, "Copying indicator constraint <%s> ...\n", consname);
6698 #endif
6699
6700 if ( modifiable )
6701 {
6702 SCIPwarningMessage(scip, "cannot create modifiable indicator constraint when trying to copy constraint <%s>,\n", SCIPconsGetName(sourcecons));
6703 *valid = FALSE;
6704 return SCIP_OKAY;
6705 }
6706
6707 sourceconsdata = SCIPconsGetData(sourcecons);
6708 assert( sourceconsdata != NULL );
6709
6710 /* get linear constraint */
6711 sourcelincons = sourceconsdata->lincons;
6712
6713 /* if the constraint has been deleted -> create empty constraint (multi-aggregation might still contain slack variable, so indicator is valid) */
6714 if ( SCIPconsIsDeleted(sourcelincons) )
6715 {
6716 SCIPdebugMsg(scip, "Linear constraint <%s> deleted! Create empty linear constraint.\n", SCIPconsGetName(sourceconsdata->lincons));
6717
6718 SCIP_CALL( SCIPcreateConsLinear(scip, &targetlincons, "dummy", 0, NULL, NULL, 0.0, SCIPinfinity(scip),
6719 FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE) );
6720 SCIP_CALL( SCIPaddCons(scip, targetlincons) );
6721 }
6722 else
6723 {
6724 /* get copied version of linear constraint */
6725 assert( sourcelincons != NULL );
6726 conshdlrlinear = SCIPfindConshdlr(sourcescip, "linear");
6727 assert( conshdlrlinear != NULL );
6728
6729 /* if copying scip after transforming the original instance before presolving, we need to correct the linear
6730 * constraint pointer */
6731 if ( SCIPisTransformed(sourcescip) && ! SCIPconsIsTransformed(sourcelincons) )
6732 {
6733 SCIP_CONS* translincons;
6734
6735 /* adjust the linear constraint in the original constraint (no need to release translincons) */
6736 SCIP_CALL( SCIPgetTransformedCons(sourcescip, sourcelincons, &translincons) );
6737 assert( translincons != NULL );
6738 SCIP_CALL( SCIPreleaseCons(sourcescip, &sourceconsdata->lincons) );
6739 SCIP_CALL( SCIPcaptureCons(sourcescip, translincons) );
6740 sourceconsdata->lincons = translincons;
6741 sourcelincons = translincons;
6742 }
6743
6744 SCIP_CALL( SCIPgetConsCopy(sourcescip, scip, sourcelincons, &targetlincons, conshdlrlinear, varmap, consmap, SCIPconsGetName(sourcelincons),
6745 SCIPconsIsInitial(sourcelincons), SCIPconsIsSeparated(sourcelincons), SCIPconsIsEnforced(sourcelincons), SCIPconsIsChecked(sourcelincons),
6746 SCIPconsIsPropagated(sourcelincons), SCIPconsIsLocal(sourcelincons), SCIPconsIsModifiable(sourcelincons), SCIPconsIsDynamic(sourcelincons),
6747 SCIPconsIsRemovable(sourcelincons), SCIPconsIsStickingAtNode(sourcelincons), global, valid) );
6748 }
6749
6750 /* find copied variable corresponding to binvar */
6751 if ( *valid )
6752 {
6753 SCIP_VAR* sourcebinvar;
6754
6755 sourcebinvar = sourceconsdata->binvar;
6756 assert( sourcebinvar != NULL );
6757
6758 SCIP_CALL( SCIPgetVarCopy(sourcescip, scip, sourcebinvar, &targetbinvar, varmap, consmap, global, valid) );
6759 }
6760
6761 /* find copied variable corresponding to slackvar */
6762 if ( *valid )
6763 {
6764 SCIP_VAR* sourceslackvar;
6765
6766 sourceslackvar = sourceconsdata->slackvar;
6767 assert( sourceslackvar != NULL );
6768
6769 SCIP_CALL( SCIPgetVarCopy(sourcescip, scip, sourceslackvar, &targetslackvar, varmap, consmap, global, valid) );
6770 }
6771
6772 /* create indicator constraint */
6773 if ( *valid )
6774 {
6775 assert( targetlincons != NULL );
6776 assert( targetbinvar != NULL );
6777 assert( targetslackvar != NULL );
6778
6779 /* creates indicator constraint (and captures the linear constraint) */
6780 /* Note that the copied constraint has activeone = TRUE, since the target binary variable already was negated if needed. */
6781 SCIP_CALL( SCIPcreateConsIndicatorGenericLinCons(scip, cons, consname, targetbinvar, targetlincons, targetslackvar, TRUE,
6782 initial, separate, enforce, check, propagate, local, dynamic, removable, stickingatnode) );
6783 }
6784 else
6785 {
6786 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "could not copy linear constraint <%s>\n", SCIPconsGetName(sourcelincons));
6787 }
6788
6789 /* release copied linear constraint */
6790 if ( targetlincons != NULL )
6791 {
6792 SCIP_CALL( SCIPreleaseCons(scip, &targetlincons) );
6793 }
6794
6795 return SCIP_OKAY;
6796 }
6797
6798
6799 /** constraint parsing method of constraint handler */
6800 static
6801 SCIP_DECL_CONSPARSE(consParseIndicator)
6802 { /*lint --e{715}*/
6803 char binvarname[1024];
6804 char slackvarname[1024];
6805 char linconsname[1024];
6806 SCIP_VAR* binvar;
6807 SCIP_VAR* slackvar;
6808 SCIP_CONS* lincons;
6809 int zeroone;
6810 int nargs;
6811
6812 *success = TRUE;
6813
6814 /* read indicator constraint */
6815 /* coverity[secure_coding] */
6816 nargs = sscanf(str, " <%1023[^>]> = %d -> <%1023[^>]> = 0 (<%1023[^>]>)", binvarname, &zeroone, slackvarname, linconsname);
6817
6818 /* downward compatible: accept missing linear constraint at end */
6819 if ( nargs != 3 && nargs != 4 )
6820 {
6821 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "Syntax error: expected the following form: <var> = [0|1] -> <var> = 0 (<lincons>).\n%s\n", str);
6822 *success = FALSE;
6823 return SCIP_OKAY;
6824 }
6825
6826 if ( zeroone != 0 && zeroone != 1 )
6827 {
6828 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "Syntax error: expected the following form: <var> = [0|1] -> <var> = 0.\n%s\n", str);
6829 *success = FALSE;
6830 return SCIP_OKAY;
6831 }
6832
6833 /* get binary variable */
6834 binvar = SCIPfindVar(scip, binvarname);
6835 if ( binvar == NULL )
6836 {
6837 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "unknown variable <%s>\n", binvarname);
6838 *success = FALSE;
6839 return SCIP_OKAY;
6840 }
6841 /* check whether we need the complemented variable */
6842 if ( zeroone == 0 )
6843 SCIP_CALL( SCIPgetNegatedVar(scip, binvar, &binvar) );
6844
6845 /* get slack variable */
6846 slackvar = SCIPfindVar(scip, slackvarname);
6847 if ( slackvar == NULL )
6848 {
6849 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "unknown variable <%s>\n", slackvarname);
6850 *success = FALSE;
6851 return SCIP_OKAY;
6852 }
6853
6854 /* determine linear constraint */
6855 if ( nargs == 4 )
6856 {
6857 lincons = SCIPfindCons(scip, linconsname);
6858 if ( lincons == NULL )
6859 {
6860 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "unknown constraint <%s>\n", linconsname);
6861 *success = FALSE;
6862 return SCIP_OKAY;
6863 }
6864 if ( strncmp(SCIPconshdlrGetName(SCIPconsGetHdlr(lincons)), "linear", 6) != 0 )
6865 {
6866 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "constraint <%s> is not linear\n", linconsname);
6867 *success = FALSE;
6868 return SCIP_OKAY;
6869 }
6870 }
6871 else
6872 {
6873 const char* posstr;
6874
6875 /* for backward compability try to determine name of linear constraint from variables names */
6876 assert( nargs == 3 );
6877
6878 /* find matching linear constraint */
6879 posstr = strstr(slackvarname, "indslack");
6880 if ( posstr == NULL )
6881 {
6882 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "strange slack variable name: <%s>\n", slackvarname);
6883 *success = FALSE;
6884 return SCIP_OKAY;
6885 }
6886
6887 /* overwrite binvarname: set up name for linear constraint */
6888 (void) SCIPsnprintf(binvarname, 1023, "indlin%s", posstr+8);
6889
6890 lincons = SCIPfindCons(scip, binvarname);
6891 if ( lincons == NULL )
6892 {
6893 /* if not found - check without indlin */
6894 (void) SCIPsnprintf(binvarname, 1023, "%s", posstr+9);
6895 lincons = SCIPfindCons(scip, binvarname);
6896
6897 if ( lincons == NULL )
6898 {
6899 /* if not found - check without indrhs or indlhs */
6900 (void) SCIPsnprintf(binvarname, 1023, "%s", posstr+16);
6901 lincons = SCIPfindCons(scip, binvarname);
6902
6903 if( lincons == NULL )
6904 {
6905 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "while parsing indicator constraint <%s>: unknown linear constraint <indlin%s>, <%s> or <%s>.\n",
6906 name, posstr+8, posstr+9, posstr+16);
6907 *success = FALSE;
6908 return SCIP_OKAY;
6909 }
6910 }
6911 }
6912 }
6913 assert( lincons != NULL );
6914
6915 /* check correct linear constraint */
6916 if ( ! SCIPisInfinity(scip, -SCIPgetLhsLinear(scip, lincons)) && ! SCIPisInfinity(scip, SCIPgetRhsLinear(scip, lincons)) )
6917 {
6918 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "while parsing indicator constraint <%s>: linear constraint is ranged or equation.\n", name);
6919 *success = FALSE;
6920 return SCIP_OKAY;
6921 }
6922
6923 /* create indicator constraint */
6924 SCIP_CALL( SCIPcreateConsIndicatorLinCons(scip, cons, name, binvar, lincons, slackvar,
6925 initial, separate, enforce, check, propagate, local, dynamic, removable, stickingatnode) );
6926
6927 return SCIP_OKAY;
6928 }
6929
6930
6931 /** constraint enabling notification method of constraint handler */
6932 static
6933 SCIP_DECL_CONSENABLE(consEnableIndicator)
6934 {
6935 SCIP_CONSHDLRDATA* conshdlrdata;
6936 SCIP_CONSDATA* consdata;
6937
6938 assert( scip != NULL );
6939 assert( conshdlr != NULL );
6940 assert( cons != NULL );
6941 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
6942
6943 #ifdef SCIP_MORE_DEBUG
6944 SCIPdebugMsg(scip, "Enabling constraint <%s>.\n", SCIPconsGetName(cons));
6945 #endif
6946
6947 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6948 assert( conshdlrdata != NULL );
6949
6950 consdata = SCIPconsGetData(cons);
6951 assert( consdata != NULL );
6952
6953 if ( conshdlrdata->altlp != NULL )
6954 {
6955 assert( conshdlrdata->sepaalternativelp );
6956
6957 if ( consdata->colindex >= 0 )
6958 {
6959 SCIP_CALL( unfixAltLPVariable(conshdlrdata->altlp, consdata->colindex) );
6960 }
6961 }
6962
6963 return SCIP_OKAY;
6964 }
6965
6966
6967 /** constraint disabling notification method of constraint handler */
6968 static
6969 SCIP_DECL_CONSDISABLE(consDisableIndicator)
6970 {
6971 SCIP_CONSHDLRDATA* conshdlrdata;
6972
6973 assert( scip != NULL );
6974 assert( conshdlr != NULL );
6975 assert( cons != NULL );
6976 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
6977
6978 #ifdef SCIP_MORE_DEBUG
6979 SCIPdebugMsg(scip, "Disabling constraint <%s>.\n", SCIPconsGetName(cons));
6980 #endif
6981
6982 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6983 assert( conshdlrdata != NULL );
6984
6985 if ( conshdlrdata->altlp != NULL )
6986 {
6987 SCIP_CONSDATA* consdata;
6988
6989 consdata = SCIPconsGetData(cons);
6990 assert( consdata != NULL );
6991 assert( conshdlrdata->sepaalternativelp );
6992
6993 if ( consdata->colindex >= 0 )
6994 {
6995 SCIP_CALL( fixAltLPVariable(conshdlrdata->altlp, consdata->colindex) );
6996 }
6997 }
6998
6999 return SCIP_OKAY;
7000 }
7001
7002
7003 /** constraint method of constraint handler which returns the variables (if possible) */
7004 static
7005 SCIP_DECL_CONSGETVARS(consGetVarsIndicator)
7006 { /*lint --e{715}*/
7007 SCIP_CONSDATA* consdata;
7008 int nvars = 0;
7009
7010 assert( scip != NULL );
7011 assert( cons != NULL );
7012 assert( vars != NULL );
7013 assert( success != NULL );
7014
7015 if ( varssize < 0 )
7016 return SCIP_INVALIDDATA;
7017 assert( varssize >= 0 );
7018
7019 (*success) = TRUE;
7020
7021 /* if indicator constraint is already deleted */
7022 if ( SCIPconsIsDeleted(cons) )
7023 return SCIP_OKAY;
7024
7025 consdata = SCIPconsGetData(cons);
7026 assert( consdata != NULL );
7027 assert( consdata->lincons != NULL );
7028
7029 if ( consdata->binvar != NULL )
7030 {
7031 assert( varssize > 0 );
7032 vars[nvars++] = consdata->binvar;
7033 }
7034 if ( consdata->slackvar != NULL )
7035 {
7036 assert( varssize > nvars );
7037 vars[nvars++] = consdata->slackvar;
7038 }
7039
7040 /* if linear constraint of indicator is already deleted */
7041 if ( SCIPconsIsDeleted(consdata->lincons) )
7042 return SCIP_OKAY;
7043
7044 SCIP_CALL( SCIPgetConsVars(scip, consdata->lincons, &(vars[nvars]), varssize - nvars, success) );
7045
7046 return SCIP_OKAY;
7047 }
7048
7049
7050 /** constraint method of constraint handler which returns the number of variables (if possible) */
7051 static
7052 SCIP_DECL_CONSGETNVARS(consGetNVarsIndicator)
7053 { /*lint --e{715}*/
7054 SCIP_CONSDATA* consdata;
7055 int nlinvars;
7056
7057 assert( scip != NULL );
7058 assert( cons != NULL );
7059 assert( nvars != NULL );
7060 assert( success != NULL );
7061
7062 (*success) = TRUE;
7063 *nvars = 0;
7064
7065 /* if indicator constraint is already deleted */
7066 if ( SCIPconsIsDeleted(cons) )
7067 return SCIP_OKAY;
7068
7069 consdata = SCIPconsGetData(cons);
7070 assert( consdata != NULL );
7071 assert( consdata->lincons != NULL );
7072
7073 if ( consdata->binvar != NULL )
7074 ++(*nvars);
7075 if ( consdata->slackvar != NULL )
7076 ++(*nvars);
7077
7078 /* if linear constraint of indicator is already deleted */
7079 if ( SCIPconsIsDeleted(consdata->lincons) )
7080 return SCIP_OKAY;
7081
7082 SCIP_CALL( SCIPgetConsNVars(scip, consdata->lincons, &nlinvars, success) );
7083
7084 if ( *success )
7085 {
7086 assert( nlinvars >= 0 );
7087 *nvars += nlinvars;
7088 }
7089
7090 return SCIP_OKAY;
7091 }
7092
7093
7094 /** constraint handler method to suggest dive bound changes during the generic diving algorithm */
7095 static
7096 SCIP_DECL_CONSGETDIVEBDCHGS(consGetDiveBdChgsIndicator)
7097 {
7098 SCIP_CONS** indconss;
7099 int nindconss;
7100 int c;
7101 SCIP_VAR* bestvar = NULL;
7102 SCIP_Bool bestvarroundup = FALSE;
7103 SCIP_Real bestscore = SCIP_REAL_MIN;
7104
7105 assert(scip != NULL);
7106 assert(conshdlr != NULL);
7107 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
7108 assert(diveset != NULL);
7109 assert(success != NULL);
7110 assert(infeasible != NULL);
7111
7112 *success = FALSE;
7113 *infeasible = FALSE;
7114
7115 indconss = SCIPconshdlrGetConss(conshdlr);
7116 nindconss = SCIPconshdlrGetNConss(conshdlr);
7117
7118 /* loop over indicator constraints and score indicator variables with already integral solution value */
7119 for (c = 0; c < nindconss; ++c)
7120 {
7121 /* check whether constraint is violated */
7122 if( SCIPisViolatedIndicator(scip, indconss[c], sol) )
7123 {
7124 SCIP_VAR* binvar;
7125 SCIP_Real solval;
7126
7127 binvar = SCIPgetBinaryVarIndicator(indconss[c]);
7128 solval = SCIPgetSolVal(scip, sol, binvar);
7129
7130 /* we only treat indicator variables with integral solution values that are not yet fixed */
7131 if( SCIPisFeasIntegral(scip, solval) && SCIPvarGetLbLocal(binvar) < SCIPvarGetUbLocal(binvar) - 0.5 )
7132 {
7133 SCIP_Real score;
7134 SCIP_Bool roundup;
7135
7136 SCIP_CALL( SCIPgetDivesetScore(scip, diveset, SCIP_DIVETYPE_INTEGRALITY, binvar, solval, 0.0,
7137 &score, &roundup) );
7138
7139 /* best candidate maximizes the score */
7140 if( score > bestscore )
7141 {
7142 bestscore = score;
7143 *success = TRUE;
7144 bestvar = binvar;
7145 bestvarroundup = roundup;
7146 }
7147 }
7148 }
7149 }
7150
7151 assert(! *success || bestvar != NULL);
7152
7153 if( *success )
7154 {
7155 /* if the diving score voted for fixing the best variable to 1.0, we add this as the preferred bound change */
7156 SCIP_CALL( SCIPaddDiveBoundChange(scip, bestvar, SCIP_BRANCHDIR_UPWARDS, 1.0, bestvarroundup) );
7157 SCIP_CALL( SCIPaddDiveBoundChange(scip, bestvar, SCIP_BRANCHDIR_DOWNWARDS, 0.0, ! bestvarroundup) );
7158 }
7159
7160 return SCIP_OKAY;
7161 }
7162
7163 /* ---------------- Constraint specific interface methods ---------------- */
7164
7165 /** creates the handler for indicator constraints and includes it in SCIP */
7166 SCIP_RETCODE SCIPincludeConshdlrIndicator(
7167 SCIP* scip /**< SCIP data structure */
7168 )
7169 {
7170 SCIP_CONFLICTHDLRDATA* conflicthdlrdata;
7171 SCIP_CONFLICTHDLR* conflicthdlr;
7172 SCIP_CONSHDLRDATA* conshdlrdata;
7173 SCIP_CONSHDLR* conshdlr;
7174
7175 /* create constraint handler data (used in conflicthdlrdata) */
7176 SCIP_CALL( SCIPallocBlockMemory(scip, &conshdlrdata) );
7177
7178 /* create event handler for bound change events */
7179 conshdlrdata->eventhdlrbound = NULL;
7180 SCIP_CALL( SCIPincludeEventhdlrBasic(scip, &(conshdlrdata->eventhdlrbound), EVENTHDLR_BOUND_NAME, EVENTHDLR_BOUND_DESC,
7181 eventExecIndicatorBound, NULL) );
7182 assert(conshdlrdata->eventhdlrbound != NULL);
7183
7184 /* create event handler for restart events */
7185 conshdlrdata->eventhdlrrestart = NULL;
7186 SCIP_CALL( SCIPincludeEventhdlrBasic(scip, &(conshdlrdata->eventhdlrrestart), EVENTHDLR_RESTART_NAME, EVENTHDLR_RESTART_DESC,
7187 eventExecIndicatorRestart, NULL) );
7188 assert( conshdlrdata->eventhdlrrestart != NULL );
7189
7190 conshdlrdata->heurtrysol = NULL;
7191 conshdlrdata->sepaalternativelp = DEFAULT_SEPAALTERNATIVELP;
7192 conshdlrdata->nolinconscont = DEFAULT_NOLINCONSCONT;
7193 conshdlrdata->forcerestart = DEFAULT_FORCERESTART;
7194 conshdlrdata->binvarhash = NULL;
7195
7196 /* initialize constraint handler data */
7197 initConshdlrData(scip, conshdlrdata);
7198
7199 /* the following three variables cannot be initialized in the above method, because initConshdlrData() is also called
7200 * in the CONSINIT callback, but these variables might be used even before the is ccallback is called, so we would
7201 * lose the data added before calling this callback */
7202 conshdlrdata->addlincons = NULL;
7203 conshdlrdata->naddlincons = 0;
7204 conshdlrdata->maxaddlincons = 0;
7205
7206 /* include constraint handler */
7207 SCIP_CALL( SCIPincludeConshdlrBasic(scip, &conshdlr, CONSHDLR_NAME, CONSHDLR_DESC,
7208 CONSHDLR_ENFOPRIORITY, CONSHDLR_CHECKPRIORITY, CONSHDLR_EAGERFREQ, CONSHDLR_NEEDSCONS,
7209 consEnfolpIndicator, consEnfopsIndicator, consCheckIndicator, consLockIndicator,
7210 conshdlrdata) );
7211
7212 assert( conshdlr != NULL );
7213
7214 /* set non-fundamental callbacks via specific setter functions */
7215 SCIP_CALL( SCIPsetConshdlrCopy(scip, conshdlr, conshdlrCopyIndicator, consCopyIndicator) );
7216 SCIP_CALL( SCIPsetConshdlrDelete(scip, conshdlr, consDeleteIndicator) );
7217 SCIP_CALL( SCIPsetConshdlrDisable(scip, conshdlr, consDisableIndicator) );
7218 SCIP_CALL( SCIPsetConshdlrEnable(scip, conshdlr, consEnableIndicator) );
7219 SCIP_CALL( SCIPsetConshdlrGetDiveBdChgs(scip, conshdlr, consGetDiveBdChgsIndicator) );
7220 SCIP_CALL( SCIPsetConshdlrExit(scip, conshdlr, consExitIndicator) );
7221 SCIP_CALL( SCIPsetConshdlrExitsol(scip, conshdlr, consExitsolIndicator) );
7222 SCIP_CALL( SCIPsetConshdlrFree(scip, conshdlr, consFreeIndicator) );
7223 SCIP_CALL( SCIPsetConshdlrGetVars(scip, conshdlr, consGetVarsIndicator) );
7224 SCIP_CALL( SCIPsetConshdlrGetNVars(scip, conshdlr, consGetNVarsIndicator) );
7225 SCIP_CALL( SCIPsetConshdlrInit(scip, conshdlr, consInitIndicator) );
7226 SCIP_CALL( SCIPsetConshdlrInitpre(scip, conshdlr, consInitpreIndicator) );
7227 SCIP_CALL( SCIPsetConshdlrInitsol(scip, conshdlr, consInitsolIndicator) );
7228 SCIP_CALL( SCIPsetConshdlrInitlp(scip, conshdlr, consInitlpIndicator) );
7229 SCIP_CALL( SCIPsetConshdlrParse(scip, conshdlr, consParseIndicator) );
7230 SCIP_CALL( SCIPsetConshdlrPresol(scip, conshdlr, consPresolIndicator, CONSHDLR_MAXPREROUNDS, CONSHDLR_PRESOLTIMING) );
7231 SCIP_CALL( SCIPsetConshdlrPrint(scip, conshdlr, consPrintIndicator) );
7232 SCIP_CALL( SCIPsetConshdlrProp(scip, conshdlr, consPropIndicator, CONSHDLR_PROPFREQ, CONSHDLR_DELAYPROP,
7233 CONSHDLR_PROP_TIMING) );
7234 SCIP_CALL( SCIPsetConshdlrResprop(scip, conshdlr, consRespropIndicator) );
7235 SCIP_CALL( SCIPsetConshdlrSepa(scip, conshdlr, consSepalpIndicator, consSepasolIndicator, CONSHDLR_SEPAFREQ,
7236 CONSHDLR_SEPAPRIORITY, CONSHDLR_DELAYSEPA) );
7237 SCIP_CALL( SCIPsetConshdlrTrans(scip, conshdlr, consTransIndicator) );
7238 SCIP_CALL( SCIPsetConshdlrEnforelax(scip, conshdlr, consEnforelaxIndicator) );
7239
7240 /* add upgrading method */
7241 if ( SCIPfindConshdlr(scip, "linear") != NULL )
7242 {
7243 /* include the linear constraint upgrade in the linear constraint handler */
7244 SCIP_CALL( SCIPincludeLinconsUpgrade(scip, linconsUpgdIndicator, LINCONSUPGD_PRIORITY, CONSHDLR_NAME) );
7245 }
7246
7247 /* create conflict handler data */
7248 SCIP_CALL( SCIPallocBlockMemory(scip, &conflicthdlrdata) );
7249 conflicthdlrdata->conshdlrdata = conshdlrdata;
7250 conflicthdlrdata->conshdlr = conshdlr;
7251 assert( conflicthdlrdata->conshdlr != NULL );
7252
7253 /* create conflict handler for indicator constraints */
7254 SCIP_CALL( SCIPincludeConflicthdlrBasic(scip, &conflicthdlr, CONFLICTHDLR_NAME, CONFLICTHDLR_DESC, CONFLICTHDLR_PRIORITY,
7255 conflictExecIndicator, conflicthdlrdata) );
7256
7257 SCIP_CALL( SCIPsetConflicthdlrFree(scip, conflicthdlr, conflictFreeIndicator) );
7258
7259 /* add indicator constraint handler parameters */
7260 SCIP_CALL( SCIPaddBoolParam(scip,
7261 "constraints/indicator/branchindicators",
7262 "Branch on indicator constraints in enforcing?",
7263 &conshdlrdata->branchindicators, TRUE, DEFAULT_BRANCHINDICATORS, NULL, NULL) );
7264
7265 SCIP_CALL( SCIPaddBoolParam(scip,
7266 "constraints/indicator/genlogicor",
7267 "Generate logicor constraints instead of cuts?",
7268 &conshdlrdata->genlogicor, TRUE, DEFAULT_GENLOGICOR, NULL, NULL) );
7269
7270 SCIP_CALL( SCIPaddBoolParam(scip,
7271 "constraints/indicator/addcoupling",
7272 "Add coupling constraints or rows if big-M is small enough?",
7273 &conshdlrdata->addcoupling, TRUE, DEFAULT_ADDCOUPLING, NULL, NULL) );
7274
7275 SCIP_CALL( SCIPaddRealParam(scip,
7276 "constraints/indicator/maxcouplingvalue",
7277 "maximum coefficient for binary variable in coupling constraint",
7278 &conshdlrdata->maxcouplingvalue, TRUE, DEFAULT_MAXCOUPLINGVALUE, 0.0, 1e9, NULL, NULL) );
7279
7280 SCIP_CALL( SCIPaddBoolParam(scip,
7281 "constraints/indicator/addcouplingcons",
7282 "Add initial variable upper bound constraints, if 'addcoupling' is true?",
7283 &conshdlrdata->addcouplingcons, TRUE, DEFAULT_ADDCOUPLINGCONS, NULL, NULL) );
7284
7285 SCIP_CALL( SCIPaddBoolParam(scip,
7286 "constraints/indicator/sepacouplingcuts",
7287 "Should the coupling inequalities be separated dynamically?",
7288 &conshdlrdata->sepacouplingcuts, TRUE, DEFAULT_SEPACOUPLINGCUTS, NULL, NULL) );
7289
7290 SCIP_CALL( SCIPaddBoolParam(scip,
7291 "constraints/indicator/sepacouplinglocal",
7292 "Allow to use local bounds in order to separate coupling inequalities?",
7293 &conshdlrdata->sepacouplinglocal, TRUE, DEFAULT_SEPACOUPLINGLOCAL, NULL, NULL) );
7294
7295 SCIP_CALL( SCIPaddRealParam(scip,
7296 "constraints/indicator/sepacouplingvalue",
7297 "maximum coefficient for binary variable in separated coupling constraint",
7298 &conshdlrdata->sepacouplingvalue, TRUE, DEFAULT_SEPACOUPLINGVALUE, 0.0, 1e9, NULL, NULL) );
7299
7300 SCIP_CALL( SCIPaddBoolParam(scip,
7301 "constraints/indicator/sepaperspective",
7302 "Separate cuts based on perspective formulation?",
7303 &conshdlrdata->sepaperspective, TRUE, DEFAULT_SEPAPERSPECTIVE, NULL, NULL) );
7304
7305 SCIP_CALL( SCIPaddBoolParam(scip,
7306 "constraints/indicator/sepapersplocal",
7307 "Allow to use local bounds in order to separate perspective cuts?",
7308 &conshdlrdata->sepapersplocal, TRUE, DEFAULT_SEPAPERSPLOCAL, NULL, NULL) );
7309
7310 SCIP_CALL( SCIPaddIntParam(scip,
7311 "constraints/indicator/maxsepanonviolated",
7312 "maximal number of separated non violated IISs, before separation is stopped",
7313 &conshdlrdata->maxsepanonviolated, FALSE, DEFAULT_MAXSEPANONVIOLATED, 0, INT_MAX, NULL, NULL) );
7314
7315 SCIP_CALL( SCIPaddBoolParam(scip,
7316 "constraints/indicator/updatebounds",
7317 "Update bounds of original variables for separation?",
7318 &conshdlrdata->updatebounds, TRUE, DEFAULT_UPDATEBOUNDS, NULL, NULL) );
7319
7320 SCIP_CALL( SCIPaddRealParam(scip,
7321 "constraints/indicator/maxconditionaltlp",
7322 "maximum estimated condition of the solution basis matrix of the alternative LP to be trustworthy (0.0 to disable check)",
7323 &conshdlrdata->maxconditionaltlp, TRUE, DEFAULT_MAXCONDITIONALTLP, 0.0, SCIP_REAL_MAX, NULL, NULL) );
7324
7325 SCIP_CALL( SCIPaddIntParam(scip,
7326 "constraints/indicator/maxsepacuts",
7327 "maximal number of cuts separated per separation round",
7328 &conshdlrdata->maxsepacuts, FALSE, DEFAULT_MAXSEPACUTS, 0, INT_MAX, NULL, NULL) );
7329
7330 SCIP_CALL( SCIPaddIntParam(scip,
7331 "constraints/indicator/maxsepacutsroot",
7332 "maximal number of cuts separated per separation round in the root node",
7333 &conshdlrdata->maxsepacutsroot, FALSE, DEFAULT_MAXSEPACUTSROOT, 0, INT_MAX, NULL, NULL) );
7334
7335 SCIP_CALL( SCIPaddBoolParam(scip,
7336 "constraints/indicator/removeindicators",
7337 "Remove indicator constraint if corresponding variable bound constraint has been added?",
7338 &conshdlrdata->removeindicators, TRUE, DEFAULT_REMOVEINDICATORS, NULL, NULL) );
7339
7340 SCIP_CALL( SCIPaddBoolParam(scip,
7341 "constraints/indicator/generatebilinear",
7342 "Do not generate indicator constraint, but a bilinear constraint instead?",
7343 &conshdlrdata->generatebilinear, TRUE, DEFAULT_GENERATEBILINEAR, NULL, NULL) );
7344
7345 SCIP_CALL( SCIPaddBoolParam(scip,
7346 "constraints/indicator/scaleslackvar",
7347 "Scale slack variable coefficient at construction time?",
7348 &conshdlrdata->scaleslackvar, TRUE, DEFAULT_SCALESLACKVAR, NULL, NULL) );
7349
7350 SCIP_CALL( SCIPaddBoolParam(scip,
7351 "constraints/indicator/trysolutions",
7352 "Try to make solutions feasible by setting indicator variables?",
7353 &conshdlrdata->trysolutions, TRUE, DEFAULT_TRYSOLUTIONS, NULL, NULL) );
7354
7355 SCIP_CALL( SCIPaddBoolParam(scip,
7356 "constraints/indicator/enforcecuts",
7357 "In enforcing try to generate cuts (only if sepaalternativelp is true)?",
7358 &conshdlrdata->enforcecuts, TRUE, DEFAULT_ENFORCECUTS, NULL, NULL) );
7359
7360 SCIP_CALL( SCIPaddBoolParam(scip,
7361 "constraints/indicator/dualreductions",
7362 "Should dual reduction steps be performed?",
7363 &conshdlrdata->dualreductions, TRUE, DEFAULT_DUALREDUCTIONS, NULL, NULL) );
7364
7365 SCIP_CALL( SCIPaddBoolParam(scip,
7366 "constraints/indicator/addopposite",
7367 "Add opposite inequality in nodes in which the binary variable has been fixed to 0?",
7368 &conshdlrdata->addopposite, TRUE, DEFAULT_ADDOPPOSITE, NULL, NULL) );
7369
7370 SCIP_CALL( SCIPaddBoolParam(scip,
7371 "constraints/indicator/conflictsupgrade",
7372 "Try to upgrade bounddisjunction conflicts by replacing slack variables?",
7373 &conshdlrdata->conflictsupgrade, TRUE, DEFAULT_CONFLICTSUPGRADE, NULL, NULL) );
7374
7375 SCIP_CALL( SCIPaddRealParam(scip,
7376 "constraints/indicator/restartfrac",
7377 "fraction of binary variables that need to be fixed before restart occurs (in forcerestart)",
7378 &conshdlrdata->restartfrac, TRUE, DEFAULT_RESTARTFRAC, 0.0, 1.0, NULL, NULL) );
7379
7380 SCIP_CALL( SCIPaddBoolParam(scip,
7381 "constraints/indicator/useotherconss",
7382 "Collect other constraints to alternative LP?",
7383 &conshdlrdata->useotherconss, TRUE, DEFAULT_USEOTHERCONSS, NULL, NULL) );
7384
7385 SCIP_CALL( SCIPaddBoolParam(scip,
7386 "constraints/indicator/useobjectivecut",
7387 "Use objective cut with current best solution to alternative LP?",
7388 &conshdlrdata->useobjectivecut, TRUE, DEFAULT_USEOBJECTIVECUT, NULL, NULL) );
7389
7390 SCIP_CALL( SCIPaddBoolParam(scip,
7391 "constraints/indicator/trysolfromcover",
7392 "Try to construct a feasible solution from a cover?",
7393 &conshdlrdata->trysolfromcover, TRUE, DEFAULT_TRYSOLFROMCOVER, NULL, NULL) );
7394
7395 SCIP_CALL( SCIPaddBoolParam(scip,
7396 "constraints/indicator/upgradelinear",
7397 "Try to upgrade linear constraints to indicator constraints?",
7398 &conshdlrdata->upgradelinear, TRUE, DEFAULT_UPGRADELINEAR, NULL, NULL) );
7399
7400 /* parameters that should not be changed after problem stage: */
7401 SCIP_CALL( SCIPaddBoolParam(scip,
7402 "constraints/indicator/sepaalternativelp",
7403 "Separate using the alternative LP?",
7404 &conshdlrdata->sepaalternativelp_, TRUE, DEFAULT_SEPAALTERNATIVELP, paramChangedIndicator, NULL) );
7405
7406 SCIP_CALL( SCIPaddBoolParam(scip,
7407 "constraints/indicator/forcerestart",
7408 "Force restart if absolute gap is 1 or enough binary variables have been fixed?",
7409 &conshdlrdata->forcerestart_, TRUE, DEFAULT_FORCERESTART, paramChangedIndicator, NULL) );
7410
7411 SCIP_CALL( SCIPaddBoolParam(scip,
7412 "constraints/indicator/nolinconscont",
7413 "Decompose problem (do not generate linear constraint if all variables are continuous)?",
7414 &conshdlrdata->nolinconscont_, TRUE, DEFAULT_NOLINCONSCONT, paramChangedIndicator, NULL) );
7415
7416 return SCIP_OKAY;
7417 }
7418
7419 /** creates and captures an indicator constraint
7420 *
7421 * @note @a binvar is checked to be binary only later. This enables a change of the type in
7422 * procedures reading an instance.
7423 *
7424 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
7425 */
7426 SCIP_RETCODE SCIPcreateConsIndicator(
7427 SCIP* scip, /**< SCIP data structure */
7428 SCIP_CONS** cons, /**< pointer to hold the created constraint (indicator or quadratic) */
7429 const char* name, /**< name of constraint */
7430 SCIP_VAR* binvar, /**< binary indicator variable (or NULL) */
7431 int nvars, /**< number of variables in the inequality */
7432 SCIP_VAR** vars, /**< array with variables of inequality (or NULL) */
7433 SCIP_Real* vals, /**< values of variables in inequality (or NULL) */
7434 SCIP_Real rhs, /**< rhs of the inequality */
7435 SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP? Usually set to TRUE. */
7436 SCIP_Bool separate, /**< should the constraint be separated during LP processing?
7437 * Usually set to TRUE. */
7438 SCIP_Bool enforce, /**< should the constraint be enforced during node processing?
7439 * TRUE for model constraints, FALSE for additional, redundant constraints. */
7440 SCIP_Bool check, /**< should the constraint be checked for feasibility?
7441 * TRUE for model constraints, FALSE for additional, redundant constraints. */
7442 SCIP_Bool propagate, /**< should the constraint be propagated during node processing?
7443 * Usually set to TRUE. */
7444 SCIP_Bool local, /**< is constraint only valid locally?
7445 * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */
7446 SCIP_Bool dynamic, /**< is constraint subject to aging?
7447 * Usually set to FALSE. Set to TRUE for own cuts which
7448 * are separated as constraints. */
7449 SCIP_Bool removable, /**< should the relaxation be removed from the LP due to aging or cleanup?
7450 * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */
7451 SCIP_Bool stickingatnode /**< should the constraint always be kept at the node where it was added, even
7452 * if it may be moved to a more global node?
7453 * Usually set to FALSE. Set to TRUE to for constraints that represent node data. */
7454 )
7455 {
7456 return SCIPcreateConsIndicatorGeneric(scip, cons, name, binvar, nvars, vars, vals, rhs, TRUE, TRUE, initial,
7457 separate, enforce, check, propagate, local, dynamic, removable, stickingatnode);
7458 }
7459
7460 /** creates and captures a indicator constraint in a more generic version.
7461 *
7462 * The key difference from SCIPcreateConsIndicator() is the activeone and lessthanineq Booleans.
7463 * If \f$z = o\f$, with \f$o\f$ the activeone flag, then:
7464 * if lessthanineq then \f$a^T x \leq b\f$ holds, else the passed vectors are assumed to be of the form \f$a^T x \geq b\f$.
7465 * The underlying linear constraint is always created as a less-than inequality.
7466 */
7467 SCIP_RETCODE SCIPcreateConsIndicatorGeneric(
7468 SCIP* scip, /**< SCIP data structure */
7469 SCIP_CONS** cons, /**< pointer to hold the created constraint (indicator or quadratic) */
7470 const char* name, /**< name of constraint */
7471 SCIP_VAR* binvar, /**< binary indicator variable (or NULL) */
7472 int nvars, /**< number of variables in the inequality */
7473 SCIP_VAR** vars, /**< array with variables of inequality (or NULL) */
7474 SCIP_Real* vals, /**< values of variables in inequality (or NULL) */
7475 SCIP_Real rhs, /**< rhs of the inequality */
7476 SCIP_Bool activeone, /**< is the constraint active when the binary is 1? */
7477 SCIP_Bool lessthanineq, /**< is the linear constraint a less than RHS (TRUE) or greater than RHS (FALSE)? */
7478 SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP? Usually set to TRUE. */
7479 SCIP_Bool separate, /**< should the constraint be separated during LP processing?
7480 * Usually set to TRUE. */
7481 SCIP_Bool enforce, /**< should the constraint be enforced during node processing?
7482 * TRUE for model constraints, FALSE for additional, redundant constraints. */
7483 SCIP_Bool check, /**< should the constraint be checked for feasibility?
7484 * TRUE for model constraints, FALSE for additional, redundant constraints. */
7485 SCIP_Bool propagate, /**< should the constraint be propagated during node processing?
7486 * Usually set to TRUE. */
7487 SCIP_Bool local, /**< is constraint only valid locally?
7488 * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */
7489 SCIP_Bool dynamic, /**< is constraint subject to aging?
7490 * Usually set to FALSE. Set to TRUE for own cuts which
7491 * are separated as constraints. */
7492 SCIP_Bool removable, /**< should the relaxation be removed from the LP due to aging or cleanup?
7493 * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */
7494 SCIP_Bool stickingatnode /**< should the constraint always be kept at the node where it was added, even
7495 * if it may be moved to a more global node?
7496 * Usually set to FALSE. Set to TRUE to for constraints that represent node data. */
7497 )
7498 {
7499 SCIP_CONSHDLR* conshdlr;
7500 SCIP_CONSHDLRDATA* conshdlrdata;
7501 SCIP_CONSDATA* consdata;
7502 SCIP_CONS* lincons;
7503 SCIP_VAR* slackvar;
7504 SCIP_Bool modifiable = FALSE;
7505 SCIP_Bool linconsactive;
7506 SCIP_VARTYPE slackvartype;
7507 SCIP_Real absvalsum = 0.0;
7508 char s[SCIP_MAXSTRLEN];
7509 SCIP_Real* valscopy;
7510 int j;
7511
7512 if ( nvars < 0 )
7513 {
7514 SCIPerrorMessage("Indicator constraint <%s> needs nonnegative number of variables in linear constraint.\n", name);
7515 return SCIP_INVALIDDATA;
7516 }
7517
7518 /* find the indicator constraint handler */
7519 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
7520 if ( conshdlr == NULL )
7521 {
7522 SCIPerrorMessage("<%s> constraint handler not found\n", CONSHDLR_NAME);
7523 return SCIP_PLUGINNOTFOUND;
7524 }
7525
7526 conshdlrdata = SCIPconshdlrGetData(conshdlr);
7527 assert( conshdlrdata != NULL );
7528
7529 if ( conshdlrdata->nolinconscont && ! conshdlrdata->sepaalternativelp )
7530 {
7531 SCIPerrorMessage("constraint handler <%s>: need parameter <sepaalternativelp> to be true if parameter <nolinconscont> is true.\n", CONSHDLR_NAME);
7532 return SCIP_INVALIDDATA;
7533 }
7534
7535 if ( conshdlrdata->nolinconscont && conshdlrdata->generatebilinear )
7536 {
7537 SCIPerrorMessage("constraint handler <%s>: parameters <nolinconscont> and <generatebilinear> cannot both be true.\n", CONSHDLR_NAME);
7538 return SCIP_INVALIDDATA;
7539 }
7540
7541 valscopy = NULL;
7542 if ( lessthanineq )
7543 valscopy = vals;
7544 else
7545 {
7546 /* flip coefficients and RHS of indicator */
7547 SCIP_CALL( SCIPallocBufferArray(scip, &valscopy, nvars) );
7548 for (j = 0; j < nvars; ++j)
7549 valscopy[j] = -vals[j];
7550 rhs = -rhs;
7551 }
7552 assert( nvars == 0 || valscopy != NULL );
7553
7554 /* check if slack variable can be made implicit integer */
7555 slackvartype = SCIP_VARTYPE_IMPLINT;
7556 for (j = 0; j < nvars; ++j)
7557 {
7558 if ( conshdlrdata->scaleslackvar )
7559 absvalsum += REALABS(valscopy[j]);
7560 if ( ! SCIPvarIsIntegral(vars[j]) || ! SCIPisIntegral(scip, valscopy[j]) )
7561 {
7562 slackvartype = SCIP_VARTYPE_CONTINUOUS;
7563 if ( ! conshdlrdata->scaleslackvar )
7564 break;
7565 }
7566 }
7567
7568 /* create slack variable */
7569 (void) SCIPsnprintf(s, SCIP_MAXSTRLEN, "indslack_%s", name);
7570 SCIP_CALL( SCIPcreateVar(scip, &slackvar, s, 0.0, SCIPinfinity(scip), 0.0, slackvartype, TRUE, FALSE,
7571 NULL, NULL, NULL, NULL, NULL) );
7572
7573 SCIP_CALL( SCIPaddVar(scip, slackvar) );
7574
7575 /* mark slack variable not to be multi-aggregated */
7576 SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, slackvar) );
7577
7578 /* if the problem should be decomposed if only non-integer variables are present */
7579 linconsactive = TRUE;
7580 if ( conshdlrdata->nolinconscont )
7581 {
7582 SCIP_Bool onlyCont = TRUE;
7583
7584 assert( ! conshdlrdata->generatebilinear );
7585
7586 /* check whether call variables are non-integer */
7587 for (j = 0; j < nvars; ++j)
7588 {
7589 SCIP_VARTYPE vartype;
7590
7591 vartype = SCIPvarGetType(vars[j]);
7592 if ( vartype != SCIP_VARTYPE_CONTINUOUS && vartype != SCIP_VARTYPE_IMPLINT )
7593 {
7594 onlyCont = FALSE;
7595 break;
7596 }
7597 }
7598
7599 if ( onlyCont )
7600 linconsactive = FALSE;
7601 }
7602
7603 /* create linear constraint */
7604 (void) SCIPsnprintf(s, SCIP_MAXSTRLEN, "indlin_%s", name);
7605
7606 /* if the linear constraint should be activated (lincons is captured) */
7607 if ( linconsactive )
7608 {
7609 /* the constraint is initial if initial is true, enforced, separated, and checked */
7610 SCIP_CALL( SCIPcreateConsLinear(scip, &lincons, s, nvars, vars, valscopy, -SCIPinfinity(scip), rhs,
7611 initial, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
7612 }
7613 else
7614 {
7615 /* create non-active linear constraint, which is neither initial, nor enforced, nor separated, nor checked */
7616 SCIP_CALL( SCIPcreateConsLinear(scip, &lincons, s, nvars, vars, valscopy, -SCIPinfinity(scip), rhs,
7617 FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE) );
7618 }
7619
7620 if ( ! lessthanineq )
7621 SCIPfreeBufferArray(scip, &valscopy);
7622
7623 /* mark linear constraint not to be upgraded - otherwise we loose control over it */
7624 SCIPconsAddUpgradeLocks(lincons, 1);
7625 assert( SCIPconsGetNUpgradeLocks(lincons) > 0 );
7626
7627 /* add slack variable */
7628 if ( conshdlrdata->scaleslackvar && nvars > 0 )
7629 {
7630 absvalsum = absvalsum/((SCIP_Real) nvars);
7631 if ( slackvartype == SCIP_VARTYPE_IMPLINT )
7632 absvalsum = SCIPceil(scip, absvalsum);
7633 if ( SCIPisZero(scip, absvalsum) )
7634 absvalsum = 1.0;
7635 SCIP_CALL( SCIPaddCoefLinear(scip, lincons, slackvar, -absvalsum) );
7636 }
7637 else
7638 {
7639 SCIP_CALL( SCIPaddCoefLinear(scip, lincons, slackvar, -1.0) );
7640 }
7641 SCIP_CALL( SCIPaddCons(scip, lincons) );
7642
7643 /* check whether we should generate a bilinear constraint instead of an indicator constraint */
7644 if ( conshdlrdata->generatebilinear )
7645 {
7646 SCIP_Real val = 1.0;
7647 SCIP_VAR* binvarinternal;
7648
7649 /* if active on 0, the binary variable is reversed */
7650 if ( activeone )
7651 binvarinternal = binvar;
7652 else
7653 {
7654 SCIP_CALL ( SCIPgetNegatedVar(scip, binvar, &binvarinternal) );
7655 }
7656
7657 /* create a quadratic constraint with a single bilinear term - note that cons is used */
7658 SCIP_CALL( SCIPcreateConsQuadraticNonlinear(scip, cons, name, 0, NULL, NULL, 1, &binvarinternal, &slackvar, &val, 0.0, 0.0,
7659 TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE) );
7660 }
7661 else
7662 {
7663 /* create constraint data */
7664 consdata = NULL;
7665 SCIP_CALL( consdataCreate(scip, conshdlr, conshdlrdata, name, &consdata, conshdlrdata->eventhdlrbound, conshdlrdata->eventhdlrrestart,
7666 binvar, activeone, lessthanineq, slackvar, lincons, linconsactive) );
7667 assert( consdata != NULL );
7668 /* do not need to capture slack variable and linear constraint here */
7669
7670 /* create constraint */
7671 SCIP_CALL( SCIPcreateCons(scip, cons, name, conshdlr, consdata, initial, separate, enforce, check, propagate,
7672 local, modifiable, dynamic, removable, stickingatnode) );
7673
7674 if ( SCIPisTransformed(scip) )
7675 {
7676 /* make sure that binary variable hash exists */
7677 if ( conshdlrdata->sepaalternativelp )
7678 {
7679 SCIP_VAR* binvarinternal;
7680
7681 if ( conshdlrdata->binvarhash == NULL )
7682 {
7683 SCIP_CALL( SCIPhashmapCreate(&conshdlrdata->binvarhash, SCIPblkmem(scip), SCIPgetNOrigVars(scip)) );
7684 }
7685
7686 /* if active on 0, the binary variable is reversed */
7687 if ( activeone )
7688 binvarinternal = binvar;
7689 else
7690 {
7691 SCIP_CALL ( SCIPgetNegatedVar(scip, binvar, &binvarinternal) );
7692 }
7693
7694 /* check whether binary variable is present: note that a binary variable might appear several times, but this seldomly happens. */
7695 assert( conshdlrdata->binvarhash != NULL );
7696 if ( ! SCIPhashmapExists(conshdlrdata->binvarhash, (void*) binvarinternal) )
7697 {
7698 SCIP_CALL( SCIPhashmapInsert(conshdlrdata->binvarhash, (void*) binvarinternal, (void*) (*cons)) );
7699 }
7700 }
7701 }
7702 }
7703
7704 return SCIP_OKAY;
7705 }
7706
7707 /** creates and captures an indicator constraint in its most basic version, i. e., all constraint flags are set to their
7708 * basic value as explained for the method SCIPcreateConsIndicator(); all flags can be set via
7709 * SCIPsetConsFLAGNAME-methods in scip.h
7710 *
7711 * @see SCIPcreateConsIndicator() for information about the basic constraint flag configuration
7712 *
7713 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
7714 */
7715 SCIP_RETCODE SCIPcreateConsBasicIndicator(
7716 SCIP* scip, /**< SCIP data structure */
7717 SCIP_CONS** cons, /**< pointer to hold the created constraint (indicator or quadratic) */
7718 const char* name, /**< name of constraint */
7719 SCIP_VAR* binvar, /**< binary indicator variable (or NULL) */
7720 int nvars, /**< number of variables in the inequality */
7721 SCIP_VAR** vars, /**< array with variables of inequality (or NULL) */
7722 SCIP_Real* vals, /**< values of variables in inequality (or NULL) */
7723 SCIP_Real rhs /**< rhs of the inequality */
7724 )
7725 {
7726 assert( scip != NULL );
7727
7728 SCIP_CALL( SCIPcreateConsIndicator(scip, cons, name, binvar, nvars, vars, vals, rhs,
7729 TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE) );
7730
7731 return SCIP_OKAY;
7732 }
7733
7734 /** creates and captures an indicator constraint with given linear constraint and slack variable
7735 * in a generic version, i. e., with a flag activeone indicating whether the constraint is active on
7736 * value 1 or 0 of the binary variable.
7737
7738 * @note @a binvar is checked to be binary only later. This enables a change of the type in
7739 * procedures reading an instance.
7740 *
7741 * @note we assume that @a slackvar actually appears in @a lincons and we also assume that it takes
7742 * the role of a slack variable!
7743 *
7744 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
7745 *
7746 * @see SCIPcreateConsIndicatorLinCons() for information about the basic constraint flag configuration
7747 */
7748 SCIP_RETCODE SCIPcreateConsIndicatorGenericLinCons(
7749 SCIP* scip, /**< SCIP data structure */
7750 SCIP_CONS** cons, /**< pointer to hold the created constraint */
7751 const char* name, /**< name of constraint */
7752 SCIP_VAR* binvar, /**< binary indicator variable (or NULL) */
7753 SCIP_CONS* lincons, /**< linear constraint */
7754 SCIP_VAR* slackvar, /**< slack variable */
7755 SCIP_Bool activeone, /**< is the constraint active when the binary is 1? */
7756 SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP? Usually set to TRUE. */
7757 SCIP_Bool separate, /**< should the constraint be separated during LP processing?
7758 * Usually set to TRUE. */
7759 SCIP_Bool enforce, /**< should the constraint be enforced during node processing?
7760 * TRUE for model constraints, FALSE for additional, redundant constraints. */
7761 SCIP_Bool check, /**< should the constraint be checked for feasibility?
7762 * TRUE for model constraints, FALSE for additional, redundant constraints. */
7763 SCIP_Bool propagate, /**< should the constraint be propagated during node processing?
7764 * Usually set to TRUE. */
7765 SCIP_Bool local, /**< is constraint only valid locally?
7766 * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */
7767 SCIP_Bool dynamic, /**< is constraint subject to aging?
7768 * Usually set to FALSE. Set to TRUE for own cuts which
7769 * are separated as constraints. */
7770 SCIP_Bool removable, /**< should the relaxation be removed from the LP due to aging or cleanup?
7771 * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */
7772 SCIP_Bool stickingatnode /**< should the constraint always be kept at the node where it was added, even
7773 * if it may be moved to a more global node?
7774 * Usually set to FALSE. Set to TRUE to for constraints that represent node data. */
7775 )
7776 {
7777 SCIP_CONSHDLR* conshdlr;
7778 SCIP_CONSHDLRDATA* conshdlrdata;
7779 SCIP_CONSDATA* consdata = NULL;
7780 SCIP_Bool modifiable = FALSE;
7781 SCIP_Bool linconsactive = TRUE;
7782
7783 assert( scip != NULL );
7784 assert( lincons != NULL );
7785 assert( slackvar != NULL );
7786
7787 /* check whether lincons is really a linear constraint */
7788 conshdlr = SCIPconsGetHdlr(lincons);
7789 if ( strcmp(SCIPconshdlrGetName(conshdlr), "linear") != 0 )
7790 {
7791 SCIPerrorMessage("Lincons constraint is not linear.\n");
7792 return SCIP_INVALIDDATA;
7793 }
7794
7795 /* find the indicator constraint handler */
7796 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
7797 if ( conshdlr == NULL )
7798 {
7799 SCIPerrorMessage("<%s> constraint handler not found.\n", CONSHDLR_NAME);
7800 return SCIP_PLUGINNOTFOUND;
7801 }
7802
7803 conshdlrdata = SCIPconshdlrGetData(conshdlr);
7804 assert( conshdlrdata != NULL );
7805
7806 if ( conshdlrdata->nolinconscont && ! conshdlrdata->sepaalternativelp )
7807 {
7808 SCIPerrorMessage("constraint handler <%s>: need parameter <sepaalternativelp> to be true if parameter <nolinconscont> is true.\n", CONSHDLR_NAME);
7809 return SCIP_INVALIDDATA;
7810 }
7811
7812 /* mark slack variable not to be multi-aggregated */
7813 SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, slackvar) );
7814
7815 /* if the problem should be decomposed (only if all variables are continuous) */
7816 if ( conshdlrdata->nolinconscont )
7817 {
7818 SCIP_Bool onlyCont = TRUE;
7819 int v;
7820 int nvars;
7821 SCIP_VAR** vars;
7822
7823 nvars = SCIPgetNVarsLinear(scip, lincons);
7824 vars = SCIPgetVarsLinear(scip, lincons);
7825
7826 /* check whether call variables are non-integer */
7827 for (v = 0; v < nvars; ++v)
7828 {
7829 SCIP_VARTYPE vartype;
7830
7831 vartype = SCIPvarGetType(vars[v]);
7832 if ( vartype != SCIP_VARTYPE_CONTINUOUS && vartype != SCIP_VARTYPE_IMPLINT )
7833 {
7834 onlyCont = FALSE;
7835 break;
7836 }
7837 }
7838
7839 if ( onlyCont )
7840 linconsactive = FALSE;
7841 }
7842
7843 /* mark linear constraint not to be upgraded - otherwise we loose control over it */
7844 SCIPconsAddUpgradeLocks(lincons, 1);
7845 assert( SCIPconsGetNUpgradeLocks(lincons) > 0 );
7846
7847 /* check whether we should generate a bilinear constraint instead of an indicator constraint */
7848 if ( conshdlrdata->generatebilinear )
7849 {
7850 SCIP_Real val = 1.0;
7851
7852 /* if active on 0, the binary variable is reversed */
7853 SCIP_VAR* binvarinternal;
7854 if ( activeone )
7855 {
7856 binvarinternal = binvar;
7857 }
7858 else
7859 {
7860 SCIP_CALL ( SCIPgetNegatedVar(scip, binvar, &binvarinternal) );
7861 }
7862
7863 /* create a quadratic constraint with a single bilinear term - note that cons is used */
7864 SCIP_CALL( SCIPcreateConsQuadraticNonlinear(scip, cons, name, 0, NULL, NULL, 1, &binvarinternal, &slackvar, &val, 0.0, 0.0,
7865 TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE) );
7866 }
7867 else
7868 {
7869 /* create constraint data */
7870 SCIP_CALL( consdataCreate(scip, conshdlr, conshdlrdata, name, &consdata, conshdlrdata->eventhdlrbound, conshdlrdata->eventhdlrrestart,
7871 binvar, activeone, TRUE, slackvar, lincons, linconsactive) );
7872 assert( consdata != NULL );
7873
7874 /* create constraint */
7875 SCIP_CALL( SCIPcreateCons(scip, cons, name, conshdlr, consdata, initial, separate, enforce, check, propagate,
7876 local, modifiable, dynamic, removable, stickingatnode) );
7877 }
7878
7879 /* capture slack variable and linear constraint */
7880 SCIP_CALL( SCIPcaptureVar(scip, slackvar) );
7881 SCIP_CALL( SCIPcaptureCons(scip, lincons) );
7882
7883 return SCIP_OKAY;
7884 }
7885
7886 /** creates and captures an indicator constraint with given linear constraint and slack variable
7887 *
7888 * @note @a binvar is checked to be binary only later. This enables a change of the type in
7889 * procedures reading an instance.
7890 *
7891 * @note we assume that @a slackvar actually appears in @a lincons and we also assume that it takes
7892 * the role of a slack variable!
7893 *
7894 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
7895 */
7896 SCIP_RETCODE SCIPcreateConsIndicatorLinCons(
7897 SCIP* scip, /**< SCIP data structure */
7898 SCIP_CONS** cons, /**< pointer to hold the created constraint */
7899 const char* name, /**< name of constraint */
7900 SCIP_VAR* binvar, /**< binary indicator variable (or NULL) */
7901 SCIP_CONS* lincons, /**< linear constraint */
7902 SCIP_VAR* slackvar, /**< slack variable */
7903 SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP? Usually set to TRUE. */
7904 SCIP_Bool separate, /**< should the constraint be separated during LP processing?
7905 * Usually set to TRUE. */
7906 SCIP_Bool enforce, /**< should the constraint be enforced during node processing?
7907 * TRUE for model constraints, FALSE for additional, redundant constraints. */
7908 SCIP_Bool check, /**< should the constraint be checked for feasibility?
7909 * TRUE for model constraints, FALSE for additional, redundant constraints. */
7910 SCIP_Bool propagate, /**< should the constraint be propagated during node processing?
7911 * Usually set to TRUE. */
7912 SCIP_Bool local, /**< is constraint only valid locally?
7913 * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */
7914 SCIP_Bool dynamic, /**< is constraint subject to aging?
7915 * Usually set to FALSE. Set to TRUE for own cuts which
7916 * are separated as constraints. */
7917 SCIP_Bool removable, /**< should the relaxation be removed from the LP due to aging or cleanup?
7918 * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */
7919 SCIP_Bool stickingatnode /**< should the constraint always be kept at the node where it was added, even
7920 * if it may be moved to a more global node?
7921 * Usually set to FALSE. Set to TRUE to for constraints that represent node data. */
7922 )
7923 {
7924 return SCIPcreateConsIndicatorGenericLinCons(scip, cons, name, binvar, lincons, slackvar, TRUE, initial, separate,
7925 enforce, check, propagate, local, dynamic, removable, stickingatnode);
7926 }
7927
7928
7929 /** creates and captures an indicator constraint with given linear constraint and slack variable
7930 * in its most basic version, i. e., all constraint flags are set to their basic value as explained for the
7931 * method SCIPcreateConsIndicator(); all flags can be set via SCIPsetConsFLAGNAME-methods in scip.h
7932 *
7933 * @note @a binvar is checked to be binary only later. This enables a change of the type in
7934 * procedures reading an instance.
7935 *
7936 * @note we assume that @a slackvar actually appears in @a lincons and we also assume that it takes
7937 * the role of a slack variable!
7938 *
7939 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
7940 *
7941 * @see SCIPcreateConsIndicatorLinCons() for information about the basic constraint flag configuration
7942 *
7943 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
7944 */
7945 SCIP_RETCODE SCIPcreateConsBasicIndicatorLinCons(
7946 SCIP* scip, /**< SCIP data structure */
7947 SCIP_CONS** cons, /**< pointer to hold the created constraint */
7948 const char* name, /**< name of constraint */
7949 SCIP_VAR* binvar, /**< binary indicator variable (or NULL) */
7950 SCIP_CONS* lincons, /**< linear constraint */
7951 SCIP_VAR* slackvar /**< slack variable */
7952 )
7953 {
7954 assert( scip != NULL );
7955
7956 SCIP_CALL( SCIPcreateConsIndicatorLinCons(scip, cons, name, binvar, lincons, slackvar,
7957 TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE) );
7958
7959 return SCIP_OKAY;
7960 }
7961
7962
7963 /** adds variable to the inequality of the indicator constraint */
7964 SCIP_RETCODE SCIPaddVarIndicator(
7965 SCIP* scip, /**< SCIP data structure */
7966 SCIP_CONS* cons, /**< indicator constraint */
7967 SCIP_VAR* var, /**< variable to add to the inequality */
7968 SCIP_Real val /**< value of variable */
7969 )
7970 {
7971 SCIP_CONSDATA* consdata;
7972
7973 assert( cons != NULL );
7974 assert( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0 );
7975
7976 consdata = SCIPconsGetData(cons);
7977 assert( consdata != NULL );
7978
7979 /* if linear inequality is flipped, variable is added with negative coefficient */
7980 if ( !consdata->lessthanineq )
7981 val = -val;
7982
7983 SCIP_CALL( SCIPaddCoefLinear(scip, consdata->lincons, var, val) );
7984
7985 /* possibly adapt variable type */
7986 if ( SCIPvarGetType(consdata->slackvar) != SCIP_VARTYPE_CONTINUOUS && (! SCIPvarIsIntegral(var) || ! SCIPisIntegral(scip, val) ) )
7987 {
7988 SCIP_Bool infeasible;
7989
7990 SCIP_CALL( SCIPchgVarType(scip, consdata->slackvar, SCIP_VARTYPE_CONTINUOUS, &infeasible) );
7991 assert( ! infeasible );
7992 }
7993
7994 return SCIP_OKAY;
7995 }
7996
7997
7998 /** gets the linear constraint corresponding to the indicator constraint (may be NULL) */
7999 SCIP_CONS* SCIPgetLinearConsIndicator(
8000 SCIP_CONS* cons /**< indicator constraint */
8001 )
8002 {
8003 SCIP_CONSDATA* consdata;
8004
8005 assert( cons != NULL );
8006 assert( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0 );
8007
8008 consdata = SCIPconsGetData(cons);
8009 assert( consdata != NULL );
8010
8011 return consdata->lincons;
8012 }
8013
8014
8015 /** sets the linear constraint corresponding to the indicator constraint (may be NULL) */
8016 SCIP_RETCODE SCIPsetLinearConsIndicator(
8017 SCIP* scip, /**< SCIP data structure */
8018 SCIP_CONS* cons, /**< indicator constraint */
8019 SCIP_CONS* lincons /**< linear constraint */
8020 )
8021 {
8022 SCIP_CONSHDLR* conshdlr;
8023 SCIP_CONSHDLRDATA* conshdlrdata;
8024 SCIP_CONSDATA* consdata;
8025
8026 if ( SCIPgetStage(scip) != SCIP_STAGE_PROBLEM )
8027 {
8028 SCIPerrorMessage("Cannot set linear constraint in SCIP stage <%d>\n", SCIPgetStage(scip) );
8029 return SCIP_INVALIDCALL;
8030 }
8031
8032 assert( cons != NULL );
8033 conshdlr = SCIPconsGetHdlr(cons);
8034
8035 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
8036 conshdlrdata = SCIPconshdlrGetData(conshdlr);
8037 assert( conshdlrdata != NULL );
8038
8039 consdata = SCIPconsGetData(cons);
8040 assert( consdata != NULL );
8041
8042 /* free old linear constraint */
8043 assert( consdata->lincons != NULL );
8044 SCIP_CALL( SCIPdelCons(scip, consdata->lincons) );
8045 SCIP_CALL( SCIPreleaseCons(scip, &(consdata->lincons) ) );
8046
8047 assert( lincons != NULL );
8048 consdata->lincons = lincons;
8049 consdata->linconsactive = TRUE;
8050 SCIP_CALL( SCIPcaptureCons(scip, lincons) );
8051
8052 /* if the problem should be decomposed if only non-integer variables are present */
8053 if ( conshdlrdata->nolinconscont )
8054 {
8055 SCIP_Bool onlyCont;
8056 int v;
8057 int nvars;
8058 SCIP_VAR** vars;
8059
8060 onlyCont = TRUE;
8061 nvars = SCIPgetNVarsLinear(scip, lincons);
8062 vars = SCIPgetVarsLinear(scip, lincons);
8063 assert( vars != NULL );
8064
8065 /* check whether call variables are non-integer */
8066 for (v = 0; v < nvars; ++v)
8067 {
8068 SCIP_VARTYPE vartype;
8069
8070 vartype = SCIPvarGetType(vars[v]);
8071 if ( vartype != SCIP_VARTYPE_CONTINUOUS && vartype != SCIP_VARTYPE_IMPLINT )
8072 {
8073 onlyCont = FALSE;
8074 break;
8075 }
8076 }
8077
8078 if ( onlyCont )
8079 consdata->linconsactive = FALSE;
8080 }
8081
8082 return SCIP_OKAY;
8083 }
8084
8085 /** gets activation value of an indicator constraint, TRUE for active on 1, FALSE for active on 0 */
8086 SCIP_Bool SCIPgetActiveOnIndicator(
8087 SCIP_CONS* cons /**< indicator constraint */
8088 )
8089 {
8090 SCIP_CONSDATA* consdata;
8091
8092 assert( cons != NULL );
8093 assert( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0 );
8094
8095 consdata = SCIPconsGetData(cons);
8096 assert( consdata != NULL );
8097
8098 return consdata->activeone;
8099 }
8100
8101
8102 /** gets binary variable corresponding to indicator constraint */
8103 SCIP_VAR* SCIPgetBinaryVarIndicator(
8104 SCIP_CONS* cons /**< indicator constraint */
8105 )
8106 {
8107 SCIP_CONSDATA* consdata;
8108
8109 assert( cons != NULL );
8110 assert( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0 );
8111
8112 consdata = SCIPconsGetData(cons);
8113 assert( consdata != NULL );
8114
8115 return consdata->binvar;
8116 }
8117
8118 /** similar to SCIPgetBinaryVarIndicator but returns the original binary variable passed by the user. */
8119 SCIP_VAR* SCIPgetBinaryVarIndicatorGeneric(
8120 SCIP_CONS* cons /**< indicator constraint */
8121 )
8122 {
8123 SCIP_CONSDATA* consdata;
8124 SCIP_VAR* binvar;
8125
8126 assert(cons != NULL);
8127 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
8128
8129 consdata = SCIPconsGetData(cons);
8130 assert(consdata != NULL);
8131 binvar = consdata->binvar;
8132
8133 if ( ! consdata->activeone )
8134 binvar = SCIPvarGetNegationVar(binvar);
8135 assert(binvar != NULL);
8136
8137 return binvar;
8138 }
8139
8140 /** sets binary indicator variable for indicator constraint */
8141 SCIP_RETCODE SCIPsetBinaryVarIndicator(
8142 SCIP* scip, /**< SCIP data structure */
8143 SCIP_CONS* cons, /**< indicator constraint */
8144 SCIP_VAR* binvar /**< binary variable to add to the inequality */
8145 )
8146 {
8147 SCIP_CONSDATA* consdata;
8148
8149 assert( cons != NULL );
8150 assert( binvar != NULL );
8151 assert( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0 );
8152
8153 consdata = SCIPconsGetData(cons);
8154 assert( consdata != NULL );
8155
8156 /* check type */
8157 if ( SCIPvarGetType(binvar) != SCIP_VARTYPE_BINARY )
8158 {
8159 SCIPerrorMessage("Indicator variable <%s> is not binary %d.\n", SCIPvarGetName(binvar), SCIPvarGetType(binvar));
8160 return SCIP_ERROR;
8161 }
8162
8163 /* check previous binary variable */
8164 if ( consdata->binvar != NULL )
8165 {
8166 /* to allow replacement of binary variables, we would need to drop events etc. */
8167 SCIPerrorMessage("Cannot replace binary variable <%s> for indicator constraint <%s>.\n", SCIPvarGetName(binvar), SCIPconsGetName(cons));
8168 return SCIP_INVALIDCALL;
8169 }
8170
8171 /* if we are transformed, obtain transformed variables and catch events */
8172 if ( SCIPconsIsTransformed(cons) )
8173 {
8174 SCIP_VAR* var;
8175 SCIP_CONSHDLR* conshdlr;
8176 SCIP_CONSHDLRDATA* conshdlrdata;
8177
8178 /* make sure we have a transformed binary variable */
(2) Event copy_paste_error: |
"var" in "&var" looks like a copy-paste error. |
(3) Event remediation: |
Should it say "binvar" instead? |
Also see events: |
[original] |
8179 SCIP_CALL( SCIPgetTransformedVar(scip, binvar, &var) );
8180 assert( var != NULL );
8181 if ( ! consdata->activeone )
8182 SCIP_CALL( SCIPgetNegatedVar(scip, var, &var) );
8183
8184 consdata->binvar = var;
8185
8186 conshdlr = SCIPconsGetHdlr(cons);
8187 assert( conshdlr != NULL );
8188 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
8189 conshdlrdata = SCIPconshdlrGetData(conshdlr);
8190 assert( conshdlrdata != NULL );
8191 assert( conshdlrdata->eventhdlrbound != NULL );
8192 assert( conshdlrdata->eventhdlrrestart != NULL );
8193
8194 /* catch local bound change events on binary variable */
8195 if ( consdata->linconsactive )
8196 {
8197 SCIP_CALL( SCIPcatchVarEvent(scip, var, SCIP_EVENTTYPE_BOUNDCHANGED, conshdlrdata->eventhdlrbound, (SCIP_EVENTDATA*) consdata, NULL) );
8198 }
8199
8200 /* catch global bound change events on binary variable */
8201 if ( conshdlrdata->forcerestart )
8202 {
8203 SCIP_CALL( SCIPcatchVarEvent(scip, var, SCIP_EVENTTYPE_GBDCHANGED, conshdlrdata->eventhdlrrestart, (SCIP_EVENTDATA*) conshdlrdata, NULL) );
8204 }
8205
8206 /* if binary variable is fixed to be nonzero */
8207 if ( SCIPvarGetLbLocal(var) > 0.5 )
8208 ++(consdata->nfixednonzero);
8209 }
8210 else
8211 {
8212 if ( ! consdata->activeone )
8213 SCIP_CALL( SCIPgetNegatedVar(scip, binvar, &binvar) );
8214 consdata->binvar = binvar;
8215 }
8216
8217 return SCIP_OKAY;
8218 }
8219
8220 /** gets slack variable corresponding to indicator constraint */
8221 SCIP_VAR* SCIPgetSlackVarIndicator(
8222 SCIP_CONS* cons /**< indicator constraint */
8223 )
8224 {
8225 SCIP_CONSDATA* consdata;
8226
8227 assert( cons != NULL );
8228 assert( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0 );
8229
8230 consdata = SCIPconsGetData(cons);
8231 assert( consdata != NULL );
8232
8233 return consdata->slackvar;
8234 }
8235
8236
8237 /** sets upper bound for slack variable corresponding to indicator constraint
8238 *
8239 * Use with care if you know that the maximal violation of the corresponding constraint is at most @p ub. This bound
8240 * might be improved automatically during the solution process.
8241 *
8242 * @pre This method should only be called if SCIP is in one of the following stages:
8243 * - \ref SCIP_STAGE_INIT
8244 * - \ref SCIP_STAGE_PROBLEM
8245 */
8246 SCIP_RETCODE SCIPsetSlackVarUb(
8247 SCIP* scip, /**< SCIP data structure */
8248 SCIP_CONS* cons, /**< indicator constraint */
8249 SCIP_Real ub /**< upper bound for slack variable */
8250 )
8251 {
8252 SCIP_CONSDATA* consdata;
8253
8254 assert( scip != NULL );
8255 assert( cons != NULL );
8256 assert( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0 );
8257
8258 consdata = SCIPconsGetData(cons);
8259 assert( consdata != NULL );
8260
8261 if ( SCIPgetStage(scip) > SCIP_STAGE_PROBLEM )
8262 return SCIP_OKAY;
8263
8264 assert( consdata->slackvar != NULL );
8265 SCIP_CALL( SCIPchgVarUb(scip, consdata->slackvar, ub) );
8266
8267 return SCIP_OKAY;
8268 }
8269
8270
8271 /** checks whether indicator constraint is violated w.r.t. sol */
8272 SCIP_Bool SCIPisViolatedIndicator(
8273 SCIP* scip, /**< SCIP data structure */
8274 SCIP_CONS* cons, /**< indicator constraint */
8275 SCIP_SOL* sol /**< solution, or NULL to use current node's solution */
8276 )
8277 {
8278 SCIP_CONSDATA* consdata;
8279
8280 assert( cons != NULL );
8281
8282 /* deleted constraints should always be satisfied */
8283 if ( SCIPconsIsDeleted(cons) )
8284 return FALSE;
8285
8286 consdata = SCIPconsGetData(cons);
8287 assert( consdata != NULL );
8288
8289 if ( consdata->linconsactive )
8290 {
8291 assert( consdata->slackvar != NULL );
8292 assert( consdata->binvar != NULL );
8293 return(
8294 SCIPisFeasPositive(scip, SCIPgetSolVal(scip, sol, consdata->slackvar)) &&
8295 SCIPisFeasPositive(scip, SCIPgetSolVal(scip, sol, consdata->binvar)) );
8296 }
8297
8298 /* @todo: check how this can be decided for linconsactive == FALSE */
8299 return TRUE;
8300 }
8301
8302
8303 /** based on values of other variables, computes slack and binary variable to turn constraint feasible
8304 *
8305 * It will also clean up the solution, i.e., shift slack variable, as follows:
8306 *
8307 * If the inequality is \f$a^T x + \gamma\, s \leq \beta\f$, the value of the slack variable
8308 * \f$s\f$ to achieve equality is
8309 * \f[
8310 * s^* = \frac{\beta - a^T x^*}{\gamma},
8311 * \f]
8312 * where \f$x^*\f$ is the given solution. In case of \f$a^T x + \gamma\, s \geq \alpha\f$, we
8313 * arrive at
8314 * \f[
8315 * s^* = \frac{\alpha - a^T x^*}{\gamma}.
8316 * \f]
8317 * The typical values of \f$\gamma\f$ in the first case is -1 and +1 in the second case.
8318 *
8319 * Now, let \f$\sigma\f$ be the sign of \f$\gamma\f$ in the first case and \f$-\gamma\f$ in the
8320 * second case. Thus, if \f$\sigma > 0\f$ and \f$s^* < 0\f$, the inequality cannot be satisfied by
8321 * a nonnegative value for the slack variable; in this case, we have to leave the values as they
8322 * are. If \f$\sigma < 0\f$ and \f$s^* > 0\f$, the solution violates the indicator constraint (we
8323 * can set the slack variable to value \f$s^*\f$). If \f$\sigma < 0\f$ and \f$s^* \leq 0\f$ or
8324 * \f$\sigma > 0\f$ and \f$s^* \geq 0\f$, the constraint is satisfied, and we can set the slack
8325 * variable to 0.
8326 */
8327 SCIP_RETCODE SCIPmakeIndicatorFeasible(
8328 SCIP* scip, /**< SCIP data structure */
8329 SCIP_CONS* cons, /**< indicator constraint */
8330 SCIP_SOL* sol, /**< solution */
8331 SCIP_Bool* changed /**< pointer to store whether the solution has been changed */
8332 )
8333 {
8334 SCIP_CONSDATA* consdata;
8335 SCIP_CONS* lincons;
8336 SCIP_VAR** linvars;
8337 SCIP_Real* linvals;
8338 SCIP_VAR* slackvar;
8339 SCIP_VAR* binvar;
8340 SCIP_Real slackcoef;
8341 SCIP_Real sum;
8342 SCIP_Real val;
8343 int nlinvars;
8344 int sigma;
8345 int v;
8346
8347 assert( cons != NULL );
8348 assert( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0 );
8349 assert( sol != NULL );
8350 assert( changed != NULL );
8351
8352 *changed = FALSE;
8353
8354 /* avoid deleted indicator constraints, e.g., due to preprocessing */
8355 if ( ! SCIPconsIsActive(cons) && SCIPgetStage(scip) >= SCIP_STAGE_INITPRESOLVE )
8356 return SCIP_OKAY;
8357
8358 assert( cons != NULL );
8359 consdata = SCIPconsGetData(cons);
8360 assert( consdata != NULL );
8361
8362 /* if the linear constraint is not present, we cannot do anything */
8363 if ( ! consdata->linconsactive )
8364 return SCIP_OKAY;
8365
8366 lincons = consdata->lincons;
8367 assert( lincons != NULL );
8368
8369 /* avoid non-active linear constraints, e.g., due to preprocessing */
8370 if ( SCIPconsIsActive(lincons) || SCIPgetStage(scip) < SCIP_STAGE_INITPRESOLVE )
8371 {
8372 slackvar = consdata->slackvar;
8373 binvar = consdata->binvar;
8374 assert( slackvar != NULL );
8375 assert( binvar != NULL );
8376
8377 nlinvars = SCIPgetNVarsLinear(scip, lincons);
8378 linvars = SCIPgetVarsLinear(scip, lincons);
8379 linvals = SCIPgetValsLinear(scip, lincons);
8380
8381 /* compute value of regular variables */
8382 sum = 0.0;
8383 slackcoef = 0.0;
8384 for (v = 0; v < nlinvars; ++v)
8385 {
8386 SCIP_VAR* var;
8387 var = linvars[v];
8388 if ( var != slackvar )
8389 sum += linvals[v] * SCIPgetSolVal(scip, sol, var);
8390 else
8391 slackcoef = linvals[v];
8392 }
8393
8394 /* do nothing if slack variable does not appear */
8395 if ( SCIPisFeasZero(scip, slackcoef) )
8396 return SCIP_OKAY;
8397
8398 assert( ! SCIPisZero(scip, slackcoef) );
8399 assert( slackcoef != 0.0 ); /* to satisfy lint */
8400 assert( SCIPisInfinity(scip, -SCIPgetLhsLinear(scip, lincons)) || SCIPisInfinity(scip, SCIPgetRhsLinear(scip, lincons)) );
8401 assert( SCIPisFeasGE(scip, SCIPvarGetLbLocal(slackvar), 0.0) );
8402
8403 val = SCIPgetRhsLinear(scip, lincons);
8404 sigma = 1;
8405 if ( SCIPisInfinity(scip, val) )
8406 {
8407 val = SCIPgetLhsLinear(scip, lincons);
8408 assert( ! SCIPisInfinity(scip, REALABS(val)) );
8409 sigma = -1;
8410 }
8411 /* compute value of slack that would achieve equality */
8412 val = (val - sum)/slackcoef;
8413
8414 /* compute direction into which slack variable would be infeasible */
8415 if ( slackcoef < 0 )
8416 sigma *= -1;
8417
8418 /* filter out cases in which no sensible change is possible */
8419 if ( sigma > 0 && SCIPisFeasNegative(scip, val) )
8420 return SCIP_OKAY;
8421
8422 /* check if linear constraint w/o slack variable is violated */
8423 if ( sigma < 0 && SCIPisFeasPositive(scip, val) )
8424 {
8425 /* the original constraint is violated */
8426 if ( ! SCIPisFeasEQ(scip, SCIPgetSolVal(scip, sol, slackvar), val) )
8427 {
8428 SCIP_CALL( SCIPsetSolVal(scip, sol, slackvar, val) );
8429 *changed = TRUE;
8430 }
8431 /* check whether binary variable is fixed or its negated variable is fixed */
8432 if ( SCIPvarGetStatus(binvar) != SCIP_VARSTATUS_FIXED &&
8433 ( SCIPvarGetStatus(binvar) != SCIP_VARSTATUS_NEGATED || SCIPvarGetStatus(SCIPvarGetNegationVar(binvar)) != SCIP_VARSTATUS_FIXED ) )
8434 {
8435 if ( ! SCIPisFeasEQ(scip, SCIPgetSolVal(scip, sol, binvar), 0.0) )
8436 {
8437 SCIP_CALL( SCIPsetSolVal(scip, sol, binvar, 0.0) );
8438 *changed = TRUE;
8439 }
8440 }
8441 }
8442 else
8443 {
8444 assert( SCIPisFeasGE(scip, val * ((SCIP_Real) sigma), 0.0) );
8445
8446 /* the original constraint is satisfied - we can set the slack variable to 0 (slackvar
8447 * should only occur in this indicator constraint) */
8448 if ( ! SCIPisFeasEQ(scip, SCIPgetSolVal(scip, sol, slackvar), 0.0) && SCIPisFeasPositive(scip, SCIPvarGetLbLocal(slackvar)) )
8449 {
8450 SCIP_CALL( SCIPsetSolVal(scip, sol, slackvar, 0.0) );
8451 *changed = TRUE;
8452 }
8453
8454 /* check whether binary variable is fixed or its negated variable is fixed */
8455 if ( SCIPvarGetStatus(binvar) != SCIP_VARSTATUS_FIXED &&
8456 ( SCIPvarGetStatus(binvar) != SCIP_VARSTATUS_NEGATED || SCIPvarGetStatus(SCIPvarGetNegationVar(binvar)) != SCIP_VARSTATUS_FIXED ) )
8457 {
8458 SCIP_Real obj;
8459 obj = varGetObjDelta(binvar);
8460
8461 /* check objective for possibly setting binary variable */
8462 if ( obj <= 0 )
8463 {
8464 /* setting variable to 1 does not increase objective - check whether we can set it to 1 */
8465 if ( ! SCIPisFeasEQ(scip, SCIPgetSolVal(scip, sol, binvar), 1.0) )
8466 {
8467 /* check whether variable only occurs in the current constraint */
8468 if ( SCIPvarGetNLocksUpType(binvar, SCIP_LOCKTYPE_MODEL) <= 1 )
8469 {
8470 SCIP_CALL( SCIPsetSolVal(scip, sol, binvar, 1.0) );
8471 *changed = TRUE;
8472 /* make sure that the other case does not occur if obj = 0: prefer variables set to 1 */
8473 obj = -1.0;
8474 }
8475 }
8476 else
8477 {
8478 /* make sure that the other case does not occur if obj = 0: prefer variables set to 1 */
8479 obj = -1.0;
8480 }
8481 }
8482 if ( obj >= 0 )
8483 {
8484 /* setting variable to 0 does not increase objective -> check whether variable only occurs in the current constraint
8485 * note: binary variables are only locked up */
8486 if ( SCIPvarGetNLocksDownType(binvar, SCIP_LOCKTYPE_MODEL) <= 0
8487 && ! SCIPisFeasEQ(scip, SCIPgetSolVal(scip, sol, binvar), 0.0) )
8488 {
8489 SCIP_CALL( SCIPsetSolVal(scip, sol, binvar, 0.0) );
8490 *changed = TRUE;
8491 }
8492 }
8493 }
8494 }
8495 }
8496
8497 return SCIP_OKAY;
8498 }
8499
8500
8501 /** based on values of other variables, computes slack and binary variable to turn all constraints feasible */
8502 SCIP_RETCODE SCIPmakeIndicatorsFeasible(
8503 SCIP* scip, /**< SCIP data structure */
8504 SCIP_CONSHDLR* conshdlr, /**< indicator constraint handler */
8505 SCIP_SOL* sol, /**< solution */
8506 SCIP_Bool* changed /**< pointer to store whether the solution has been changed */
8507 )
8508 {
8509 SCIP_CONS** conss;
8510 int nconss;
8511 int c;
8512
8513 assert( conshdlr != NULL );
8514 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
8515 assert( sol != NULL );
8516 assert( changed != NULL );
8517
8518 *changed = FALSE;
8519
8520 /* only run after or in presolving */
8521 if ( SCIPgetStage(scip) < SCIP_STAGE_INITPRESOLVE )
8522 return SCIP_OKAY;
8523
8524 conss = SCIPconshdlrGetConss(conshdlr);
8525 nconss = SCIPconshdlrGetNConss(conshdlr);
8526
8527 for (c = 0; c < nconss; ++c)
8528 {
8529 SCIP_CONSDATA* consdata;
8530 SCIP_Bool chg = FALSE;
8531 assert( conss[c] != NULL );
8532
8533 consdata = SCIPconsGetData(conss[c]);
8534 assert( consdata != NULL );
8535
8536 /* if the linear constraint is not present, we stop */
8537 if ( ! consdata->linconsactive )
8538 break;
8539
8540 SCIP_CALL( SCIPmakeIndicatorFeasible(scip, conss[c], sol, &chg) );
8541 *changed = *changed || chg;
8542 }
8543
8544 return SCIP_OKAY;
8545 }
8546
8547
8548 /** adds additional linear constraint that is not connected with an indicator constraint, but can be used for separation */
8549 SCIP_RETCODE SCIPaddLinearConsIndicator(
8550 SCIP* scip, /**< SCIP data structure */
8551 SCIP_CONSHDLR* conshdlr, /**< indicator constraint handler */
8552 SCIP_CONS* lincons /**< linear constraint */
8553 )
8554 {
8555 assert( scip != NULL );
8556 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
8557 assert( lincons != NULL );
8558
8559 /* do not add locally valid constraints (this would require much more bookkeeping) */
8560 if ( ! SCIPconsIsLocal(lincons) && ! SCIPconsIsModifiable(lincons) )
8561 {
8562 SCIP_CONSHDLRDATA* conshdlrdata;
8563
8564 conshdlrdata = SCIPconshdlrGetData(conshdlr);
8565 assert( conshdlrdata != NULL );
8566
8567 SCIP_CALL( consdataEnsureAddLinConsSize(scip, conshdlr, conshdlrdata->naddlincons+1) );
8568 assert( conshdlrdata->naddlincons+1 <= conshdlrdata->maxaddlincons );
8569
8570 conshdlrdata->addlincons[conshdlrdata->naddlincons++] = lincons;
8571 }
8572
8573 return SCIP_OKAY;
8574 }
8575
8576
8577 /** adds additional row that is not connected with an indicator constraint, but can be used for separation
8578 *
8579 * @note The row is directly added to the alternative polyhedron and is not stored.
8580 */
8581 SCIP_RETCODE SCIPaddRowIndicator(
8582 SCIP* scip, /**< SCIP data structure */
8583 SCIP_CONSHDLR* conshdlr, /**< indicator constraint handler */
8584 SCIP_ROW* row /**< row to add */
8585 )
8586 {
8587 assert( scip != NULL );
8588 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
8589 assert( row != NULL );
8590
8591 /* skip local cuts (local cuts would require to dynamically add and remove columns from the alternative polyhedron */
8592 if ( ! SCIProwIsLocal(row) )
8593 {
8594 int colindex;
8595 SCIP_CONSHDLRDATA* conshdlrdata;
8596
8597 conshdlrdata = SCIPconshdlrGetData(conshdlr);
8598 assert( conshdlrdata != NULL );
8599
8600 /* do not add rows if we do not separate */
8601 if ( ! conshdlrdata->sepaalternativelp )
8602 return SCIP_OKAY;
8603
8604 SCIPdebugMsg(scip, "Adding row <%s> to alternative LP.\n", SCIProwGetName(row));
8605
8606 /* add row directly to alternative polyhedron */
8607 SCIP_CALL( addAltLPRow(scip, conshdlr, row, 0.0, &colindex) );
8608 }
8609
8610 return SCIP_OKAY;
8611 }
8612