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