All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
cons_indicator.c
Go to the documentation of this file.
29 * @note The constraint only implements an implication not an equivalence, i.e., it does not ensure
32 * This constraint is equivalent to a linear constraint \f$ax - s \leq b\f$ and an SOS1 constraint on
42 * handler adds an inequality for each indicator constraint. We assume that this system (with added
43 * slack variables) is \f$ Ax - s \leq b \f$, where \f$ x \f$ are the original variables and \f$ s
44 * \f$ are the slack variables added by the indicator constraint. Variables \f$ y \f$ are the binary
70 * while the system \f$Dx + Cy \leq f\f$ contains all other inequalities (which are ignored in the
85 * It turns out that the vertices of \f$P\f$ correspond to minimal infeasible subsystems of \f$A x
86 * \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
100 * The first step in the separation heuristic is to apply the transformation \f$\bar{y} = 1 - y\f$, which
107 * The basic idea is to use the current solution to the LP relaxation and use it as the objective,
118 * Since each indicator constraint adds a linear constraint to the formulation, preprocessing of the
130 * Note again that the requirement \f$s \leq 0\f$ leads to an infeasible system. Consider now the
131 * preprocessing of the linear constraint (aggregation, bound strengthening, etc.) and assume that
140 * Note that we forbid multi-aggregation of the \f$s\f$ variables in order to be able to change their
157 * where the second form arises by substituting \f$v \geq 0\f$. A closer look at this system reveals
160 * - (Multi-)Aggregation of variables \f$x\f$ will remove these variables from the formulation, such that
164 * nonnegativity constraints, only if the corresponding slack variables appear in an aggregation.
170 * corresponding to an indicator constraint becomes redundant and is deleted. From this we cannot
171 * conclude that the indicator constraint is redundant as well (i.e. always fulfilled), because the
173 * (linear) constraints. Thus, we have to rely on the dual presolving of the linear constraints to
174 * detect this case: If the linear constraint is really redundant, i.e., is always fulfilled, it is
175 * deleted and the slack variable can be fixed to 0. In this case, the indicator constraint can be
178 * @todo Accept arbitrary ranged linear constraints as input (in particular: equations). Internally
179 * create two indicator constraints or correct alternative polyhedron accordingly (need to split the
182 * @todo Treat variable upper bounds in a special way: Do not create the artificial slack variable,
185 * @todo Turn off separation if the alternative polyhedron is infeasible and updateBounds is false.
187 * @todo Improve parsing of indicator constraint in CIP-format. Currently, we have to rely on a particular name, i.e.,
188 * the slack variable has to start with "indslack" and end with the name of the corresponding linear constraint.
192 * @todo Check whether using an objective cutoff can be integrated into the alternative polyhedron.
195 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
213 #define CONSHDLR_ENFOPRIORITY -100 /**< priority of the constraint handler for constraint enforcing */
214 #define CONSHDLR_CHECKPRIORITY -1000000 /**< priority of the constraint handler for checking feasibility */
215 #define CONSHDLR_SEPAFREQ 10 /**< frequency for separating cuts; zero means to separate only in the root node */
216 #define CONSHDLR_PROPFREQ 1 /**< frequency for propagating domains; zero means only preprocessing propagation */
217 #define CONSHDLR_EAGERFREQ 100 /**< frequency for using all instead of only the useful constraints in separation,
219 #define CONSHDLR_MAXPREROUNDS -1 /**< maximal number of presolving rounds the constraint handler participates in (-1: no limit) */
220 #define CONSHDLR_DELAYSEPA FALSE /**< should separation method be delayed, if other separators found cuts? */
221 #define CONSHDLR_DELAYPROP FALSE /**< should propagation method be delayed, if other propagators found reductions? */
222 #define CONSHDLR_DELAYPRESOL FALSE /**< should presolving method be delayed, if other presolvers found reductions? */
223 #define CONSHDLR_NEEDSCONS TRUE /**< should the constraint handler be skipped, if no constraints are available? */
244 #define DEFAULT_MAXCOUPLINGVALUE 1e4 /**< maximum coefficient for binary variable in coupling constraint */
245 #define DEFAULT_ADDCOUPLINGCONS FALSE /**< Add initial coupling inequalities as linear constraints, if 'addcoupling' is true? */
246 #define DEFAULT_SEPACOUPLINGCUTS FALSE /**< Should the coupling inequalities be separated dynamically? */
247 #define DEFAULT_SEPACOUPLINGLOCAL FALSE /**< Allow to use local bounds in order to separated coupling inequalities? */
248 #define DEFAULT_SEPACOUPLINGVALUE 1e4 /**< maximum coefficient for binary variable in separated coupling constraint */
250 #define DEFAULT_TRYSOLFROMCOVER FALSE /**< Try to construct a feasible solution from a cover? */
253 #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) */
255 #define DEFAULT_MAXSEPACUTSROOT 2000 /**< maximal number of cuts separated per separation round in the root node */
256 #define DEFAULT_REMOVEINDICATORS FALSE /**< Remove indicator constraint if corresponding variable bound constraint has been added? */
257 #define DEFAULT_GENERATEBILINEAR FALSE /**< Do not generate indicator constraint, but a bilinear constraint instead? */
258 #define DEFAULT_SCALESLACKVAR FALSE /**< Scale slack variable coefficient at construction time? */
259 #define DEFAULT_NOLINCONSCONT FALSE /**< decompose problem - do not generate linear constraint if all variables are continuous */
260 #define DEFAULT_TRYSOLUTIONS TRUE /**< Try to make solutions feasible by setting indicator variables? */
261 #define DEFAULT_ENFORCECUTS FALSE /**< In enforcing try to generate cuts (only if sepaalternativelp is true)? */
263 #define DEFAULT_ADDOPPOSITE FALSE /**< Add opposite inequality in nodes in which the binary variable has been fixed to 0? */
264 #define DEFAULT_CONFLICTSUPGRADE FALSE /**< Try to upgrade bounddisjunction conflicts by replacing slack variables? */
265 #define DEFAULT_FORCERESTART FALSE /**< force restart if we have a max FS instance and gap is 1? */
266 #define DEFAULT_RESTARTFRAC 0.9 /**< fraction of binary variables that need to be fixed before restart occurs (in forcerestart) */
270 #define OBJEPSILON 0.001 /**< value to add to objective in alt. LP if the binary variable is 1 to get small IISs */
271 #define SEPAALTTHRESHOLD 10 /**< only separate IIS cuts if the number of separated coupling cuts is less than this value */
284 unsigned int slacktypechecked:1; /**< whether it has been checked to convert the slack variable to be implicit integer */
295 SCIP_Bool objindicatoronly; /**< whether the objective is nonzero only for indicator variables */
298 int nrows; /**< # rows in the alt. LP corr. to original variables in linear constraints and slacks */
302 SCIP_HASHMAP* lbhash; /**< hash map from variable to index of lower bound column in alternative LP */
303 SCIP_HASHMAP* ubhash; /**< hash map from variable to index of upper bound column in alternative LP */
312 SCIP_Bool addcoupling; /**< whether the coupling inequalities should be added at the beginning */
313 SCIP_Bool addcouplingcons; /**< whether coupling inequalities should be variable bounds, if 'addcoupling' is true*/
315 SCIP_Bool sepacouplinglocal; /**< Allow to use local bounds in order to separated coupling inequalities? */
316 SCIP_Bool removeindicators; /**< remove indicator constraint if corresponding variable bound constraint has been added? */
317 SCIP_Bool updatebounds; /**< whether the bounds of the original variables should be changed for separation */
319 SCIP_Bool enforcecuts; /**< in enforcing try to generate cuts (only if sepaalternativelp is true) */
321 SCIP_Bool addopposite; /**< Add opposite inequality in nodes in which the binary variable has been fixed to 0? */
322 SCIP_Bool generatebilinear; /**< do not generate indicator constraint, but a bilinear constraint instead */
324 SCIP_Bool conflictsupgrade; /**< Try to upgrade bounddisjunction conflicts by replacing slack variables? */
330 SCIP_Real maxcouplingvalue; /**< maximum coefficient for binary variable in initial coupling constraint */
331 SCIP_Real sepacouplingvalue; /**< maximum coefficient for binary variable in separated coupling constraint */
332 SCIP_Real maxconditionaltlp; /**< maximum estimated condition number of the alternative LP to trust its solution */
333 SCIP_Real restartfrac; /**< fraction of binary variables that need to be fixed before restart occurs (in forcerestart) */
336 SCIP_CONS** addlincons; /**< additional linear constraints that should be added to the alternative LP */
344 SCIP_Bool nolinconscont; /**< decompose problem - do not generate linear constraint if all variables are continuous */
444 * - If we have a max FS instance, i.e., the objective is 1 for indicator variables and 0 otherwise,
447 * - If a large fraction of the binary indicator variables have been globally fixed, it makes sense
499 if ( conshdlrdata->nbinvarszero > (int) ((SCIP_Real) conshdlrdata->ninitconss * conshdlrdata->restartfrac) )
502 "Forcing restart, since %d binary variables among %d have been fixed.\n", conshdlrdata->nbinvarszero, conshdlrdata->ninitconss);
508 SCIP_CALL( SCIPdropEvent(scip, SCIP_EVENTTYPE_BESTSOLFOUND, eventhdlr, (SCIP_EVENTDATA*) conshdlrdata, -1) );
525 if ( SCIPisEQ(scip, REALABS(SCIPgetPrimalbound(scip) - SCIPgetDualbound(scip)), conshdlrdata->minabsobj) )
527 SCIPverbMessage(scip, SCIP_VERBLEVEL_NORMAL, NULL, "Forcing restart, since the absolute gap is %f.\n", conshdlrdata->minabsobj);
531 if ( SCIPfindBranchrule(scip, "inference") != NULL && !SCIPisParamFixed(scip, "branching/inference/priority") )
537 SCIP_CALL( SCIPdropEvent(scip, SCIP_EVENTTYPE_BESTSOLFOUND, eventhdlr, (SCIP_EVENTDATA*) conshdlrdata, -1) );
553 /** destructor of conflict handler to free conflict handler data (called when SCIP is exiting) */
618 if ( SCIPvarGetType(var) == SCIP_VARTYPE_IMPLINT || SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS )
666 SCIPdebugMessage(" <%s> %s %g\n", SCIPvarGetName(var), SCIPbdchginfoGetBoundtype(bdchginfos[i]) == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=",
670 if ( (SCIPvarGetType(var) == SCIP_VARTYPE_IMPLINT || SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS) && strstr(SCIPvarGetName(var), "indslack") != NULL )
718 (void) SCIPsnprintf(consname, SCIP_MAXSTRLEN, "cf%d_%"SCIP_LONGINT_FORMAT, SCIPgetNRuns(scip), SCIPgetNConflictConssApplied(scip));
770 SCIPwarningMessage(scip, "Cannot change parameter <%s> stage %d - reset to old value %s.\n", name, SCIPgetStage(scip), oldvalue ? "true" : "false");
800 SCIP_CALL( checkParam(scip, param, "constraints/indicator/sepaalternativelp", conshdlrdata->sepaalternativelp, &value) );
804 SCIP_CALL( checkParam(scip, param, "constraints/indicator/forcerestart", conshdlrdata->forcerestart, &value) );
808 SCIP_CALL( checkParam(scip, param, "constraints/indicator/nolinconscont", conshdlrdata->nolinconscont, &value) );
818 /** Check that indicator constraints corresponding to nonnegative entries in @a vector are infeasible in original problem
853 SCIP_CALL( SCIPhashmapCreate(&varhash, SCIPblkmem(scip), SCIPcalcHashtableSize(10 * SCIPgetNVars(scip))) );
863 if ( consdata->colindex >= 0 && (! SCIPisFeasZero(scip, vector[consdata->colindex]) || ! SCIPconsIsEnabled(conss[c])) )
908 /* SCIPdebugMessage("slack variable aggregated (scalar: %f, constant: %f)\n", scalar, constant); */
960 /* SCIPdebugMessage("Inserted variable <%s> into hashmap (%d).\n", SCIPvarGetName(var), nvars); */
1121 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &conshdlrdata->addlincons, conshdlrdata->maxaddlincons, newsize) );
1163 SCIP_CALL( SCIPhashmapCreate(&conshdlrdata->varhash, SCIPblkmem(scip), SCIPcalcHashtableSize(10 * SCIPgetNVars(scip))) );
1164 SCIP_CALL( SCIPhashmapCreate(&conshdlrdata->lbhash, SCIPblkmem(scip), SCIPcalcHashtableSize(10 * SCIPgetNVars(scip))) );
1165 SCIP_CALL( SCIPhashmapCreate(&conshdlrdata->ubhash, SCIPblkmem(scip), SCIPcalcHashtableSize(10 * SCIPgetNVars(scip))) );
1168 SCIP_CALL( SCIPlpiCreate(&conshdlrdata->altlp, SCIPgetMessagehdlr(scip), "altlp", SCIP_OBJSEN_MINIMIZE) );
1249 if ( ( ! SCIPlpiIsInfinity(lp, -lb[j]) && ! SCIPisFeasZero(scip, lb[j])) || (! SCIPlpiIsInfinity(lp, ub[j]) && ! SCIPisFeasZero(scip, ub[j])) )
1944 /* to avoid trivial rows: only add row corresponding to slack variable if it appears outside its own constraint */
1956 SCIP_CALL( SCIPhashmapInsert(conshdlrdata->slackhash, var, (void*) (size_t) conshdlrdata->nrows) );
1957 assert( conshdlrdata->nrows == (int) (size_t) SCIPhashmapGetImage(conshdlrdata->slackhash, var) );
1958 SCIPdebugMessage("Inserted slack variable <%s> into hashmap (row: %d).\n", SCIPvarGetName(var), conshdlrdata->nrows);
1964 assert( conshdlrdata->nrows >= (int) (size_t) SCIPhashmapGetImage(conshdlrdata->slackhash, var) );
1977 SCIP_CALL( SCIPhashmapInsert(conshdlrdata->varhash, var, (void*) (size_t) conshdlrdata->nrows) );
1978 assert( conshdlrdata->nrows == (int) (size_t) SCIPhashmapGetImage(conshdlrdata->varhash, var) );
1979 SCIPdebugMessage("Inserted variable <%s> into hashmap (row: %d).\n", SCIPvarGetName(var), conshdlrdata->nrows);
2010 SCIP_CALL( SCIPlpiAddRows(conshdlrdata->altlp, nNewRows, lhs, rhs, NULL, 0, NULL, NULL, NULL) );
2022 /* create a free variable for equations -> should only happen for additional linear constraints */
2029 SCIP_CALL( SCIPlpiAddCols(conshdlrdata->altlp, 1, obj, lb, ub, NULL, cnt, matbeg, matind, matval) );
2031 /* add columns corresponding to bounds of original variables - no bounds needed for slack vars */
2057 SCIP_CALL( SCIPhashmapInsert(conshdlrdata->lbhash, var, (void*) (size_t) (nCols + 1 + nNewCols)) );
2059 SCIPdebugMessage("added column corr. to lower bound (%f) of variable <%s> to alternative polyhedron (col: %d).\n",
2083 SCIP_CALL( SCIPhashmapInsert(conshdlrdata->ubhash, var, (void*) (size_t) (nCols + 1 + nNewCols)) );
2085 SCIPdebugMessage("added column corr. to upper bound (%f) of variable <%s> to alternative polyhedron (col: %d).\n",
2094 SCIP_CALL( SCIPlpiAddCols(conshdlrdata->altlp, nNewCols, obj, lb, ub, NULL, cnt, matbeg, matind, matval) );
2258 SCIP_CALL( SCIPhashmapInsert(conshdlrdata->slackhash, var, (void*) (size_t) conshdlrdata->nrows) );
2259 assert( conshdlrdata->nrows == (int) (size_t) SCIPhashmapGetImage(conshdlrdata->slackhash, var) );
2260 SCIPdebugMessage("Inserted slack variable <%s> into hashmap (row: %d).\n", SCIPvarGetName(var), conshdlrdata->nrows);
2266 assert( conshdlrdata->nrows >= (int) (size_t) SCIPhashmapGetImage(conshdlrdata->slackhash, var) );
2278 SCIP_CALL( SCIPhashmapInsert(conshdlrdata->varhash, var, (void*) (size_t) conshdlrdata->nrows) );
2279 assert( conshdlrdata->nrows == (int) (size_t) SCIPhashmapGetImage(conshdlrdata->varhash, var) );
2280 SCIPdebugMessage("Inserted variable <%s> into hashmap (row: %d).\n", SCIPvarGetName(var), conshdlrdata->nrows);
2311 SCIP_CALL( SCIPlpiAddRows(conshdlrdata->altlp, nNewRows, lhs, rhs, NULL, 0, NULL, NULL, NULL) );
2329 SCIP_CALL( SCIPlpiAddCols(conshdlrdata->altlp, 1, obj, lb, ub, NULL, cnt, matbeg, matind, matval) );
2331 /* add columns corresponding to bounds of original variables - no bounds needed for slack vars */
2357 SCIP_CALL( SCIPhashmapInsert(conshdlrdata->lbhash, var, (void*) (size_t) (nCols + 1 + nNewCols)) );
2359 SCIPdebugMessage("added column corr. to lower bound (%f) of variable <%s> to alternative polyhedron (col: %d).\n",
2383 SCIP_CALL( SCIPhashmapInsert(conshdlrdata->ubhash, var, (void*) (size_t) (nCols + 1 + nNewCols)) );
2385 SCIPdebugMessage("added column corr. to upper bound (%f) of variable <%s> to alternative polyhedron (col: %d).\n",
2394 SCIP_CALL( SCIPlpiAddCols(conshdlrdata->altlp, nNewCols, obj, lb, ub, NULL, cnt, matbeg, matind, matval) );
2466 * If @a primal is false we assume that the problem is <em>dual feasible</em>, e.g., the problem
2469 * This is the workhorse for all methods that have to solve the alternative LP. We try in several
2542 SCIPdebugMessage("estim. condition number of basis matrix (%e) exceeds maximal allowance (%e).\n", condition, maxcondition);
2550 SCIPdebugMessage("estim. condition number of basis matrix (%e) is below maximal allowance (%e).\n", condition, maxcondition);
2569 SCIPwarningMessage(scip, "The dual simplex produced a primal ray. Retrying with primal ...\n");
2600 SCIPwarningMessage(scip, "Primal simplex returned with unknown status: %d\n", SCIPlpiGetInternalStatus(lp));
2604 SCIPwarningMessage(scip, "Dual simplex returned with unknown status: %d\n", SCIPlpiGetInternalStatus(lp));
2621 * number of IISs a variable is contained in. The corresponding IIS inequalities are added to the
2625 * corresponding to @a S are fixed. Furthermore @c xVal_ should contain the current LP solution.
2684 SCIP_CALL( checkAltLPInfeasible(scip, lp, conshdlrdata->maxconditionaltlp, TRUE, &infeasible, error) );
2688 SCIP_CALL( checkAltLPInfeasible(scip, lp, conshdlrdata->maxconditionaltlp, FALSE, &infeasible, error) );
2698 /* Check whether we want to try to construct a feasible solution: there should be no integer/binary variables
2699 * except the indicator variables. Thus, there should be no integral variables and the number of indicator
2720 /* Fix binary variables not in cover to 1 and corresponding slack variables to 0. The other binary variables
2749 /* Cannot call SCIPpropagateProbing() within separation - thus, we had to fix the slack variables above. */
2765 /* check solution for feasibility, and add it to solution store if possible neither integrality nor
2766 * feasibility of LP rows has to be checked, because this is already done in the intshifting heuristic
2829 SCIPdebugMessage(" size: %4d add %4d with objective value %f and alt-LP solution value %g (IIS size: %d)\n", *size, candidate, candObj, primsol[SCIPconsGetData(conss[candidate])->colindex], sizeIIS);
2885 SCIP_CALL( SCIPcreateConsLogicor(scip, &cons, "iis", cnt, vars, FALSE, TRUE, TRUE, TRUE, TRUE, isLocal, FALSE, TRUE, removable, FALSE) );
2904 SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, conshdlr, "iis", -SCIPinfinity(scip), (SCIP_Real) (sizeIIS - 1), isLocal, FALSE, removable) );
3008 SCIPerrorMessage("Indicator variable <%s> is not binary %d.\n", SCIPvarGetName(var), SCIPvarGetType(var));
3015 SCIP_CALL( SCIPcatchVarEvent(scip, var, SCIP_EVENTTYPE_BOUNDCHANGED, eventhdlrbound, (SCIP_EVENTDATA*)*consdata, NULL) );
3022 SCIP_CALL( SCIPcatchVarEvent(scip, var, SCIP_EVENTTYPE_GBDCHANGED, eventhdlrrestart, (SCIP_EVENTDATA*) conshdlrdata, NULL) );
3038 SCIP_CALL( SCIPcatchVarEvent(scip, var, SCIP_EVENTTYPE_BOUNDCHANGED, eventhdlrbound, (SCIP_EVENTDATA*)*consdata, NULL) );
3046 if ( conshdlrdata->sepaalternativelp && SCIPgetStage(scip) >= SCIP_STAGE_INITSOLVE && lincons != NULL )
3062 SCIPdebugMessage("constraint <%s> has %d variables fixed to be nonzero.\n", consname, (*consdata)->nfixednonzero);
3117 SCIPdebugMessage("Insert coupling varbound constraint for indicator constraint <%s> (coeff: %f).\n", SCIPconsGetName(conss[c]), ub);
3124 SCIP_CALL( SCIPcreateConsVarbound(scip, &cons, name, consdata->slackvar, consdata->binvar, ub, -SCIPinfinity(scip), ub,
3193 SCIPdebugMessage("The problem is infeasible: binary and slack variable are fixed to be nonzero.\n");
3223 SCIPdebugMessage("Presolving <%s>: Binary variable fixed to 0, deleting indicator constraint.\n", SCIPconsGetName(cons));
3243 SCIPdebugMessage("Presolving <%s>: Slack variable fixed to nonzero.\n", SCIPconsGetName(cons));
3248 SCIPdebugMessage("The problem is infeasible: binary and slack variable are fixed to be nonzero.\n");
3299 SCIPdebugMessage("Presolving <%s> - dual reduction: Slack variable fixed to 0, fix binary variable to 1.\n", SCIPconsGetName(cons));
3317 SCIPdebugMessage("Presolving <%s> - dual reduction: Slack variable fixed to 0, fix binary variable to 0.\n", SCIPconsGetName(cons));
3327 SCIPdebugMessage("Presolving <%s>: Slack variable fixed to zero, delete redundant indicator constraint.\n", SCIPconsGetName(cons));
3358 SCIPdebugMessage("Indicator variable <%s> is aggregated and replaced by active/negated variable <%s>.\n", SCIPvarGetName(consdata->binvar), SCIPvarGetName(var) );
3362 SCIP_CALL( SCIPdropVarEvent(scip, consdata->binvar, SCIP_EVENTTYPE_BOUNDCHANGED, conshdlrdata->eventhdlrbound, (SCIP_EVENTDATA*) consdata, -1) );
3363 SCIP_CALL( SCIPcatchVarEvent(scip, var, SCIP_EVENTTYPE_BOUNDCHANGED, conshdlrdata->eventhdlrbound, (SCIP_EVENTDATA*) consdata, NULL) );
3387 if ( var != consdata->slackvar && boundtype == SCIP_BOUNDTYPE_LOWER && SCIPisGE(scip, bound, 0.0) )
3390 SCIPdebugMessage("Slack variable <%s> is aggregated and replaced by active variable <%s>.\n", SCIPvarGetName(consdata->slackvar), SCIPvarGetName(var) );
3394 SCIP_CALL( SCIPdropVarEvent(scip, consdata->slackvar, SCIP_EVENTTYPE_BOUNDCHANGED, conshdlrdata->eventhdlrbound, (SCIP_EVENTDATA*) consdata, -1) );
3395 SCIP_CALL( SCIPcatchVarEvent(scip, var, SCIP_EVENTTYPE_BOUNDCHANGED, conshdlrdata->eventhdlrbound, (SCIP_EVENTDATA*) consdata, NULL) );
3411 /* if the lower bound is transformed to an upper bound, we have "y = 1 -> 1 - y = 0", i.e., the constraint is redundant */
3414 SCIPdebugMessage("Slack variable <%s> is aggregated to negated indicator variable <%s> -> constraint redundant.\n",
3427 /* 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 */
3428 SCIPdebugMessage("Slack variable <%s> is aggregated to the indicator variable <%s> -> fix indicator variable to 0.\n",
3493 SCIPdebugMessage("the node is infeasible, both the slack variable and the binary variable are fixed to be nonzero.\n");
3519 /* increase age of constraint; age is reset to zero, if a conflict or a propagation was found */
3531 SCIPdebugMessage("binary variable <%s> is fixed to be nonzero, fixing slack variable <%s> to 0.\n",
3535 SCIP_CALL( SCIPinferVarUbCons(scip, consdata->slackvar, 0.0, cons, 0, FALSE, &infeasible, &tightened) );
3548 SCIPdebugMessage("slack variable <%s> is fixed to be nonzero, fixing binary variable <%s> to 0.\n",
3552 SCIP_CALL( SCIPinferVarUbCons(scip, consdata->binvar, 0.0, cons, 1, FALSE, &infeasible, &tightened) );
3647 /* constraint is initial, separated, not enforced, not checked, propagated, local, not modifiable, dynamic, removable */
3651 SCIPdebugMessage("Binary variable <%s> fixed to 0. Adding opposite linear inequality.\n", SCIPvarGetName(consdata->binvar));
3691 SCIPdebugMessage("Propagating <%s> - dual reduction: Slack variable fixed to 0, fix binary variable to 1.\n", SCIPconsGetName(cons));
3696 /* Make sure that the other case does not occur, since we are not sure whether SCIPinferVarLbCons() directly changes the bounds. */
3709 SCIPdebugMessage("Propagating <%s> - dual reduction: Slack variable fixed to 0, fix binary variable to 0.\n", SCIPconsGetName(cons));
3719 SCIPdebugMessage("Slack variable fixed to zero, delete redundant indicator constraint <%s>.\n", SCIPconsGetName(cons));
3748 * This is a variant of the enforcement method that generates cuts/constraints via the alternative
3827 SCIP_CALL( extendToCover(scip, conshdlr, conshdlrdata, lp, sol, conshdlrdata->removable, genlogicor, nconss, conss, S, &size, &value, &error, &nCuts) );
3830 /* return with an error if no cuts have been produced and and error occured in extendToCover() */
3915 SCIP_CALL( propIndicator(scip, conss[c], consdata, conshdlrdata->dualreductions, conshdlrdata->addopposite, &cutoff, &cnt) );
3918 SCIPdebugMessage("propagation in enforcing <%s> detected cutoff.\n", SCIPconsGetName(conss[c]));
3924 SCIPdebugMessage("propagation in enforcing <%s> reduced domains: %d.\n", SCIPconsGetName(conss[c]), cnt);
3933 if ( ! SCIPisFeasZero(scip, SCIPgetSolVal(scip, NULL, binvar)) && ! SCIPisFeasZero(scip, valSlack) )
3954 /* if some constraint has a linear constraint that is not active, we need to check feasibility via the alternative polyhedron */
3994 SCIPdebugMessage("Branching on constraint <%s> (slack value: %f).\n", SCIPconsGetName(branchCons), maxSlack);
4093 rounds < conshdlrdata->roundingrounds && threshold >= conshdlrdata->roundingminthres && *nGen < maxsepacuts;
4144 SCIP_CALL( extendToCover(scip, conshdlr, conshdlrdata, lp, sol, conshdlrdata->removable, conshdlrdata->genlogicor, nconss, conss, S, &size, &value, &error, &nCuts) );
4173 * We first check whether coupling inequalities can be separated (if required). If not enough of
4249 activity = SCIPgetSolVal(scip, sol, consdata->slackvar) + ub * SCIPgetSolVal(scip, sol, consdata->binvar) - ub;
4261 SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, SCIPconsGetHdlr(conss[c]), name, -SCIPinfinity(scip), ub, islocal, FALSE, conshdlrdata->removable) );
4268 SCIPdebugMessage("Separated coupling inequality for indicator constraint <%s> (coeff: %f).\n", SCIPconsGetName(conss[c]), ub);
4390 /** deinitialization method of constraint handler (called before transformed problem is freed) */
4411 /** destructor of constraint handler to free constraint handler data (called when SCIP is exiting) */
4431 /* if problem was not yet transformed the array may need to be freed, because we did not call the EXIT callback */
4444 /** solving process initialization method of constraint handler (called when branch and bound process is about to begin) */
4462 SCIP_CALL( SCIPhashmapCreate(&conshdlrdata->slackhash, SCIPblkmem(scip), SCIPcalcHashtableSize(10 * SCIPgetNVars(scip))) );
4480 SCIP_CALL( SCIPhashmapInsert(conshdlrdata->slackhash, consdata->slackvar, (void*) (size_t) (INT_MAX)) );
4498 /* SCIPdebugMessage("Initializing indicator constraint <%s>.\n", SCIPconsGetName(conss[c]) ); */
4511 SCIP_CALL( addAltLPConstraint(scip, conshdlr, consdata->lincons, consdata->slackvar, 1.0, &consdata->colindex) );
4512 SCIPdebugMessage("Column index for <%s>: %d\n", SCIPconsGetName(conss[c]), consdata->colindex);
4538 SCIP_CALL( SCIPcreateNlRow(scip, &nlrow, SCIPconsGetName(conss[c]), 0.0, 0, NULL, NULL, 2, quadvars, 1, &quadelem, NULL, 0.0, 0.0) );
4562 /* get transformed constraint - since it is needed only here, we do not store the information */
4567 /* @todo check when exactly the transformed constraint does not exist - SCIPisActive() does not suffice */
4583 /* if no stored linear constraints are available, possibly collect other linear constraints; we only use linear
4584 * constraints, since most other constraints involve integral variables, and in this context we will likely
4612 SCIPdebugMessage("Adding column for linear constraint <%s> to alternative LP ...\n", SCIPconsGetName(cons));
4618 SCIPdebugMessage("Added %d additional columns from linear constraints to alternative LP.\n", cnt);
4676 /* if presolving detected infeasibility it might be that the binary variables are not active */
4701 /* if all variables have integral objective and only indicator variables have nonzero objective */
4713 SCIP_CALL( SCIPcatchEvent(scip, SCIP_EVENTTYPE_BESTSOLFOUND, conshdlrdata->eventhdlrrestart, (SCIP_EVENTDATA*) conshdlrdata, NULL) );
4724 /** solving process deinitialization method of constraint handler (called before branch and bound process data is freed) */
4821 if ( SCIPgetStage(scip) >= SCIP_STAGE_TRANSFORMING && SCIPgetStage(scip) <= SCIP_STAGE_SOLVED )
4826 SCIP_CALL( SCIPdropVarEvent(scip, (*consdata)->binvar, SCIP_EVENTTYPE_BOUNDCHANGED, conshdlrdata->eventhdlrbound,
4828 SCIP_CALL( SCIPdropVarEvent(scip, (*consdata)->slackvar, SCIP_EVENTTYPE_BOUNDCHANGED, conshdlrdata->eventhdlrbound,
4834 SCIP_CALL( SCIPdropVarEvent(scip, (*consdata)->binvar, SCIP_EVENTTYPE_GBDCHANGED, conshdlrdata->eventhdlrrestart,
4840 /* Can there be cases where lincons is NULL, e.g., if presolve found the problem infeasible? */
4883 SCIPerrorMessage("The indicator constraint <%s> needs a slack variable.\n", SCIPconsGetName(sourcecons));
4890 SCIPerrorMessage("The indicator constraint <%s> needs a linear constraint variable.\n", SCIPconsGetName(sourcecons));
4898 SCIP_CALL( consdataCreate(scip, conshdlr, conshdlrdata, SCIPconsGetName(sourcecons), &consdata, conshdlrdata->eventhdlrbound,
4899 conshdlrdata->eventhdlrrestart, sourcedata->binvar, sourcedata->slackvar, sourcedata->lincons, sourcedata->linconsactive) );
4915 /** presolving initialization method of constraint handler (called when presolving is about to begin) */
4973 * For an indicator constraint with binary variable \f$y\f$ and slack variable \f$s\f$ the coupling
4974 * inequality \f$s \le M (1-y)\f$ (equivalently: \f$s + M y \le M\f$) is inserted, where \f$M\f$ is
4976 * inserted. Depending on the parameter @a addcouplingcons we add a variable upper bound or a row
4979 * @warning We can never delete linear constraints, because we need them to get the right values
5043 SCIP_CALL( SCIPaddVarImplication(scip, consdata->binvar, TRUE, consdata->slackvar, SCIP_BOUNDTYPE_UPPER, 0.0,
5088 /* something is strange if the slack variable does not appear in the linear constraint (possibly because it is an artificial constraint) */
5100 SCIP_CALL( presolRoundIndicator(scip, conshdlrdata, cons, consdata, conshdlrdata->dualreductions, &cutoff, &success, ndelconss, nfixedvars) );
5113 noReductions = nnewfixedvars == 0 && nnewaggrvars == 0 && nnewchgvartypes == 0 && nnewchgbds == 0
5117 if ( noReductions && *result != SCIP_SUCCESS && conshdlrdata->addcouplingcons && ! conshdlrdata->addedcouplingcons )
5134 SCIPdebugMessage("Presolved %d constraints (fixed %d variables, removed %d variables, and deleted %d constraints).\n",
5141 /** LP initialization method of constraint handler (called before the initial LP relaxation at a node is solved)
5143 * For an indicator constraint with binary variable \f$y\f$ and slack variable \f$s\f$ the coupling
5144 * inequality \f$s \le M (1-y)\f$ (equivalently: \f$s + M y \le M\f$) is inserted, where \f$M\f$ is
5145 * an upper bound on the value of \f$s\f$. If \f$M\f$ is too large the inequality is not inserted.
5183 SCIPdebugMessage("Adding initial rows for indicator constraint <%s>.\n", SCIPconsGetName(conss[c]));
5207 SCIPdebugMessage("Insert coupling varbound constraint for indicator constraint <%s> (coeff: %f).\n", SCIPconsGetName(conss[c]), ub);
5209 SCIP_CALL( SCIPcreateConsVarbound(scip, &cons, name, consdata->slackvar, consdata->binvar, ub, -SCIPinfinity(scip), ub,
5220 SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, conshdlr, name, -SCIPinfinity(scip), ub, FALSE, FALSE, FALSE) );
5227 SCIPdebugMessage("Insert coupling inequality for indicator constraint <%s> (coeff: %f).\n", SCIPconsGetName(conss[c]), ub);
5300 SCIP_CALL( enforceIndicators(scip, conshdlr, nconss, conss, conshdlrdata->genlogicor, result) );
5350 SCIPdebugMessage("Checking %d indicator constraints <%s>.\n", nconss, SCIPconshdlrGetName(conshdlr) );
5355 /* copy solution if it makes sense (will send solution to trysol heuristic in any case (see below) */
5356 if ( SCIPgetStage(scip) > SCIP_STAGE_PROBLEM && SCIPgetStage(scip) < SCIP_STAGE_SOLVED && conshdlrdata->trysolutions && conshdlrdata->heurtrysol != NULL )
5385 assert( checkintegrality || SCIPisFeasIntegral(scip, SCIPgetSolVal(scip, sol, consdata->binvar)) );
5426 /* if some linear constraints are not active, we need to check feasibility via the alternative polyhedron */
5437 /* the check maybe called before we have build the alternative polyhedron -> return SCIP_INFEASIBLE */
5468 assert( checkintegrality || SCIPisFeasIntegral(scip, SCIPgetSolVal(scip, sol, consdata->binvar)) );
5480 SCIP_CALL( checkAltLPInfeasible(scip, lp, conshdlrdata->maxconditionaltlp, TRUE, &infeasible, &error) );
5544 SCIPdebugMessage("Start propagation of constraint handler <%s>.\n", SCIPconshdlrGetName(conshdlr));
5568 SCIP_CALL( propIndicator(scip, cons, consdata, conshdlrdata->dualreductions, conshdlrdata->addopposite, &cutoff, &cnt) );
5576 SCIPdebugMessage("Propagated %d domains in constraint handler <%s>.\n", ngen, SCIPconshdlrGetName(conshdlr));
5586 * We check which bound changes were the reason for infeasibility. We use that @a inferinfo is 0 if
5587 * the binary variable has bounds that fix it to be nonzero (these bounds are the reason). Likewise
5603 SCIPdebugMessage("Propagation resolution method of indicator constraint <%s>.\n", SCIPconsGetName(cons));
5622 /* Use a weaker comparison to SCIPvarGetLbAtIndex here (i.e., SCIPisPositive instead of SCIPisFeasPositive),
5623 * because SCIPvarGetLbAtIndex might differ from the local bound at time bdchgidx by epsilon. */
5659 SCIPdebugMessage("%socking constraint <%s>.\n", (nlocksneg < 0) || (nlockspos < 0) ? "Unl" : "L", SCIPconsGetName(cons));
5780 SCIPwarningMessage(scip, "cannot create modifiable indicator constraint when trying to copy constraint <%s>,\n", SCIPconsGetName(sourcecons));
5791 /* if the constraint has been deleted -> create empty constraint (multi-aggregation might still contain slack variable, so indicator is valid) */
5794 SCIPdebugMessage("Linear constraint <%s> deleted! Create empty linear constraint.\n", SCIPconsGetName(sourceconsdata->lincons));
5796 SCIP_CALL( SCIPcreateConsLinear(scip, &targetlincons, "dummy", 0, NULL, NULL, 0.0, SCIPinfinity(scip),
5807 /* if copying scip after transforming the original instance before presolving, we need to correct the linear
5813 /* adjust the linear constraint in the original constraint (no need to release translincons) */
5822 SCIP_CALL( SCIPgetConsCopy(sourcescip, scip, sourcelincons, &targetlincons, conshdlrlinear, varmap, consmap, SCIPconsGetName(sourcelincons),
5823 SCIPconsIsInitial(sourcelincons), SCIPconsIsSeparated(sourcelincons), SCIPconsIsEnforced(sourcelincons), SCIPconsIsChecked(sourcelincons),
5824 SCIPconsIsPropagated(sourcelincons), SCIPconsIsLocal(sourcelincons), SCIPconsIsModifiable(sourcelincons), SCIPconsIsDynamic(sourcelincons),
5836 SCIP_CALL( SCIPgetVarCopy(sourcescip, scip, sourcebinvar, &targetbinvar, varmap, consmap, global, valid) );
5847 SCIP_CALL( SCIPgetVarCopy(sourcescip, scip, sourceslackvar, &targetslackvar, varmap, consmap, global, valid) );
5858 SCIP_CALL( SCIPcreateConsIndicatorLinCons(scip, cons, consname, targetbinvar, targetlincons, targetslackvar,
5863 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "could not copy linear constraint <%s>\n", SCIPconsGetName(sourcelincons));
5892 nargs = sscanf(str, " <%1023[^>]> = %d -> <%1023[^>]> = 0", binvarname, &zeroone, slackvarname);
5896 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "Syntax error: expected the following form: <var> = [0|1] -> <var> = 0.\n%s\n", str);
5903 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "Syntax error: expected the following form: <var> = [0|1] -> <var> = 0.\n%s\n", str);
5933 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "strange slack variable name: <%s>\n", slackvarname);
5950 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "while parsing indicator constraint <%s>: unknown linear constraint <indlin_%s> or <%s>.\n",
6074 SCIP_CALL( SCIPgetConsVars(scip, consdata->lincons, &(vars[nvars]), varssize - nvars, success) );
6079 /** constraint method of constraint handler which returns the number of variables (if possible) */
6145 SCIP_CALL( SCIPincludeEventhdlrBasic(scip, &(conshdlrdata->eventhdlrrestart), EVENTHDLR_RESTART_NAME, EVENTHDLR_RESTART_DESC,
6171 /* the following three variables cannot be initialized in the above method, because initConshdlrData() is also called
6172 * in the CONSINIT callback, but these variables might be used even before the is ccallback is called, so we would
6202 SCIP_CALL( SCIPsetConshdlrPresol(scip, conshdlr, consPresolIndicator, CONSHDLR_MAXPREROUNDS, CONSHDLR_DELAYPRESOL) );
6204 SCIP_CALL( SCIPsetConshdlrProp(scip, conshdlr, consPropIndicator, CONSHDLR_PROPFREQ, CONSHDLR_DELAYPROP,
6207 SCIP_CALL( SCIPsetConshdlrSepa(scip, conshdlr, consSepalpIndicator, consSepasolIndicator, CONSHDLR_SEPAFREQ,
6218 SCIP_CALL( SCIPincludeConflicthdlrBasic(scip, &conflicthdlr, CONFLICTHDLR_NAME, CONFLICTHDLR_DESC, CONFLICTHDLR_PRIORITY,
6271 "maximum estimated condition of the solution basis matrix of the alternative LP to be trustworthy (0.0 to disable check)",
6272 &conshdlrdata->maxconditionaltlp, TRUE, DEFAULT_MAXCONDITIONALTLP, 0.0, SCIP_REAL_MAX, NULL, NULL) );
6343 &conshdlrdata->sepaalternativelp_, TRUE, DEFAULT_SEPAALTERNATIVELP, paramChangedIndicator, NULL) );
6364 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
6375 SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP? Usually set to TRUE. */
6389 SCIP_Bool removable, /**< should the relaxation be removed from the LP due to aging or cleanup?
6391 SCIP_Bool stickingatnode /**< should the constraint always be kept at the node where it was added, even
6410 SCIPerrorMessage("Indicator constraint <%s> needs nonnegative number of variables in linear constraint.\n", name);
6429 SCIPerrorMessage("constraint handler <%s>: need parameter <sepaalternativelp> to be true if parameter <nolinconscont> is true.\n", CONSHDLR_NAME);
6435 SCIPerrorMessage("constraint handler <%s>: parameters <nolinconscont> and <generatebilinear> cannot both be true.\n", CONSHDLR_NAME);
6455 SCIP_CALL( SCIPcreateVar(scip, &slackvar, s, 0.0, SCIPinfinity(scip), 0.0, slackvartype, TRUE, FALSE,
6495 SCIP_CALL( SCIPcreateConsLinear(scip, &lincons, s, nvars, vars, vals, -SCIPinfinity(scip), rhs,
6500 /* create non-active linear constraint, which is neither initial, nor enforced, nor separated, nor checked */
6501 SCIP_CALL( SCIPcreateConsLinear(scip, &lincons, s, nvars, vars, vals, -SCIPinfinity(scip), rhs,
6525 /* check whether we should generate a bilinear constraint instead of an indicator constraint */
6531 SCIP_CALL( SCIPcreateConsQuadratic(scip, cons, name, 0, NULL, NULL, 1, &binvar, &slackvar, &val, 0.0, 0.0,
6538 SCIP_CALL( consdataCreate(scip, conshdlr, conshdlrdata, name, &consdata, conshdlrdata->eventhdlrbound, conshdlrdata->eventhdlrrestart,
6543 SCIP_CALL( SCIPcreateCons(scip, cons, name, conshdlr, consdata, initial, separate, enforce, check, propagate,
6555 * in its most basic version, i. e., all constraint flags are set to their basic value as explained for the
6556 * method SCIPcreateConsIndicator(); all flags can be set via SCIPsetConsFLAGNAME-methods in scip.h
6560 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
6581 /** creates and captures an indicator constraint with given linear constraint and slack variable
6586 * @note we assume that @a slackvar actually appears in @a lincons and we also assume that it takes
6589 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
6598 SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP? Usually set to TRUE. */
6612 SCIP_Bool removable, /**< should the relaxation be removed from the LP due to aging or cleanup?
6614 SCIP_Bool stickingatnode /**< should the constraint always be kept at the node where it was added, even
6652 SCIPerrorMessage("constraint handler <%s>: need parameter <sepaalternativelp> to be true if parameter <nolinconscont> is true.\n", CONSHDLR_NAME);
6693 /* check whether we should generate a bilinear constraint instead of an indicator constraint */
6700 SCIP_CALL( SCIPcreateConsQuadratic(scip, cons, name, 0, NULL, NULL, 1, &binvar, &slackvar, &val, 0.0, 0.0,
6707 SCIP_CALL( consdataCreate(scip, conshdlr, conshdlrdata, name, &consdata, conshdlrdata->eventhdlrbound, conshdlrdata->eventhdlrrestart,
6712 SCIP_CALL( SCIPcreateCons(scip, cons, name, conshdlr, consdata, initial, separate, enforce, check, propagate,
6719 /** creates and captures an indicator constraint with given linear constraint and slack variable
6720 * in its most basic version, i. e., all constraint flags are set to their basic value as explained for the
6721 * method SCIPcreateConsIndicator(); all flags can be set via SCIPsetConsFLAGNAME-methods in scip.h
6726 * @note we assume that @a slackvar actually appears in @a lincons and we also assume that it takes
6729 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
6731 * @see SCIPcreateConsIndicatorLinCons() for information about the basic constraint flag configuration
6733 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
6772 if ( SCIPvarGetType(consdata->slackvar) != SCIP_VARTYPE_CONTINUOUS && (! SCIPvarIsIntegral(var) || ! SCIPisIntegral(scip, val) ) )
6908 SCIPerrorMessage("Indicator variable <%s> is not binary %d.\n", SCIPvarGetName(binvar), SCIPvarGetType(binvar));
6916 SCIPerrorMessage("Cannot replace binary variable <%s> for indicator constraint <%s>.\n", SCIPvarGetName(binvar), SCIPconsGetName(cons));
6943 SCIP_CALL( SCIPcatchVarEvent(scip, var, SCIP_EVENTTYPE_BOUNDCHANGED, conshdlrdata->eventhdlrbound, (SCIP_EVENTDATA*) consdata, NULL) );
6949 SCIP_CALL( SCIPcatchVarEvent(scip, var, SCIP_EVENTTYPE_GBDCHANGED, conshdlrdata->eventhdlrrestart, (SCIP_EVENTDATA*) conshdlrdata, NULL) );
7012 /** Based on values of other variables, computes slack and binary variable to turn constraint feasible
7029 * second case. Thus, if \f$\sigma > 0\f$ and \f$s^* < 0\f$, the inequality cannot be satisfied by
7030 * a nonnegative value for the slack variable; in this case, we have to leave the values as they
7031 * are. If \f$\sigma < 0\f$ and \f$s^* > 0\f$, the solution violates the indicator constraint (we
7109 assert( SCIPisInfinity(scip, -SCIPgetLhsLinear(scip, lincons)) || SCIPisInfinity(scip, SCIPgetRhsLinear(scip, lincons)) );
7152 if ( ! SCIPisFeasEQ(scip, SCIPgetSolVal(scip, sol, slackvar), 0.0) && SCIPisFeasPositive(scip, SCIPvarGetLbLocal(slackvar)) )
7160 ( SCIPvarGetStatus(binvar) != SCIP_VARSTATUS_NEGATED || SCIPvarGetStatus(SCIPvarGetNegationVar(binvar)) != SCIP_VARSTATUS_FIXED ) )
7188 /* setting variable to 0 does not inrease objective -> check whether variable only occurs in the current constraint
7190 if ( SCIPvarGetNLocksDown(binvar) <= 0 && ! SCIPisFeasEQ(scip, SCIPgetSolVal(scip, sol, binvar), 0.0) )
7204 /** Based on values of other variables, computes slack and binary variable to turn all constraints feasible */
7251 /** adds additional linear constraint that is not connected with an indicator constraint, but can be used for separation */
7280 /** adds additional row that is not connected by an indicator constraint, but can be used for separation
|