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