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_sos1.c
17 * @ingroup DEFPLUGINS_CONS
18 * @brief constraint handler for SOS type 1 constraints
19 * @author Tobias Fischer
20 * @author Marc Pfetsch
21 *
22 * A specially ordered set of type 1 (SOS1) is a sequence of variables such that at most one
23 * variable is nonzero. The special case of two variables arises, for instance, from equilibrium or
24 * complementary conditions like \f$x \cdot y = 0\f$. Note that it is in principle allowed that a
25 * variables appears twice, but it then can be fixed to 0.
26 *
27 * This implementation of this constraint handler is based on classical ideas, see e.g.@n
28 * "Special Facilities in General Mathematical Programming System for
29 * Non-Convex Problems Using Ordered Sets of Variables"@n
30 * E. Beale and J. Tomlin, Proc. 5th IFORS Conference, 447-454 (1970)
31 *
32 *
33 * The order of the variables is determined as follows:
34 *
35 * - If the constraint is created with SCIPcreateConsSOS1() and weights are given, the weights
36 * determine the order (decreasing weights). Additional variables can be added with
37 * SCIPaddVarSOS1(), which adds a variable with given weight.
38 *
39 * - If an empty constraint is created and then variables are added with SCIPaddVarSOS1(), weights
40 * are needed and stored.
41 *
42 * - All other calls ignore the weights, i.e., if a nonempty constraint is created or variables are
43 * added with SCIPappendVarSOS1().
44 *
45 * The validity of the SOS1 constraints can be enforced by different branching rules:
46 *
47 * - If classical SOS branching is used, branching is performed on only one SOS1 constraint.
48 * Depending on the parameters, there are two ways to choose this branching constraint. Either
49 * the constraint with the most number of nonzeros or the one with the largest nonzero-variable
50 * weight. The later version allows the user to specify an order for the branching importance of
51 * the constraints. Constraint branching can also be turned off.
52 *
53 * - Another way is to branch on the neighborhood of a single variable @p i, i.e., in one branch
54 * \f$x_i\f$ is fixed to zero and in the other its neighbors from the conflict graph.
55 *
56 * - If bipartite branching is used, then we branch using complete bipartite subgraphs of the
57 * conflict graph, i.e., in one branch fix the variables from the first bipartite partition and
58 * the variables from the second bipartite partition in the other.
59 *
60 * - In addition to variable domain fixings, it is sometimes also possible to add new SOS1
61 * constraints to the branching nodes. This results in a nonstatic conflict graph, which may
62 * change dynamically with every branching node.
63 *
64 *
65 * @todo Possibly allow to generate local cuts via strengthened local cuts (would need to modified coefficients of rows).
66 *
67 * @todo Check whether we can avoid turning off multi-aggregation (it is sometimes possible to fix a multi-aggregated
68 * variable to 0 by fixing the aggregating variables to 0).
69 */
70
71 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
72
73 #include "blockmemshell/memory.h"
74 #include "scip/cons_linear.h"
75 #include "scip/cons_setppc.h"
76 #include "scip/cons_sos1.h"
77 #include "scip/pub_cons.h"
78 #include "scip/pub_event.h"
79 #include "scip/pub_heur.h"
80 #include "scip/pub_lp.h"
81 #include "scip/pub_message.h"
82 #include "scip/pub_misc.h"
83 #include "scip/pub_misc_sort.h"
84 #include "scip/pub_tree.h"
85 #include "scip/pub_var.h"
86 #include "scip/scip_branch.h"
87 #include "scip/scip_conflict.h"
88 #include "scip/scip_cons.h"
89 #include "scip/scip_copy.h"
90 #include "scip/scip_cut.h"
91 #include "scip/scip_datastructures.h"
92 #include "scip/scip_event.h"
93 #include "scip/scip_general.h"
94 #include "scip/scip_lp.h"
95 #include "scip/scip_mem.h"
96 #include "scip/scip_message.h"
97 #include "scip/scip_numerics.h"
98 #include "scip/scip_param.h"
99 #include "scip/scip_prob.h"
100 #include "scip/scip_probing.h"
101 #include "scip/scip_sol.h"
102 #include "scip/scip_solvingstats.h"
103 #include "scip/scip_tree.h"
104 #include "scip/scip_var.h"
105 #include "tclique/tclique.h"
106 #include <ctype.h>
107 #include <stdlib.h>
108 #include <string.h>
109
110
111 /* constraint handler properties */
112 #define CONSHDLR_NAME "SOS1"
113 #define CONSHDLR_DESC "SOS1 constraint handler"
114 #define CONSHDLR_SEPAPRIORITY 1000 /**< priority of the constraint handler for separation */
115 #define CONSHDLR_ENFOPRIORITY 100 /**< priority of the constraint handler for constraint enforcing */
116 #define CONSHDLR_CHECKPRIORITY -10 /**< priority of the constraint handler for checking feasibility */
117 #define CONSHDLR_SEPAFREQ 10 /**< frequency for separating cuts; zero means to separate only in the root node */
118 #define CONSHDLR_PROPFREQ 1 /**< frequency for propagating domains; zero means only preprocessing propagation */
119 #define CONSHDLR_EAGERFREQ 100 /**< frequency for using all instead of only the useful constraints in separation,
120 * propagation and enforcement, -1 for no eager evaluations, 0 for first only */
121 #define CONSHDLR_MAXPREROUNDS -1 /**< maximal number of presolving rounds the constraint handler participates in (-1: no limit) */
122 #define CONSHDLR_DELAYSEPA FALSE /**< should separation method be delayed, if other separators found cuts? */
123 #define CONSHDLR_DELAYPROP FALSE /**< should propagation method be delayed, if other propagators found reductions? */
124 #define CONSHDLR_NEEDSCONS TRUE /**< should the constraint handler be skipped, if no constraints are available? */
125 #define CONSHDLR_PROP_TIMING SCIP_PROPTIMING_BEFORELP
126 #define CONSHDLR_PRESOLTIMING SCIP_PRESOLTIMING_MEDIUM
127
128 /* adjacency matrix */
129 #define DEFAULT_MAXSOSADJACENCY 10000 /**< do not create an adjacency matrix if number of SOS1 variables is larger than predefined value
130 * (-1: no limit) */
131
132 /* presolving */
133 #define DEFAULT_MAXEXTENSIONS 1 /**< maximal number of extensions that will be computed for each SOS1 constraint */
134 #define DEFAULT_MAXTIGHTENBDS 5 /**< maximal number of bound tightening rounds per presolving round (-1: no limit) */
135 #define DEFAULT_PERFIMPLANALYSIS FALSE /**< if TRUE then perform implication graph analysis (might add additional SOS1 constraints) */
136 #define DEFAULT_DEPTHIMPLANALYSIS -1 /**< number of recursive calls of implication graph analysis (-1: no limit) */
137
138 /* propagation */
139 #define DEFAULT_CONFLICTPROP TRUE /**< whether to use conflict graph propagation */
140 #define DEFAULT_IMPLPROP TRUE /**< whether to use implication graph propagation */
141 #define DEFAULT_SOSCONSPROP FALSE /**< whether to use SOS1 constraint propagation */
142
143 /* branching rules */
144 #define DEFAULT_BRANCHSTRATEGIES "nbs" /**< possible branching strategies (see parameter DEFAULT_BRANCHINGRULE) */
145 #define DEFAULT_BRANCHINGRULE 'n' /**< which branching rule should be applied ? ('n': neighborhood, 'b': bipartite, 's': SOS1/clique)
146 * (note: in some cases an automatic switching to SOS1 branching is possible) */
147 #define DEFAULT_AUTOSOS1BRANCH TRUE /**< if TRUE then automatically switch to SOS1 branching if the SOS1 constraints do not overlap */
148 #define DEFAULT_FIXNONZERO FALSE /**< if neighborhood branching is used, then fix the branching variable (if positive in sign) to the value of the
149 * feasibility tolerance */
150 #define DEFAULT_ADDCOMPS FALSE /**< if TRUE then add complementarity constraints to the branching nodes (can be used in combination with
151 * neighborhood or bipartite branching) */
152 #define DEFAULT_MAXADDCOMPS -1 /**< maximal number of complementarity constraints added per branching node (-1: no limit) */
153 #define DEFAULT_ADDCOMPSDEPTH 30 /**< only add complementarity constraints to branching nodes for predefined depth (-1: no limit) */
154 #define DEFAULT_ADDCOMPSFEAS -0.6 /**< minimal feasibility value for complementarity constraints in order to be added to the branching node */
155 #define DEFAULT_ADDBDSFEAS 1.0 /**< minimal feasibility value for bound inequalities in order to be added to the branching node */
156 #define DEFAULT_ADDEXTENDEDBDS TRUE /**< should added complementarity constraints be extended to SOS1 constraints to get tighter bound inequalities */
157
158 /* selection rules */
159 #define DEFAULT_NSTRONGROUNDS 0 /**< maximal number of strong branching rounds to perform for each node (-1: auto)
160 * (only available for neighborhood and bipartite branching) */
161 #define DEFAULT_NSTRONGITER 10000 /**< maximal number LP iterations to perform for each strong branching round (-2: auto, -1: no limit) */
162
163 /* separation */
164 #define DEFAULT_BOUNDCUTSFROMSOS1 FALSE /**< if TRUE separate bound inequalities from SOS1 constraints */
165 #define DEFAULT_BOUNDCUTSFROMGRAPH TRUE /**< if TRUE separate bound inequalities from the conflict graph */
166 #define DEFAULT_AUTOCUTSFROMSOS1 TRUE /**< if TRUE then automatically switch to separating from SOS1 constraints if the SOS1 constraints do not overlap */
167 #define DEFAULT_BOUNDCUTSFREQ 10 /**< frequency for separating bound cuts; zero means to separate only in the root node */
168 #define DEFAULT_BOUNDCUTSDEPTH 40 /**< node depth of separating bound cuts (-1: no limit) */
169 #define DEFAULT_MAXBOUNDCUTS 50 /**< maximal number of bound cuts separated per branching node */
170 #define DEFAULT_MAXBOUNDCUTSROOT 150 /**< maximal number of bound cuts separated per iteration in the root node */
171 #define DEFAULT_STRTHENBOUNDCUTS TRUE /**< if TRUE then bound cuts are strengthened in case bound variables are available */
172 #define DEFAULT_IMPLCUTSFREQ 0 /**< frequency for separating implied bound cuts; zero means to separate only in the root node */
173 #define DEFAULT_IMPLCUTSDEPTH 40 /**< node depth of separating implied bound cuts (-1: no limit) */
174 #define DEFAULT_MAXIMPLCUTS 50 /**< maximal number of implied bound cuts separated per branching node */
175 #define DEFAULT_MAXIMPLCUTSROOT 150 /**< maximal number of implied bound cuts separated per iteration in the root node */
176
177 /* event handler properties */
178 #define EVENTHDLR_NAME "SOS1"
179 #define EVENTHDLR_DESC "bound change event handler for SOS1 constraints"
180
181 #define EVENTHDLR_EVENT_TYPE (SCIP_EVENTTYPE_BOUNDCHANGED | SCIP_EVENTTYPE_GBDCHANGED)
182
183 /* defines */
184 #define DIVINGCUTOFFVALUE 1e6
185
186
187 /** constraint data for SOS1 constraints */
188 struct SCIP_ConsData
189 {
190 int nvars; /**< number of variables in the constraint */
191 int maxvars; /**< maximal number of variables (= size of storage) */
192 int nfixednonzeros; /**< number of variables fixed to be nonzero */
193 SCIP_Bool local; /**< TRUE if constraint is only valid locally */
194 SCIP_VAR** vars; /**< variables in constraint */
195 SCIP_ROW* rowlb; /**< row corresponding to lower bounds, or NULL if not yet created */
196 SCIP_ROW* rowub; /**< row corresponding to upper bounds, or NULL if not yet created */
197 SCIP_Real* weights; /**< weights determining the order (ascending), or NULL if not used */
198 };
199
200
201 /** node data of a given node in the conflict graph */
202 struct SCIP_NodeData
203 {
204 SCIP_VAR* var; /**< variable belonging to node */
205 SCIP_VAR* lbboundvar; /**< bound variable @p z from constraint \f$x \geq \mu \cdot z\f$ (or NULL if not existent) */
206 SCIP_VAR* ubboundvar; /**< bound variable @p z from constraint \f$x \leq \mu \cdot z\f$ (or NULL if not existent) */
207 SCIP_Real lbboundcoef; /**< value \f$\mu\f$ from constraint \f$x \geq \mu z \f$ (0.0 if not existent) */
208 SCIP_Real ubboundcoef; /**< value \f$\mu\f$ from constraint \f$x \leq \mu z \f$ (0.0 if not existent) */
209 SCIP_Bool lbboundcomp; /**< TRUE if the nodes from the connected component of the conflict graph the given node belongs to
210 * all have the same lower bound variable */
211 SCIP_Bool ubboundcomp; /**< TRUE if the nodes from the connected component of the conflict graph the given node belongs to
212 * all have the same lower bound variable */
213 };
214 typedef struct SCIP_NodeData SCIP_NODEDATA;
215
216
217 /** successor data of a given nodes successor in the implication graph */
218 struct SCIP_SuccData
219 {
220 SCIP_Real lbimpl; /**< lower bound implication */
221 SCIP_Real ubimpl; /**< upper bound implication */
222 };
223 typedef struct SCIP_SuccData SCIP_SUCCDATA;
224
225
226 /** tclique data for bound cut generation */
227 struct TCLIQUE_Data
228 {
229 SCIP* scip; /**< SCIP data structure */
230 SCIP_CONSHDLR* conshdlr; /**< SOS1 constraint handler */
231 SCIP_DIGRAPH* conflictgraph; /**< conflict graph */
232 SCIP_SOL* sol; /**< LP solution to be separated (or NULL) */
233 SCIP_Real scaleval; /**< factor for scaling weights */
234 SCIP_Bool cutoff; /**< whether a cutoff occurred */
235 int ncuts; /**< number of bound cuts found in this iteration */
236 int nboundcuts; /**< number of bound cuts found so far */
237 int maxboundcuts; /**< maximal number of clique cuts separated per separation round (-1: no limit) */
238 SCIP_Bool strthenboundcuts; /**< if TRUE then bound cuts are strengthened in case bound variables are available */
239 };
240
241
242 /** SOS1 constraint handler data */
243 struct SCIP_ConshdlrData
244 {
245 /* conflict graph */
246 SCIP_DIGRAPH* conflictgraph; /**< conflict graph */
247 SCIP_DIGRAPH* localconflicts; /**< local conflicts */
248 SCIP_Bool isconflocal; /**< if TRUE then local conflicts are present and conflict graph has to be updated for each node */
249 SCIP_HASHMAP* varhash; /**< hash map from variable to node in the conflict graph */
250 int nsos1vars; /**< number of problem variables that are part of the SOS1 conflict graph */
251 /* adjacency matrix */
252 int maxsosadjacency; /**< do not create an adjacency matrix if number of SOS1 variables is larger than predefined
253 * value (-1: no limit) */
254 /* implication graph */
255 SCIP_DIGRAPH* implgraph; /**< implication graph (@p j is successor of @p i if and only if \f$ x_i\not = 0 \Rightarrow x_j\not = 0\f$) */
256 int nimplnodes; /**< number of nodes in the implication graph */
257 /* tclique graph */
258 TCLIQUE_GRAPH* tcliquegraph; /**< tclique graph data structure */
259 TCLIQUE_DATA* tcliquedata; /**< tclique data */
260 /* event handler */
261 SCIP_EVENTHDLR* eventhdlr; /**< event handler for bound change events */
262 SCIP_VAR** fixnonzerovars; /**< stack of variables fixed to nonzero marked by event handler */
263 int maxnfixnonzerovars; /**< size of stack fixnonzerovars */
264 int nfixnonzerovars; /**< number of variables fixed to nonzero marked by event handler */
265 /* presolving */
266 int cntextsos1; /**< counts number of extended SOS1 constraints */
267 int maxextensions; /**< maximal number of extensions that will be computed for each SOS1 constraint */
268 int maxtightenbds; /**< maximal number of bound tightening rounds per presolving round (-1: no limit) */
269 SCIP_Bool perfimplanalysis; /**< if TRUE then perform implication graph analysis (might add additional SOS1 constraints) */
270 int depthimplanalysis; /**< number of recursive calls of implication graph analysis (-1: no limit) */
271 /* propagation */
272 SCIP_Bool conflictprop; /**< whether to use conflict graph propagation */
273 SCIP_Bool implprop; /**< whether to use implication graph propagation */
274 SCIP_Bool sosconsprop; /**< whether to use SOS1 constraint propagation */
275 /* branching */
276 char branchingrule; /**< which branching rule should be applied ? ('n': neighborhood, 'b': bipartite, 's': SOS1/clique)
277 * (note: in some cases an automatic switching to SOS1 branching is possible) */
278 SCIP_Bool autosos1branch; /**< if TRUE then automatically switch to SOS1 branching if the SOS1 constraints do not overlap */
279 SCIP_Bool fixnonzero; /**< if neighborhood branching is used, then fix the branching variable (if positive in sign) to the value of the
280 * feasibility tolerance */
281 SCIP_Bool addcomps; /**< if TRUE then add complementarity constraints to the branching nodes additionally to domain fixings
282 * (can be used in combination with neighborhood or bipartite branching) */
283 int maxaddcomps; /**< maximal number of complementarity cons. and cor. bound ineq. added per branching node (-1: no limit) */
284 int addcompsdepth; /**< only add complementarity constraints to branching nodes for predefined depth (-1: no limit) */
285 SCIP_Real addcompsfeas; /**< minimal feasibility value for complementarity constraints in order to be added to the branching node */
286 SCIP_Real addbdsfeas; /**< minimal feasibility value for bound inequalities in order to be added to the branching node */
287 SCIP_Bool addextendedbds; /**< should added complementarity constraints be extended to SOS1 constraints to get tighter bound inequalities */
288 SCIP_Bool branchsos; /**< Branch on SOS condition in enforcing? This value can only be set to false if all SOS1 variables are binary */
289 SCIP_Bool branchnonzeros; /**< Branch on SOS cons. with most number of nonzeros? */
290 SCIP_Bool branchweight; /**< Branch on SOS cons. with highest nonzero-variable weight for branching - needs branchnonzeros to be false */
291 SCIP_Bool switchsos1branch; /**< whether to switch to SOS1 branching */
292 /* selection rules */
293 int nstrongrounds; /**< maximal number of strong branching rounds to perform for each node (-1: auto)
294 * (only available for neighborhood and bipartite branching) */
295 int nstrongiter; /**< maximal number LP iterations to perform for each strong branching round (-2: auto, -1: no limit) */
296 /* separation */
297 SCIP_Bool boundcutsfromsos1; /**< if TRUE separate bound inequalities from SOS1 constraints */
298 SCIP_Bool boundcutsfromgraph; /**< if TRUE separate bound inequalities from the conflict graph */
299 SCIP_Bool autocutsfromsos1; /**< if TRUE then automatically switch to separating SOS1 constraints if the SOS1 constraints do not overlap */
300 SCIP_Bool switchcutsfromsos1; /**< whether to switch to separate bound inequalities from SOS1 constraints */
301 int boundcutsfreq; /**< frequency for separating bound cuts; zero means to separate only in the root node */
302 int boundcutsdepth; /**< node depth of separating bound cuts (-1: no limit) */
303 int maxboundcuts; /**< maximal number of bound cuts separated per branching node */
304 int maxboundcutsroot; /**< maximal number of bound cuts separated per iteration in the root node */
305 int nboundcuts; /**< number of bound cuts found so far */
306 SCIP_Bool strthenboundcuts; /**< if TRUE then bound cuts are strengthened in case bound variables are available */
307 int implcutsfreq; /**< frequency for separating implied bound cuts; zero means to separate only in the root node */
308 int implcutsdepth; /**< node depth of separating implied bound cuts (-1: no limit) */
309 int maximplcuts; /**< maximal number of implied bound cuts separated per branching node */
310 int maximplcutsroot; /**< maximal number of implied bound cuts separated per iteration in the root node */
311 };
312
313
314
315 /*
316 * local methods
317 */
318
319 /** returns whether two vertices are adjacent in the conflict graph */
320 static
isConnectedSOS1(SCIP_Bool ** adjacencymatrix,SCIP_DIGRAPH * conflictgraph,int vertex1,int vertex2)321 SCIP_Bool isConnectedSOS1(
322 SCIP_Bool** adjacencymatrix, /**< adjacency matrix of conflict graph (lower half) (or NULL if an adjacencymatrix is not at hand) */
323 SCIP_DIGRAPH* conflictgraph, /**< conflict graph (or NULL if an adjacencymatrix is at hand) */
324 int vertex1, /**< first vertex */
325 int vertex2 /**< second vertex */
326 )
327 {
328 assert( adjacencymatrix != NULL || conflictgraph != NULL );
329
330 /* we do not allow self-loops */
331 if ( vertex1 == vertex2 )
332 return FALSE;
333
334 /* for debugging */
335 if ( adjacencymatrix == NULL )
336 {
337 int succvertex;
338 int* succ;
339 int nsucc1;
340 int nsucc2;
341 int j;
342
343 nsucc1 = SCIPdigraphGetNSuccessors(conflictgraph, vertex1);
344 nsucc2 = SCIPdigraphGetNSuccessors(conflictgraph, vertex2);
345
346 if ( nsucc1 < 1 || nsucc2 < 1 )
347 return FALSE;
348
349 if ( nsucc1 > nsucc2 )
350 {
351 SCIPswapInts(&vertex1, &vertex2);
352 SCIPswapInts(&nsucc1, &nsucc2);
353 }
354
355 succ = SCIPdigraphGetSuccessors(conflictgraph, vertex1);
356 SCIPsortInt(succ, nsucc1);
357
358 for (j = 0; j < nsucc1; ++j)
359 {
360 succvertex = succ[j];
361 if ( succvertex == vertex2 )
362 return TRUE;
363 else if ( succvertex > vertex2 )
364 return FALSE;
365 }
366 }
367 else
368 {
369 if ( vertex1 < vertex2 )
370 return adjacencymatrix[vertex2][vertex1];
371 else
372 return adjacencymatrix[vertex1][vertex2];
373 }
374
375 return FALSE;
376 }
377
378
379 /** checks whether a variable violates an SOS1 constraint w.r.t. sol together with at least one other variable */
380 static
isViolatedSOS1(SCIP * scip,SCIP_DIGRAPH * conflictgraph,int node,SCIP_SOL * sol)381 SCIP_Bool isViolatedSOS1(
382 SCIP* scip, /**< SCIP data structure */
383 SCIP_DIGRAPH* conflictgraph, /**< conflict graph (or NULL if an adjacencymatrix is at hand) */
384 int node, /**< node of variable in the conflict graph */
385 SCIP_SOL* sol /**< solution, or NULL to use current node's solution */
386 )
387 {
388 SCIP_Real solval;
389 SCIP_VAR* var;
390
391 assert( scip != NULL );
392 assert( conflictgraph != NULL );
393 assert( node >= 0 );
394
395 var = SCIPnodeGetVarSOS1(conflictgraph, node);
396 assert( var != NULL );
397 solval = SCIPgetSolVal(scip, sol, var);
398
399 /* check whether variable is nonzero w.r.t. sol and the bounds have not been fixed to zero by propagation */
400 if ( ! SCIPisFeasZero(scip, solval) && ( ! SCIPisFeasZero(scip, SCIPvarGetLbLocal(var)) || ! SCIPisFeasZero(scip, SCIPvarGetUbLocal(var)) ) )
401 {
402 int* succ;
403 int nsucc;
404 int s;
405
406 nsucc = SCIPdigraphGetNSuccessors(conflictgraph, node);
407 succ = SCIPdigraphGetSuccessors(conflictgraph, node);
408
409 /* check whether a neighbor variable is nonzero w.r.t. sol */
410 for (s = 0; s < nsucc; ++s)
411 {
412 var = SCIPnodeGetVarSOS1(conflictgraph, succ[s]);
413 assert( var != NULL );
414 solval = SCIPgetSolVal(scip, sol, var);
415 if ( ! SCIPisFeasZero(scip, solval) && ( ! SCIPisFeasZero(scip, SCIPvarGetLbLocal(var)) || ! SCIPisFeasZero(scip, SCIPvarGetUbLocal(var)) ) )
416 return TRUE;
417 }
418 }
419
420 return FALSE;
421 }
422
423
424 /** returns solution value of imaginary binary big-M variable of a given node from the conflict graph */
425 static
nodeGetSolvalBinaryBigMSOS1(SCIP * scip,SCIP_DIGRAPH * conflictgraph,SCIP_SOL * sol,int node)426 SCIP_Real nodeGetSolvalBinaryBigMSOS1(
427 SCIP* scip, /**< SCIP pointer */
428 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
429 SCIP_SOL* sol, /**< primal solution, or NULL for current LP/pseudo solution */
430 int node /**< node of the conflict graph */
431 )
432 {
433 SCIP_Real bound;
434 SCIP_VAR* var;
435 SCIP_Real val;
436
437 assert( scip != NULL );
438 assert( conflictgraph != NULL );
439 assert( node >= 0 && node < SCIPdigraphGetNNodes(conflictgraph) );
440
441 var = SCIPnodeGetVarSOS1(conflictgraph, node);
442 val = SCIPgetSolVal(scip, sol, var);
443
444 if ( SCIPisFeasNegative(scip, val) )
445 {
446 bound = SCIPvarGetLbLocal(var);
447 assert( SCIPisFeasNegative(scip, bound) );
448
449 if ( SCIPisInfinity(scip, -val) )
450 return 1.0;
451 else if ( SCIPisInfinity(scip, -bound) )
452 return 0.0;
453 else
454 return (val/bound);
455 }
456 else if ( SCIPisFeasPositive(scip, val) )
457 {
458 bound = SCIPvarGetUbLocal(var);
459 assert( SCIPisFeasPositive(scip, bound) );
460 assert( SCIPisFeasPositive(scip, val) );
461
462 if ( SCIPisInfinity(scip, val) )
463 return 1.0;
464 else if ( SCIPisInfinity(scip, bound) )
465 return 0.0;
466 else
467 return (val/bound);
468 }
469 else
470 return 0.0;
471 }
472
473
474 /** gets (variable) lower bound value of current LP relaxation solution for a given node from the conflict graph */
475 static
nodeGetSolvalVarboundLbSOS1(SCIP * scip,SCIP_DIGRAPH * conflictgraph,SCIP_SOL * sol,int node)476 SCIP_Real nodeGetSolvalVarboundLbSOS1(
477 SCIP* scip, /**< SCIP pointer */
478 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
479 SCIP_SOL* sol, /**< primal solution, or NULL for current LP/pseudo solution */
480 int node /**< node of the conflict graph */
481 )
482 {
483 SCIP_NODEDATA* nodedata;
484
485 assert( scip != NULL );
486 assert( conflictgraph != NULL );
487 assert( node >= 0 && node < SCIPdigraphGetNNodes(conflictgraph) );
488
489 /* get node data */
490 nodedata = (SCIP_NODEDATA*)SCIPdigraphGetNodeData(conflictgraph, node);
491 assert( nodedata != NULL );
492
493 /* if variable is not involved in a variable upper bound constraint */
494 if ( nodedata->lbboundvar == NULL || ! nodedata->lbboundcomp )
495 return SCIPvarGetLbLocal(nodedata->var);
496
497 return nodedata->lbboundcoef * SCIPgetSolVal(scip, sol, nodedata->lbboundvar);
498 }
499
500
501 /** gets (variable) upper bound value of current LP relaxation solution for a given node from the conflict graph */
502 static
nodeGetSolvalVarboundUbSOS1(SCIP * scip,SCIP_DIGRAPH * conflictgraph,SCIP_SOL * sol,int node)503 SCIP_Real nodeGetSolvalVarboundUbSOS1(
504 SCIP* scip, /**< SCIP pointer */
505 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
506 SCIP_SOL* sol, /**< primal solution, or NULL for current LP/pseudo solution */
507 int node /**< node of the conflict graph */
508 )
509 {
510 SCIP_NODEDATA* nodedata;
511
512 assert( scip != NULL );
513 assert( conflictgraph != NULL );
514 assert( node >= 0 && node < SCIPdigraphGetNNodes(conflictgraph) );
515
516 /* get node data */
517 nodedata = (SCIP_NODEDATA*)SCIPdigraphGetNodeData(conflictgraph, node);
518 assert( nodedata != NULL );
519
520 /* if variable is not involved in a variable upper bound constraint */
521 if ( nodedata->ubboundvar == NULL || ! nodedata->ubboundcomp )
522 return SCIPvarGetUbLocal(nodedata->var);
523
524 return nodedata->ubboundcoef * SCIPgetSolVal(scip, sol, nodedata->ubboundvar);
525 }
526
527
528 /** returns whether variable is part of the SOS1 conflict graph */
529 static
varIsSOS1(SCIP_CONSHDLRDATA * conshdlrdata,SCIP_VAR * var)530 SCIP_Bool varIsSOS1(
531 SCIP_CONSHDLRDATA* conshdlrdata, /**< SOS1 constraint handler */
532 SCIP_VAR* var /**< variable */
533 )
534 {
535 assert( conshdlrdata != NULL );
536 assert( var != NULL );
537
538 if ( conshdlrdata->varhash == NULL || ! SCIPhashmapExists(conshdlrdata->varhash, var) )
539 return FALSE;
540
541 return TRUE;
542 }
543
544
545 /** returns SOS1 index of variable or -1 if variable is not part of the SOS1 conflict graph */
546 static
varGetNodeSOS1(SCIP_CONSHDLRDATA * conshdlrdata,SCIP_VAR * var)547 int varGetNodeSOS1(
548 SCIP_CONSHDLRDATA* conshdlrdata, /**< SOS1 constraint handler */
549 SCIP_VAR* var /**< variable */
550 )
551 {
552 assert( conshdlrdata != NULL );
553 assert( var != NULL );
554 assert( conshdlrdata->varhash != NULL );
555
556 if ( ! SCIPhashmapExists(conshdlrdata->varhash, var) )
557 return -1;
558
559 return SCIPhashmapGetImageInt(conshdlrdata->varhash, var);
560 }
561
562
563 /** fix variable in given node to 0 or add constraint if variable is multi-aggregated
564 *
565 * @todo Try to handle multi-aggregated variables as in fixVariableZero() below.
566 */
567 static
fixVariableZeroNode(SCIP * scip,SCIP_VAR * var,SCIP_NODE * node,SCIP_Bool * infeasible)568 SCIP_RETCODE fixVariableZeroNode(
569 SCIP* scip, /**< SCIP pointer */
570 SCIP_VAR* var, /**< variable to be fixed to 0*/
571 SCIP_NODE* node, /**< node */
572 SCIP_Bool* infeasible /**< if fixing is infeasible */
573 )
574 {
575 /* if variable cannot be nonzero */
576 *infeasible = FALSE;
577 if ( SCIPisFeasPositive(scip, SCIPvarGetLbLocal(var)) || SCIPisFeasNegative(scip, SCIPvarGetUbLocal(var)) )
578 {
579 *infeasible = TRUE;
580 return SCIP_OKAY;
581 }
582
583 /* if variable is multi-aggregated */
584 if ( SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR )
585 {
586 SCIP_CONS* cons;
587 SCIP_Real val;
588
589 val = 1.0;
590
591 if ( ! SCIPisFeasZero(scip, SCIPvarGetLbLocal(var)) || ! SCIPisFeasZero(scip, SCIPvarGetUbLocal(var)) )
592 {
593 SCIPdebugMsg(scip, "creating constraint to force multi-aggregated variable <%s> to 0.\n", SCIPvarGetName(var));
594 /* we have to insert a local constraint var = 0 */
595 SCIP_CALL( SCIPcreateConsLinear(scip, &cons, "branch", 1, &var, &val, 0.0, 0.0, TRUE, TRUE, TRUE, TRUE, TRUE,
596 TRUE, FALSE, FALSE, FALSE, FALSE) );
597 SCIP_CALL( SCIPaddConsNode(scip, node, cons, NULL) );
598 SCIP_CALL( SCIPreleaseCons(scip, &cons) );
599 }
600 }
601 else
602 {
603 if ( ! SCIPisFeasZero(scip, SCIPvarGetLbLocal(var)) )
604 SCIP_CALL( SCIPchgVarLbNode(scip, node, var, 0.0) );
605 if ( ! SCIPisFeasZero(scip, SCIPvarGetUbLocal(var)) )
606 SCIP_CALL( SCIPchgVarUbNode(scip, node, var, 0.0) );
607 }
608
609 return SCIP_OKAY;
610 }
611
612
613 /** try to fix variable to 0
614 *
615 * Try to treat fixing by special consideration of multiaggregated variables. For a multi-aggregation
616 * \f[
617 * x = \sum_{i=1}^n \alpha_i x_i + c,
618 * \f]
619 * we can express the fixing \f$x = 0\f$ by fixing all \f$x_i\f$ to 0 if \f$c = 0\f$ and the lower bounds of \f$x_i\f$
620 * are nonnegative if \f$\alpha_i > 0\f$ or the upper bounds are nonpositive if \f$\alpha_i < 0\f$.
621 */
622 static
fixVariableZero(SCIP * scip,SCIP_VAR * var,SCIP_Bool * infeasible,SCIP_Bool * tightened)623 SCIP_RETCODE fixVariableZero(
624 SCIP* scip, /**< SCIP pointer */
625 SCIP_VAR* var, /**< variable to be fixed to 0*/
626 SCIP_Bool* infeasible, /**< if fixing is infeasible */
627 SCIP_Bool* tightened /**< if fixing was performed */
628 )
629 {
630 assert( scip != NULL );
631 assert( var != NULL );
632 assert( infeasible != NULL );
633 assert( tightened != NULL );
634
635 *infeasible = FALSE;
636 *tightened = FALSE;
637
638 if ( SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR )
639 {
640 SCIP_Real aggrconst;
641
642 /* if constant is 0 */
643 aggrconst = SCIPvarGetMultaggrConstant(var);
644 if ( SCIPisZero(scip, aggrconst) )
645 {
646 SCIP_VAR** aggrvars;
647 SCIP_Real* aggrvals;
648 SCIP_Bool allnonnegative = TRUE;
649 int naggrvars;
650 int i;
651
652 SCIP_CALL( SCIPflattenVarAggregationGraph(scip, var) );
653
654 /* check whether all variables are "nonnegative" */
655 naggrvars = SCIPvarGetMultaggrNVars(var);
656 aggrvars = SCIPvarGetMultaggrVars(var);
657 aggrvals = SCIPvarGetMultaggrScalars(var);
658 for (i = 0; i < naggrvars; ++i)
659 {
660 if ( (SCIPisPositive(scip, aggrvals[i]) && SCIPisNegative(scip, SCIPvarGetLbLocal(aggrvars[i]))) ||
661 (SCIPisNegative(scip, aggrvals[i]) && SCIPisPositive(scip, SCIPvarGetUbLocal(aggrvars[i]))) )
662 {
663 allnonnegative = FALSE;
664 break;
665 }
666 }
667
668 if ( allnonnegative )
669 {
670 /* all variables are nonnegative -> fix variables */
671 for (i = 0; i < naggrvars; ++i)
672 {
673 SCIP_Bool fixed;
674 SCIP_CALL( SCIPfixVar(scip, aggrvars[i], 0.0, infeasible, &fixed) );
675 if ( *infeasible )
676 return SCIP_OKAY;
677 *tightened = *tightened || fixed;
678 }
679 }
680 }
681 }
682 else
683 {
684 SCIP_CALL( SCIPfixVar(scip, var, 0.0, infeasible, tightened) );
685 }
686
687 return SCIP_OKAY;
688 }
689
690
691 /** fix variable in local node to 0, and return whether the operation was feasible
692 *
693 * @note We do not add a linear constraint if the variable is multi-aggregated as in
694 * fixVariableZeroNode(), since this would be too time consuming.
695 */
696 static
inferVariableZero(SCIP * scip,SCIP_VAR * var,SCIP_CONS * cons,int inferinfo,SCIP_Bool * infeasible,SCIP_Bool * tightened,SCIP_Bool * success)697 SCIP_RETCODE inferVariableZero(
698 SCIP* scip, /**< SCIP pointer */
699 SCIP_VAR* var, /**< variable to be fixed to 0*/
700 SCIP_CONS* cons, /**< constraint */
701 int inferinfo, /**< info for reverse prop. */
702 SCIP_Bool* infeasible, /**< if fixing is infeasible */
703 SCIP_Bool* tightened, /**< if fixing was performed */
704 SCIP_Bool* success /**< whether fixing was successful, i.e., variable is not multi-aggregated */
705 )
706 {
707 *infeasible = FALSE;
708 *tightened = FALSE;
709 *success = FALSE;
710
711 /* if variable cannot be nonzero */
712 if ( SCIPisFeasPositive(scip, SCIPvarGetLbLocal(var)) || SCIPisFeasNegative(scip, SCIPvarGetUbLocal(var)) )
713 {
714 *infeasible = TRUE;
715 return SCIP_OKAY;
716 }
717
718 /* directly fix variable if it is not multi-aggregated */
719 if ( SCIPvarGetStatus(var) != SCIP_VARSTATUS_MULTAGGR )
720 {
721 SCIP_Bool tighten;
722
723 /* fix lower bound */
724 SCIP_CALL( SCIPinferVarLbCons(scip, var, 0.0, cons, inferinfo, FALSE, infeasible, &tighten) );
725 *tightened = *tightened || tighten;
726
727 /* fix upper bound */
728 SCIP_CALL( SCIPinferVarUbCons(scip, var, 0.0, cons, inferinfo, FALSE, infeasible, &tighten) );
729 *tightened = *tightened || tighten;
730
731 *success = TRUE;
732 }
733
734 return SCIP_OKAY;
735 }
736
737
738 /** add lock on variable */
739 static
lockVariableSOS1(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var)740 SCIP_RETCODE lockVariableSOS1(
741 SCIP* scip, /**< SCIP data structure */
742 SCIP_CONS* cons, /**< constraint */
743 SCIP_VAR* var /**< variable */
744 )
745 {
746 assert( scip != NULL );
747 assert( cons != NULL );
748 assert( var != NULL );
749
750 /* rounding down == bad if lb < 0, rounding up == bad if ub > 0 */
751 SCIP_CALL( SCIPlockVarCons(scip, var, cons, SCIPisFeasNegative(scip, SCIPvarGetLbGlobal(var)),
752 SCIPisFeasPositive(scip, SCIPvarGetUbGlobal(var))) );
753
754 return SCIP_OKAY;
755 }
756
757
758 /** remove lock on variable */
759 static
unlockVariableSOS1(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var)760 SCIP_RETCODE unlockVariableSOS1(
761 SCIP* scip, /**< SCIP data structure */
762 SCIP_CONS* cons, /**< constraint */
763 SCIP_VAR* var /**< variable */
764 )
765 {
766 assert( scip != NULL );
767 assert( cons != NULL );
768 assert( var != NULL );
769
770 /* rounding down == bad if lb < 0, rounding up == bad if ub > 0 */
771 SCIP_CALL( SCIPunlockVarCons(scip, var, cons, SCIPisFeasNegative(scip, SCIPvarGetLbGlobal(var)),
772 SCIPisFeasPositive(scip, SCIPvarGetUbGlobal(var))) );
773
774 return SCIP_OKAY;
775 }
776
777
778 /** ensures that the vars and weights array can store at least num entries */
779 static
consdataEnsurevarsSizeSOS1(SCIP * scip,SCIP_CONSDATA * consdata,int num,SCIP_Bool reserveWeights)780 SCIP_RETCODE consdataEnsurevarsSizeSOS1(
781 SCIP* scip, /**< SCIP data structure */
782 SCIP_CONSDATA* consdata, /**< constraint data */
783 int num, /**< minimum number of entries to store */
784 SCIP_Bool reserveWeights /**< whether the weights array is handled */
785 )
786 {
787 assert( consdata != NULL );
788 assert( consdata->nvars <= consdata->maxvars );
789
790 if ( num > consdata->maxvars )
791 {
792 int newsize;
793
794 newsize = SCIPcalcMemGrowSize(scip, num);
795 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->vars, consdata->maxvars, newsize) );
796 if ( reserveWeights )
797 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->weights, consdata->maxvars, newsize) );
798 consdata->maxvars = newsize;
799 }
800 assert( num <= consdata->maxvars );
801
802 return SCIP_OKAY;
803 }
804
805
806 /** handle new variable */
807 static
handleNewVariableSOS1(SCIP * scip,SCIP_CONS * cons,SCIP_CONSDATA * consdata,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_VAR * var,SCIP_Bool transformed)808 SCIP_RETCODE handleNewVariableSOS1(
809 SCIP* scip, /**< SCIP data structure */
810 SCIP_CONS* cons, /**< constraint */
811 SCIP_CONSDATA* consdata, /**< constraint data */
812 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
813 SCIP_VAR* var, /**< variable */
814 SCIP_Bool transformed /**< whether original variable was transformed */
815 )
816 {
817 SCIP_DIGRAPH* conflictgraph;
818 int node;
819
820 assert( scip != NULL );
821 assert( cons != NULL );
822 assert( consdata != NULL );
823 assert( conshdlrdata != NULL );
824 assert( var != NULL );
825
826 /* if we are in transformed problem, catch the variable's events */
827 if ( transformed )
828 {
829 assert( conshdlrdata->eventhdlr != NULL );
830
831 /* catch bound change events of variable */
832 SCIP_CALL( SCIPcatchVarEvent(scip, var, EVENTHDLR_EVENT_TYPE, conshdlrdata->eventhdlr,
833 (SCIP_EVENTDATA*)cons, NULL) ); /*lint !e740*/
834
835 /* if the variable if fixed to nonzero */
836 assert( consdata->nfixednonzeros >= 0 );
837 if ( SCIPisFeasPositive(scip, SCIPvarGetLbLocal(var)) || SCIPisFeasNegative(scip, SCIPvarGetUbLocal(var)) )
838 ++consdata->nfixednonzeros;
839 }
840
841 /* install the rounding locks for the new variable */
842 SCIP_CALL( lockVariableSOS1(scip, cons, var) );
843
844 /* branching on multiaggregated variables does not seem to work well, so avoid it */
845 SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, var) );
846
847 /* add the new coefficient to the upper bound LP row, if necessary */
848 if ( consdata->rowub != NULL && ! SCIPisInfinity(scip, SCIPvarGetUbGlobal(var)) && ! SCIPisZero(scip, SCIPvarGetUbGlobal(var)) )
849 {
850 SCIP_CALL( SCIPaddVarToRow(scip, consdata->rowub, var, 1.0/SCIPvarGetUbGlobal(var)) );
851 }
852
853 /* add the new coefficient to the lower bound LP row, if necessary */
854 if ( consdata->rowlb != NULL && ! SCIPisInfinity(scip, SCIPvarGetLbGlobal(var)) && ! SCIPisZero(scip, SCIPvarGetLbGlobal(var)) )
855 {
856 SCIP_CALL( SCIPaddVarToRow(scip, consdata->rowlb, var, 1.0/SCIPvarGetLbGlobal(var)) );
857 }
858
859 /* return if the conflict graph has not been created yet */
860 conflictgraph = conshdlrdata->conflictgraph;
861 if ( conflictgraph == NULL )
862 return SCIP_OKAY;
863
864 /* get node of variable in the conflict graph (or -1) */
865 node = varGetNodeSOS1(conshdlrdata, var);
866 assert( node < conshdlrdata->nsos1vars );
867
868 /* if the variable is not already a node of the conflict graph */
869 if ( node < 0 )
870 {
871 /* variable does not appear in the conflict graph: switch to SOS1 branching rule, which does not make use of a conflict graph
872 * @todo: maybe recompute the conflict graph, implication graph and varhash instead */
873 SCIPdebugMsg(scip, "Switched to SOS1 branching rule, since conflict graph could be infeasible.\n");
874 conshdlrdata->switchsos1branch = TRUE;
875 return SCIP_OKAY;
876 }
877
878 /* if the constraint is local, then there is no need to act, since local constraints are handled by the local conflict graph in the
879 * function enforceConflictgraph() */
880 if ( ! consdata->local )
881 {
882 SCIP_VAR** vars;
883 int nvars;
884 int v;
885
886 vars = consdata->vars;
887 nvars = consdata->nvars;
888
889 for (v = 0; v < nvars; ++v)
890 {
891 int nodev;
892
893 if ( var == vars[v] )
894 continue;
895
896 /* get node of variable in the conflict graph (or -1) */
897 nodev = varGetNodeSOS1(conshdlrdata, vars[v]);
898 assert( nodev < conshdlrdata->nsos1vars );
899
900 /* if the variable is already a node of the conflict graph */
901 if ( nodev >= 0 )
902 {
903 int nsucc;
904 int nsuccv;
905
906 nsucc = SCIPdigraphGetNSuccessors(conflictgraph, node);
907 nsuccv = SCIPdigraphGetNSuccessors(conflictgraph, nodev);
908
909 /* add arcs if not existent */
910 SCIP_CALL( SCIPdigraphAddArcSafe(conflictgraph, nodev, node, NULL) );
911 SCIP_CALL( SCIPdigraphAddArcSafe(conflictgraph, node, nodev, NULL) );
912
913 /* in case of new arcs: sort successors in ascending order */
914 if ( nsucc < SCIPdigraphGetNSuccessors(conflictgraph, node) )
915 {
916 SCIPdebugMsg(scip, "Added new conflict graph arc from variable %s to variable %s.\n", SCIPvarGetName(var), SCIPvarGetName(vars[v]));
917 SCIPsortInt(SCIPdigraphGetSuccessors(conflictgraph, node), SCIPdigraphGetNSuccessors(conflictgraph, node));
918 }
919
920 if ( nsuccv < SCIPdigraphGetNSuccessors(conflictgraph, nodev) )
921 {
922 SCIPdebugMsg(scip, "Added new conflict graph arc from variable %s to variable %s.\n", SCIPvarGetName(vars[v]), SCIPvarGetName(var));
923 SCIPsortInt(SCIPdigraphGetSuccessors(conflictgraph, nodev), SCIPdigraphGetNSuccessors(conflictgraph, nodev));
924 }
925 }
926 else
927 {
928 /* variable does not appear in the conflict graph: switch to SOS1 branching rule, which does not make use of a conflict graph
929 * @todo: maybe recompute the conflict graph, implication graph and varhash instead */
930 SCIPdebugMsg(scip, "Switched to SOS1 branching rule, since conflict graph could be infeasible.\n");
931 conshdlrdata->switchsos1branch = TRUE;
932 return SCIP_OKAY;
933 }
934 }
935 }
936
937 return SCIP_OKAY;
938 }
939
940
941 /** adds a variable to an SOS1 constraint, at position given by weight - ascending order */
942 static
addVarSOS1(SCIP * scip,SCIP_CONS * cons,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_VAR * var,SCIP_Real weight)943 SCIP_RETCODE addVarSOS1(
944 SCIP* scip, /**< SCIP data structure */
945 SCIP_CONS* cons, /**< constraint */
946 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
947 SCIP_VAR* var, /**< variable to add to the constraint */
948 SCIP_Real weight /**< weight to determine position */
949 )
950 {
951 SCIP_CONSDATA* consdata;
952 SCIP_Bool transformed;
953 int pos;
954 int j;
955
956 assert( var != NULL );
957 assert( cons != NULL );
958 assert( conshdlrdata != NULL );
959
960 consdata = SCIPconsGetData(cons);
961 assert( consdata != NULL );
962
963 if ( consdata->weights == NULL && consdata->maxvars > 0 )
964 {
965 SCIPerrorMessage("cannot add variable to SOS1 constraint <%s> that does not contain weights.\n", SCIPconsGetName(cons));
966 return SCIP_INVALIDCALL;
967 }
968
969 /* are we in the transformed problem? */
970 transformed = SCIPconsIsTransformed(cons);
971
972 /* always use transformed variables in transformed constraints */
973 if ( transformed )
974 {
975 SCIP_CALL( SCIPgetTransformedVar(scip, var, &var) );
976 }
977 assert( var != NULL );
978 assert( transformed == SCIPvarIsTransformed(var) );
979
980 SCIP_CALL( consdataEnsurevarsSizeSOS1(scip, consdata, consdata->nvars + 1, TRUE) );
981 assert( consdata->weights != NULL );
982 assert( consdata->maxvars >= consdata->nvars+1 );
983
984 /* find variable position */
985 for (pos = 0; pos < consdata->nvars; ++pos)
986 {
987 if ( consdata->weights[pos] > weight )
988 break;
989 }
990 assert( 0 <= pos && pos <= consdata->nvars );
991
992 /* move other variables, if necessary */
993 for (j = consdata->nvars; j > pos; --j)
994 {
995 consdata->vars[j] = consdata->vars[j-1];
996 consdata->weights[j] = consdata->weights[j-1];
997 }
998
999 /* insert variable */
1000 consdata->vars[pos] = var;
1001 consdata->weights[pos] = weight;
1002 ++consdata->nvars;
1003
1004 /* handle the new variable */
1005 SCIP_CALL( handleNewVariableSOS1(scip, cons, consdata, conshdlrdata, var, transformed) );
1006
1007 return SCIP_OKAY;
1008 }
1009
1010
1011 /** appends a variable to an SOS1 constraint */
1012 static
appendVarSOS1(SCIP * scip,SCIP_CONS * cons,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_VAR * var)1013 SCIP_RETCODE appendVarSOS1(
1014 SCIP* scip, /**< SCIP data structure */
1015 SCIP_CONS* cons, /**< constraint */
1016 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
1017 SCIP_VAR* var /**< variable to add to the constraint */
1018 )
1019 {
1020 SCIP_CONSDATA* consdata;
1021 SCIP_Bool transformed;
1022
1023 assert( var != NULL );
1024 assert( cons != NULL );
1025 assert( conshdlrdata != NULL );
1026
1027 consdata = SCIPconsGetData(cons);
1028 assert( consdata != NULL );
1029 assert( consdata->nvars >= 0 );
1030
1031 /* are we in the transformed problem? */
1032 transformed = SCIPconsIsTransformed(cons);
1033
1034 /* always use transformed variables in transformed constraints */
1035 if ( transformed )
1036 {
1037 SCIP_CALL( SCIPgetTransformedVar(scip, var, &var) );
1038 }
1039 assert( var != NULL );
1040 assert( transformed == SCIPvarIsTransformed(var) );
1041
1042 if ( consdata->weights != NULL )
1043 {
1044 SCIP_CALL( consdataEnsurevarsSizeSOS1(scip, consdata, consdata->nvars + 1, TRUE) );
1045 }
1046 else
1047 {
1048 SCIP_CALL( consdataEnsurevarsSizeSOS1(scip, consdata, consdata->nvars + 1, FALSE) );
1049 }
1050
1051 /* insert variable */
1052 consdata->vars[consdata->nvars] = var;
1053 if ( consdata->weights != NULL )
1054 {
1055 if ( consdata->nvars > 0 )
1056 consdata->weights[consdata->nvars] = consdata->weights[consdata->nvars-1] + 1.0;
1057 else
1058 consdata->weights[consdata->nvars] = 0.0;
1059 }
1060 ++consdata->nvars;
1061
1062 /* handle the new variable */
1063 SCIP_CALL( handleNewVariableSOS1(scip, cons, consdata, conshdlrdata, var, transformed) );
1064
1065 return SCIP_OKAY;
1066 }
1067
1068
1069 /** deletes a variable of an SOS1 constraint */
1070 static
deleteVarSOS1(SCIP * scip,SCIP_CONS * cons,SCIP_CONSDATA * consdata,SCIP_EVENTHDLR * eventhdlr,int pos)1071 SCIP_RETCODE deleteVarSOS1(
1072 SCIP* scip, /**< SCIP data structure */
1073 SCIP_CONS* cons, /**< constraint */
1074 SCIP_CONSDATA* consdata, /**< constraint data */
1075 SCIP_EVENTHDLR* eventhdlr, /**< corresponding event handler */
1076 int pos /**< position of variable in array */
1077 )
1078 {
1079 int j;
1080
1081 assert( 0 <= pos && pos < consdata->nvars );
1082
1083 /* remove lock of variable */
1084 SCIP_CALL( unlockVariableSOS1(scip, cons, consdata->vars[pos]) );
1085
1086 /* drop events on variable */
1087 SCIP_CALL( SCIPdropVarEvent(scip, consdata->vars[pos], EVENTHDLR_EVENT_TYPE, eventhdlr, (SCIP_EVENTDATA*)cons, -1) ); /*lint !e740*/
1088
1089 /* delete variable - need to copy since order is important */
1090 for (j = pos; j < consdata->nvars-1; ++j)
1091 {
1092 consdata->vars[j] = consdata->vars[j+1]; /*lint !e679*/
1093 if ( consdata->weights != NULL )
1094 consdata->weights[j] = consdata->weights[j+1]; /*lint !e679*/
1095 }
1096 --consdata->nvars;
1097
1098 return SCIP_OKAY;
1099 }
1100
1101
1102 /* ----------------------------- presolving --------------------------------------*/
1103
1104 /** extends a given clique of the conflict graph
1105 *
1106 * Implementation of the Bron-Kerbosch Algorithm from the paper:
1107 * Algorithm 457: Finding all Cliques of an Undirected Graph, Bron & Kerbosch, Commun. ACM, 1973
1108 */
1109 static
extensionOperatorSOS1(SCIP * scip,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_Bool ** adjacencymatrix,SCIP_DIGRAPH * vertexcliquegraph,int nsos1vars,int nconss,SCIP_CONS * cons,SCIP_VAR ** vars,SCIP_Real * weights,SCIP_Bool firstcall,SCIP_Bool usebacktrack,int ** cliques,int * ncliques,int * cliquesizes,int * newclique,int * workingset,int nworkingset,int nexts,int pos,int * maxextensions,int * naddconss,SCIP_Bool * success)1110 SCIP_RETCODE extensionOperatorSOS1(
1111 SCIP* scip, /**< SCIP pointer */
1112 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
1113 SCIP_Bool** adjacencymatrix, /**< adjacencymatrix of the conflict graph (only lower half filled) */
1114 SCIP_DIGRAPH* vertexcliquegraph, /**< graph that contains the information which cliques contain a given vertex
1115 * vertices of variables = 0, ..., nsos1vars-1; vertices of cliques = nsos1vars, ..., nsos1vars+ncliques-1*/
1116 int nsos1vars, /**< number of SOS1 variables */
1117 int nconss, /**< number of SOS1 constraints */
1118 SCIP_CONS* cons, /**< constraint to be extended */
1119 SCIP_VAR** vars, /**< variables of extended clique */
1120 SCIP_Real* weights, /**< weights of extended clique */
1121 SCIP_Bool firstcall, /**< whether this is the first call of extension operator */
1122 SCIP_Bool usebacktrack, /**< whether backtracking is needed for the computation */
1123 int** cliques, /**< all cliques found so far */
1124 int* ncliques, /**< number of clique found so far */
1125 int* cliquesizes, /**< number of variables of current clique */
1126 int* newclique, /**< clique we want to extended*/
1127 int* workingset, /**< set of vertices that already served as extension and set of candidates that probably will lead to an extension */
1128 int nworkingset, /**< length of array workingset */
1129 int nexts, /**< number of vertices that already served as extension */
1130 int pos, /**< position of potential candidate */
1131 int* maxextensions, /**< maximal number of extensions */
1132 int* naddconss, /**< number of added constraints */
1133 SCIP_Bool* success /**< pointer to store if at least one new clique was found */
1134 )
1135 {
1136 int* workingsetnew = NULL;
1137 int nextsnew;
1138 int nworkingsetnew;
1139 int mincands;
1140 int btriter = 0; /* backtrack iterator */
1141 int selvertex;
1142 int selpos = -1;
1143 int fixvertex = -1;
1144 int i;
1145 int j;
1146
1147 assert( scip != NULL );
1148 assert( conshdlrdata != NULL );
1149 assert( adjacencymatrix != NULL );
1150 assert( vertexcliquegraph != NULL );
1151 assert( cons != NULL );
1152 assert( cliques != NULL );
1153 assert( cliquesizes != NULL );
1154 assert( newclique != NULL );
1155 assert( workingset != NULL );
1156 assert( maxextensions != NULL );
1157 assert( naddconss != NULL );
1158 assert( success != NULL );
1159
1160 if ( firstcall )
1161 *success = FALSE;
1162
1163 mincands = nworkingset;
1164 if ( mincands < 1 )
1165 return SCIP_OKAY;
1166
1167 /* allocate buffer array */
1168 SCIP_CALL( SCIPallocBufferArray(scip, &workingsetnew, nworkingset) );
1169
1170 #ifdef SCIP_DEBUG
1171 for (i = 0; i < nexts; ++i)
1172 {
1173 for (j = nexts; j < nworkingset; ++j)
1174 {
1175 assert( isConnectedSOS1(adjacencymatrix, NULL, workingset[i], workingset[j]) );
1176 }
1177 }
1178 #endif
1179
1180 /* determine candidate with minimum number of disconnections */
1181 for (i = 0; i < nworkingset; ++i)
1182 {
1183 int vertex;
1184 int cnt = 0;
1185
1186 vertex = workingset[i];
1187
1188 /* count disconnections */
1189 for (j = nexts; j < nworkingset && cnt < mincands; ++j)
1190 {
1191 if ( vertex != workingset[j] && ! isConnectedSOS1(adjacencymatrix, NULL, vertex, workingset[j]) )
1192 {
1193 cnt++;
1194
1195 /* save position of potential candidate */
1196 pos = j;
1197 }
1198 }
1199
1200 /* check whether a new minimum was found */
1201 if ( cnt < mincands )
1202 {
1203 fixvertex = vertex;
1204 mincands = cnt;
1205 if ( i < nexts )
1206 {
1207 assert( pos >= 0 );
1208 selpos = pos;
1209 }
1210 else
1211 {
1212 selpos = i;
1213
1214 /* preincrement */
1215 btriter = 1;
1216 }
1217 }
1218 }
1219
1220 /* If fixed point is initially chosen from candidates then number of disconnections will be preincreased by one. */
1221
1222 /* backtrackcycle */
1223 for (btriter = mincands + btriter; btriter >= 1; --btriter)
1224 {
1225 assert( selpos >= 0);
1226 assert( fixvertex >= 0);
1227
1228 /* interchange */
1229 selvertex = workingset[selpos];
1230 workingset[selpos] = workingset[nexts];
1231 workingset[nexts] = selvertex;
1232
1233 /* create new workingset */
1234 nextsnew = 0;
1235 for (j = 0 ; j < nexts; ++j)
1236 {
1237 if ( isConnectedSOS1(adjacencymatrix, NULL, selvertex, workingset[j]) )
1238 workingsetnew[nextsnew++] = workingset[j];
1239 }
1240 nworkingsetnew = nextsnew;
1241 for (j = nexts + 1; j < nworkingset; ++j)
1242 {
1243 if ( isConnectedSOS1(adjacencymatrix, NULL, selvertex, workingset[j]) )
1244 workingsetnew[nworkingsetnew++] = workingset[j];
1245 }
1246
1247 newclique[cliquesizes[*ncliques]++] = selvertex;
1248
1249 /* if we found a new clique */
1250 if ( nworkingsetnew == 0 )
1251 {
1252 char consname[SCIP_MAXSTRLEN];
1253 SCIP_CONSDATA* consdata;
1254 SCIP_CONS* newcons;
1255 int cliqueind;
1256
1257 cliqueind = nsos1vars + *ncliques; /* index of clique in the vertex-clique graph */
1258
1259 /* save new clique */
1260 assert( cliquesizes[*ncliques] >= 0 && cliquesizes[*ncliques] <= nsos1vars );
1261 assert( *ncliques < MAX(1, conshdlrdata->maxextensions) * nconss );
1262 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(cliques[*ncliques]), cliquesizes[*ncliques]) );/*lint !e866*/
1263 for (j = 0 ; j < cliquesizes[*ncliques]; ++j)
1264 {
1265 vars[j] = SCIPnodeGetVarSOS1(conshdlrdata->conflictgraph, newclique[j]);
1266 weights[j] = j+1;
1267 cliques[*ncliques][j] = newclique[j];
1268 }
1269
1270 SCIPsortInt(cliques[*ncliques], cliquesizes[*ncliques]);
1271
1272 /* create new constraint */
1273 (void) SCIPsnprintf(consname, SCIP_MAXSTRLEN, "extsos1_%d", conshdlrdata->cntextsos1);
1274
1275 SCIP_CALL( SCIPcreateConsSOS1(scip, &newcons, consname, cliquesizes[*ncliques], vars, weights,
1276 SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons),
1277 SCIPconsIsEnforced(cons), SCIPconsIsChecked(cons),
1278 SCIPconsIsPropagated(cons), SCIPconsIsLocal(cons),
1279 SCIPconsIsDynamic(cons),
1280 SCIPconsIsRemovable(cons), SCIPconsIsStickingAtNode(cons)) );
1281
1282 consdata = SCIPconsGetData(newcons);
1283
1284 /* add directed edges to the vertex-clique graph */
1285 for (j = 0; j < consdata->nvars; ++j)
1286 {
1287 /* add arc from clique vertex to clique (needed in presolRoundConssSOS1() to delete redundand cliques) */
1288 SCIP_CALL( SCIPdigraphAddArcSafe(vertexcliquegraph, cliques[*ncliques][j], cliqueind, NULL) );
1289 }
1290
1291 SCIP_CALL( SCIPaddCons(scip, newcons) );
1292 SCIP_CALL( SCIPreleaseCons(scip, &newcons) );
1293
1294 ++(*naddconss);
1295 ++(conshdlrdata->cntextsos1);
1296 ++(*ncliques);
1297 cliquesizes[*ncliques] = cliquesizes[*ncliques-1]; /* cliquesizes[*ncliques] = size of newclique */
1298
1299 *success = TRUE;
1300
1301 --(*maxextensions);
1302
1303 if ( *maxextensions <= 0 )
1304 {
1305 SCIPfreeBufferArray(scip, &workingsetnew);
1306 return SCIP_OKAY;
1307 }
1308 }
1309 else if ( nextsnew < nworkingsetnew ) /* else if the number of of candidates equals zero */
1310 {
1311 /* if backtracking is used, it is necessary to keep the memory for 'workingsetnew' */
1312 if ( usebacktrack )
1313 {
1314 SCIP_CALL( extensionOperatorSOS1(scip, conshdlrdata, adjacencymatrix, vertexcliquegraph, nsos1vars, nconss, cons, vars, weights, FALSE, usebacktrack,
1315 cliques, ncliques, cliquesizes, newclique, workingsetnew, nworkingsetnew, nextsnew, pos, maxextensions, naddconss, success) );
1316 if ( *maxextensions <= 0 )
1317 {
1318 SCIPfreeBufferArrayNull(scip, &workingsetnew);
1319 return SCIP_OKAY;
1320 }
1321 }
1322 else
1323 {
1324 int w;
1325
1326 assert( nworkingset >= nworkingsetnew );
1327 for (w = 0; w < nworkingsetnew; ++w)
1328 workingset[w] = workingsetnew[w];
1329 nworkingset = nworkingsetnew;
1330
1331 SCIPfreeBufferArrayNull(scip, &workingsetnew);
1332
1333 SCIP_CALL( extensionOperatorSOS1(scip, conshdlrdata, adjacencymatrix, vertexcliquegraph, nsos1vars, nconss, cons, vars, weights, FALSE, usebacktrack,
1334 cliques, ncliques, cliquesizes, newclique, workingset, nworkingset, nextsnew, pos, maxextensions, naddconss, success) );
1335 assert( *maxextensions <= 0 );
1336 return SCIP_OKAY;
1337 }
1338 }
1339 assert( workingsetnew != NULL );
1340 assert( workingset != NULL );
1341
1342 /* remove selvertex from clique */
1343 --cliquesizes[*ncliques];
1344
1345 /* add selvertex to the set of vertices that already served as extension */
1346 ++nexts;
1347
1348 if ( btriter > 1 )
1349 {
1350 /* select a candidate that is not connected to the fixed vertex */
1351 for (j = nexts; j < nworkingset; ++j)
1352 {
1353 assert( fixvertex != workingset[j] );
1354 if ( ! isConnectedSOS1(adjacencymatrix, NULL, fixvertex, workingset[j]) )
1355 {
1356 selpos = j;
1357 break;
1358 }
1359 }
1360 }
1361 }
1362
1363 SCIPfreeBufferArrayNull(scip, &workingsetnew);
1364
1365 return SCIP_OKAY;
1366 }
1367
1368
1369 /** generates conflict graph that is induced by the variables of a linear constraint */
1370 static
genConflictgraphLinearCons(SCIP_CONSHDLRDATA * conshdlrdata,SCIP_DIGRAPH * conflictgraphlin,SCIP_DIGRAPH * conflictgraphorig,SCIP_VAR ** linvars,int nlinvars,int * posinlinvars)1371 SCIP_RETCODE genConflictgraphLinearCons(
1372 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
1373 SCIP_DIGRAPH* conflictgraphlin, /**< conflict graph of linear constraint (nodes: 1, ..., nlinvars) */
1374 SCIP_DIGRAPH* conflictgraphorig, /**< original conflict graph (nodes: 1, ..., nsos1vars) */
1375 SCIP_VAR** linvars, /**< linear variables in linear constraint */
1376 int nlinvars, /**< number of linear variables in linear constraint */
1377 int* posinlinvars /**< posinlinvars[i] = position (index) of SOS1 variable i in linear constraint,
1378 * posinlinvars[i]= -1 if @p i is not a SOS1 variable or not a variable of the linear constraint */
1379 )
1380 {
1381 int indexinsosvars;
1382 int indexinlinvars;
1383 int* succ;
1384 int nsucc;
1385 int v;
1386 int s;
1387
1388 assert( conflictgraphlin != NULL );
1389 assert( conflictgraphorig != NULL );
1390 assert( linvars != NULL );
1391 assert( posinlinvars != NULL );
1392
1393 for (v = 1; v < nlinvars; ++v) /* we start with v = 1, since "indexinlinvars < v" (see below) is never fulfilled for v = 0 */
1394 {
1395 indexinsosvars = varGetNodeSOS1(conshdlrdata, linvars[v]);
1396
1397 /* if linvars[v] is contained in at least one SOS1 constraint */
1398 if ( indexinsosvars >= 0 )
1399 {
1400 succ = SCIPdigraphGetSuccessors(conflictgraphorig, indexinsosvars);
1401 nsucc = SCIPdigraphGetNSuccessors(conflictgraphorig, indexinsosvars);
1402
1403 for (s = 0; s < nsucc; ++s)
1404 {
1405 assert( succ[s] >= 0 );
1406 indexinlinvars = posinlinvars[succ[s]];
1407 assert( indexinlinvars < nlinvars );
1408
1409 if ( indexinlinvars >= 0 && indexinlinvars < v )
1410 {
1411 SCIP_CALL( SCIPdigraphAddArcSafe(conflictgraphlin, v, indexinlinvars, NULL) );
1412 SCIP_CALL( SCIPdigraphAddArcSafe(conflictgraphlin, indexinlinvars, v, NULL) );
1413 }
1414 }
1415 }
1416 }
1417
1418 return SCIP_OKAY;
1419 }
1420
1421
1422 /** determine the common successors of the vertices from the considered clique */
1423 static
cliqueGetCommonSuccessorsSOS1(SCIP_CONSHDLRDATA * conshdlrdata,SCIP_DIGRAPH * conflictgraph,int * clique,SCIP_VAR ** vars,int nvars,int * comsucc,int * ncomsucc)1424 SCIP_RETCODE cliqueGetCommonSuccessorsSOS1(
1425 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
1426 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
1427 int* clique, /**< current clique */
1428 SCIP_VAR** vars, /**< clique variables */
1429 int nvars, /**< number of clique variables */
1430 int* comsucc, /**< pointer to store common successors of clique vertices (size = nvars) */
1431 int* ncomsucc /**< pointer to store number common successors of clique vertices */
1432 )
1433 {
1434 int nsucc;
1435 int* succ;
1436 int ind;
1437 int k = 0;
1438 int v;
1439 int i;
1440 int j;
1441
1442 assert( conflictgraph != NULL );
1443 assert( clique != NULL );
1444 assert( vars != NULL );
1445 assert( comsucc != NULL );
1446 assert( ncomsucc != NULL );
1447
1448 *ncomsucc = 0;
1449
1450 /* determine the common successors of the vertices from the considered clique */
1451
1452 /* determine successors of variable var[0] that are not in the clique */
1453 assert(vars[0] != NULL );
1454 ind = varGetNodeSOS1(conshdlrdata, vars[0]);
1455
1456 if( ind == -1 )
1457 return SCIP_INVALIDDATA;
1458
1459 assert( ind < SCIPdigraphGetNNodes(conflictgraph) );
1460 nsucc = SCIPdigraphGetNSuccessors(conflictgraph, ind);
1461 succ = SCIPdigraphGetSuccessors(conflictgraph, ind);
1462
1463 for (j = 0; j < nvars; ++j)
1464 {
1465 for (i = k; i < nsucc; ++i)
1466 {
1467 if ( succ[i] > clique[j] )
1468 {
1469 k = i;
1470 break;
1471 }
1472 else if ( succ[i] == clique[j] )
1473 {
1474 k = i + 1;
1475 break;
1476 }
1477 else
1478 comsucc[(*ncomsucc)++] = succ[i];
1479 }
1480 }
1481
1482 /* for all variables except the first one */
1483 for (v = 1; v < nvars; ++v)
1484 {
1485 int ncomsuccsave = 0;
1486 k = 0;
1487
1488 assert(vars[v] != NULL );
1489 ind = varGetNodeSOS1(conshdlrdata, vars[v]);
1490 assert( ind >= 0 && ind < SCIPdigraphGetNNodes(conflictgraph) );
1491
1492 if ( ind >= 0 )
1493 {
1494 nsucc = SCIPdigraphGetNSuccessors(conflictgraph, ind);
1495 succ = SCIPdigraphGetSuccessors(conflictgraph, ind);
1496
1497 /* determine successors that are in comsucc */
1498 for (j = 0; j < *ncomsucc; ++j)
1499 {
1500 for (i = k; i < nsucc; ++i)
1501 {
1502 if ( succ[i] > comsucc[j] )
1503 {
1504 k = i;
1505 break;
1506 }
1507 else if ( succ[i] == comsucc[j] )
1508 {
1509 comsucc[ncomsuccsave++] = succ[i];
1510 k = i + 1;
1511 break;
1512 }
1513 }
1514 }
1515 *ncomsucc = ncomsuccsave;
1516 }
1517 }
1518
1519 return SCIP_OKAY;
1520 }
1521
1522
1523 /** get nodes whose corresponding SOS1 variables are nonzero if an SOS1 variable of a given node is nonzero */
1524 static
getSOS1Implications(SCIP * scip,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_VAR ** vars,SCIP_DIGRAPH * implgraph,SCIP_HASHMAP * implhash,SCIP_Bool * implnodes,int node)1525 SCIP_RETCODE getSOS1Implications(
1526 SCIP* scip, /**< SCIP pointer */
1527 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
1528 SCIP_VAR** vars, /**< problem and SOS1 variables */
1529 SCIP_DIGRAPH* implgraph, /**< implication graph (@p j is successor of @p i if and only if \f$ x_i\not = 0 \Rightarrow x_j\not = 0\f$) */
1530 SCIP_HASHMAP* implhash, /**< hash map from variable to node in implication graph */
1531 SCIP_Bool* implnodes, /**< implnodes[i] = TRUE if the SOS1 variable corresponding to node i in the implication graph is implied to be nonzero */
1532 int node /**< node of the implication graph */
1533 )
1534 {
1535 SCIP_SUCCDATA** succdatas;
1536 int sos1node;
1537 int* succ;
1538 int nsucc;
1539 int s;
1540
1541 assert( scip != NULL );
1542 assert( implgraph != NULL );
1543 assert( implnodes != NULL );
1544 assert( node >= 0 );
1545 assert( vars[node] != NULL );
1546 assert( SCIPhashmapGetImageInt(implhash, vars[node]) == node );
1547
1548 /* get node of variable in the conflict graph (-1 if variable is no SOS1 variable) */
1549 sos1node = varGetNodeSOS1(conshdlrdata, vars[node]);
1550 if ( sos1node < 0 )
1551 return SCIP_OKAY;
1552
1553 succdatas = (SCIP_SUCCDATA**) SCIPdigraphGetSuccessorsData(implgraph, node);
1554 nsucc = SCIPdigraphGetNSuccessors(implgraph, node);
1555 succ = SCIPdigraphGetSuccessors(implgraph, node);
1556
1557 for (s = 0; s < nsucc; ++s)
1558 {
1559 SCIP_SUCCDATA* data;
1560 int succnode;
1561 succnode = succ[s];
1562 data = succdatas[s];
1563 sos1node = varGetNodeSOS1(conshdlrdata, vars[succnode]);
1564
1565 /* if node is SOS1 and the corresponding variable is implied to be nonzero */
1566 assert( succdatas[s] != NULL );
1567 if ( sos1node >= 0 && ! implnodes[sos1node] && ( SCIPisFeasPositive(scip, data->lbimpl) || SCIPisFeasNegative(scip, data->ubimpl) ) )
1568 {
1569 assert( sos1node == succnode );
1570 implnodes[sos1node] = TRUE;
1571 SCIP_CALL( getSOS1Implications(scip, conshdlrdata, vars, implgraph, implhash, implnodes, succnode) );
1572 }
1573 }
1574
1575 return SCIP_OKAY;
1576 }
1577
1578
1579 /** perform one presolving round for a single SOS1 constraint
1580 *
1581 * We perform the following presolving steps.
1582 *
1583 * - If the bounds of some variable force it to be nonzero, we can
1584 * fix all other variables to zero and remove the SOS1 constraints
1585 * that contain it.
1586 * - If a variable is fixed to zero, we can remove the variable.
1587 * - If a variable appears twice, it can be fixed to 0.
1588 * - We substitute appregated variables.
1589 */
1590 static
presolRoundConsSOS1(SCIP * scip,SCIP_CONS * cons,SCIP_CONSDATA * consdata,SCIP_EVENTHDLR * eventhdlr,SCIP_Bool * substituted,SCIP_Bool * cutoff,SCIP_Bool * success,int * ndelconss,int * nupgdconss,int * nfixedvars,int * nremovedvars)1591 SCIP_RETCODE presolRoundConsSOS1(
1592 SCIP* scip, /**< SCIP pointer */
1593 SCIP_CONS* cons, /**< constraint */
1594 SCIP_CONSDATA* consdata, /**< constraint data */
1595 SCIP_EVENTHDLR* eventhdlr, /**< event handler */
1596 SCIP_Bool* substituted, /**< whether a variable was substituted */
1597 SCIP_Bool* cutoff, /**< whether a cutoff happened */
1598 SCIP_Bool* success, /**< whether we performed a successful reduction */
1599 int* ndelconss, /**< number of deleted constraints */
1600 int* nupgdconss, /**< number of upgraded constraints */
1601 int* nfixedvars, /**< number of fixed variables */
1602 int* nremovedvars /**< number of variables removed */
1603 )
1604 {
1605 SCIP_VAR** vars;
1606 SCIP_Bool allvarsbinary;
1607 SCIP_Bool infeasible;
1608 SCIP_Bool fixed;
1609 int nfixednonzeros;
1610 int lastFixedNonzero;
1611 int j;
1612
1613 assert( scip != NULL );
1614 assert( cons != NULL );
1615 assert( consdata != NULL );
1616 assert( eventhdlr != NULL );
1617 assert( cutoff != NULL );
1618 assert( success != NULL );
1619 assert( ndelconss != NULL );
1620 assert( nfixedvars != NULL );
1621 assert( nremovedvars != NULL );
1622
1623 *substituted = FALSE;
1624 *cutoff = FALSE;
1625 *success = FALSE;
1626
1627 SCIPdebugMsg(scip, "Presolving SOS1 constraint <%s>.\n", SCIPconsGetName(cons) );
1628
1629 j = 0;
1630 nfixednonzeros = 0;
1631 lastFixedNonzero = -1;
1632 allvarsbinary = TRUE;
1633 vars = consdata->vars;
1634
1635 /* check for variables fixed to 0 and bounds that fix a variable to be nonzero */
1636 while ( j < consdata->nvars )
1637 {
1638 int l;
1639 SCIP_VAR* var;
1640 SCIP_Real lb;
1641 SCIP_Real ub;
1642 SCIP_Real scalar;
1643 SCIP_Real constant;
1644
1645 scalar = 1.0;
1646 constant = 0.0;
1647
1648 /* check for aggregation: if the constant is zero the variable is zero iff the aggregated
1649 * variable is 0 */
1650 var = vars[j];
1651 SCIP_CALL( SCIPgetProbvarSum(scip, &var, &scalar, &constant) );
1652
1653 /* if constant is zero and we get a different variable, substitute variable */
1654 if ( SCIPisZero(scip, constant) && ! SCIPisZero(scip, scalar) && var != vars[j] )
1655 {
1656 SCIPdebugMsg(scip, "substituted variable <%s> by <%s>.\n", SCIPvarGetName(vars[j]), SCIPvarGetName(var));
1657 SCIP_CALL( SCIPdropVarEvent(scip, consdata->vars[j], EVENTHDLR_EVENT_TYPE, eventhdlr, (SCIP_EVENTDATA*)cons, -1) ); /*lint !e740*/
1658 SCIP_CALL( SCIPcatchVarEvent(scip, var, EVENTHDLR_EVENT_TYPE, eventhdlr, (SCIP_EVENTDATA*)cons, NULL) ); /*lint !e740*/
1659
1660 /* change the rounding locks */
1661 SCIP_CALL( unlockVariableSOS1(scip, cons, consdata->vars[j]) );
1662 SCIP_CALL( lockVariableSOS1(scip, cons, var) );
1663
1664 vars[j] = var;
1665 *substituted = TRUE;
1666 }
1667
1668 /* check whether the variable appears again later */
1669 for (l = j+1; l < consdata->nvars; ++l)
1670 {
1671 /* if variable appeared before, we can fix it to 0 and remove it */
1672 if ( vars[j] == vars[l] )
1673 {
1674 SCIPdebugMsg(scip, "variable <%s> appears twice in constraint, fixing it to 0.\n", SCIPvarGetName(vars[j]));
1675 SCIP_CALL( SCIPfixVar(scip, vars[j], 0.0, &infeasible, &fixed) );
1676
1677 if ( infeasible )
1678 {
1679 *cutoff = TRUE;
1680 return SCIP_OKAY;
1681 }
1682 if ( fixed )
1683 ++(*nfixedvars);
1684 }
1685 }
1686
1687 /* get bounds */
1688 lb = SCIPvarGetLbLocal(vars[j]);
1689 ub = SCIPvarGetUbLocal(vars[j]);
1690
1691 /* if the variable if fixed to nonzero */
1692 if ( SCIPisFeasPositive(scip, lb) || SCIPisFeasNegative(scip, ub) )
1693 {
1694 ++nfixednonzeros;
1695 lastFixedNonzero = j;
1696 }
1697
1698 /* if the variable is fixed to 0 */
1699 if ( SCIPisFeasZero(scip, lb) && SCIPisFeasZero(scip, ub) )
1700 {
1701 SCIPdebugMsg(scip, "deleting variable <%s> fixed to 0.\n", SCIPvarGetName(vars[j]));
1702 SCIP_CALL( deleteVarSOS1(scip, cons, consdata, eventhdlr, j) );
1703 ++(*nremovedvars);
1704 }
1705 else
1706 {
1707 /* check whether all variables are binary */
1708 if ( ! SCIPvarIsBinary(vars[j]) )
1709 allvarsbinary = FALSE;
1710
1711 ++j;
1712 }
1713 }
1714
1715 /* if the number of variables is less than 2 */
1716 if ( consdata->nvars < 2 )
1717 {
1718 SCIPdebugMsg(scip, "Deleting SOS1 constraint <%s> with < 2 variables.\n", SCIPconsGetName(cons));
1719
1720 /* delete constraint */
1721 assert( ! SCIPconsIsModifiable(cons) );
1722 SCIP_CALL( SCIPdelCons(scip, cons) );
1723 ++(*ndelconss);
1724 *success = TRUE;
1725 return SCIP_OKAY;
1726 }
1727
1728 /* if more than one variable are fixed to be nonzero, we are infeasible */
1729 if ( nfixednonzeros > 1 )
1730 {
1731 SCIPdebugMsg(scip, "The problem is infeasible: more than one variable has bounds that keep it from being 0.\n");
1732 assert( lastFixedNonzero >= 0 );
1733 *cutoff = TRUE;
1734 return SCIP_OKAY;
1735 }
1736
1737 /* if there is exactly one fixed nonzero variable */
1738 if ( nfixednonzeros == 1 )
1739 {
1740 assert( lastFixedNonzero >= 0 );
1741
1742 /* fix all other variables to zero */
1743 for (j = 0; j < consdata->nvars; ++j)
1744 {
1745 if ( j != lastFixedNonzero )
1746 {
1747 SCIP_CALL( fixVariableZero(scip, vars[j], &infeasible, &fixed) );
1748 if ( infeasible )
1749 {
1750 *cutoff = TRUE;
1751 return SCIP_OKAY;
1752 }
1753 if ( fixed )
1754 ++(*nfixedvars);
1755 }
1756 }
1757
1758 SCIPdebugMsg(scip, "Deleting redundant SOS1 constraint <%s> with one variable.\n", SCIPconsGetName(cons));
1759
1760 /* delete original constraint */
1761 assert( ! SCIPconsIsModifiable(cons) );
1762 SCIP_CALL( SCIPdelCons(scip, cons) );
1763 ++(*ndelconss);
1764 *success = TRUE;
1765 }
1766 /* note: there is no need to update consdata->nfixednonzeros, since the constraint is deleted as soon nfixednonzeros > 0. */
1767 else
1768 {
1769 /* if all variables are binary create a set packing constraint */
1770 if ( allvarsbinary && SCIPfindConshdlr(scip, "setppc") != NULL )
1771 {
1772 SCIP_CONS* setpackcons;
1773
1774 /* create, add, and release the logicor constraint */
1775 SCIP_CALL( SCIPcreateConsSetpack(scip, &setpackcons, SCIPconsGetName(cons), consdata->nvars, consdata->vars,
1776 SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), SCIPconsIsEnforced(cons), SCIPconsIsChecked(cons),
1777 SCIPconsIsPropagated(cons), SCIPconsIsLocal(cons), SCIPconsIsModifiable(cons), SCIPconsIsDynamic(cons),
1778 SCIPconsIsRemovable(cons), SCIPconsIsStickingAtNode(cons)) );
1779 SCIP_CALL( SCIPaddCons(scip, setpackcons) );
1780 SCIP_CALL( SCIPreleaseCons(scip, &setpackcons) );
1781
1782 SCIPdebugMsg(scip, "Upgrading SOS1 constraint <%s> to set packing constraint.\n", SCIPconsGetName(cons));
1783
1784 /* remove the SOS1 constraint globally */
1785 assert( ! SCIPconsIsModifiable(cons) );
1786 SCIP_CALL( SCIPdelCons(scip, cons) );
1787 ++(*nupgdconss);
1788 *success = TRUE;
1789 }
1790 }
1791
1792 return SCIP_OKAY;
1793 }
1794
1795
1796
1797 /** perform one presolving round for all SOS1 constraints
1798 *
1799 * We perform the following presolving steps.
1800 *
1801 * - If the bounds of some variable force it to be nonzero, we can
1802 * fix all other variables to zero and remove the SOS1 constraints
1803 * that contain it.
1804 * - If a variable is fixed to zero, we can remove the variable.
1805 * - If a variable appears twice, it can be fixed to 0.
1806 * - We substitute appregated variables.
1807 * - Remove redundant SOS1 constraints
1808 *
1809 * If the adjacency matrix of the conflict graph is present, then
1810 * we perform the following additional presolving steps
1811 *
1812 * - Search for larger SOS1 constraints in the conflict graph
1813 *
1814 * @todo Use one long array for storing cliques.
1815 */
1816 static
presolRoundConssSOS1(SCIP * scip,SCIP_EVENTHDLR * eventhdlr,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_DIGRAPH * conflictgraph,SCIP_Bool ** adjacencymatrix,SCIP_CONS ** conss,int nconss,int nsos1vars,int * naddconss,int * ndelconss,int * nupgdconss,int * nfixedvars,int * nremovedvars,SCIP_RESULT * result)1817 SCIP_RETCODE presolRoundConssSOS1(
1818 SCIP* scip, /**< SCIP pointer */
1819 SCIP_EVENTHDLR* eventhdlr, /**< event handler */
1820 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
1821 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
1822 SCIP_Bool** adjacencymatrix, /**< adjacency matrix of conflict graph (or NULL) */
1823 SCIP_CONS** conss, /**< SOS1 constraints */
1824 int nconss, /**< number of SOS1 constraints */
1825 int nsos1vars, /**< number of SOS1 variables */
1826 int* naddconss, /**< number of added constraints */
1827 int* ndelconss, /**< number of deleted constraints */
1828 int* nupgdconss, /**< number of upgraded constraints */
1829 int* nfixedvars, /**< number of fixed variables */
1830 int* nremovedvars, /**< number of variables removed */
1831 SCIP_RESULT* result /**< result */
1832 )
1833 {
1834 SCIP_DIGRAPH* vertexcliquegraph;
1835 SCIP_VAR** consvars;
1836 SCIP_Real* consweights;
1837 int** cliques = NULL;
1838 int ncliques = 0;
1839 int* cliquesizes = NULL;
1840 int* newclique = NULL;
1841 int* indconss = NULL;
1842 int* lengthconss = NULL;
1843 int* comsucc = NULL;
1844 int csize;
1845 int iter;
1846 int c;
1847
1848 assert( scip != NULL );
1849 assert( eventhdlr != NULL );
1850 assert( conshdlrdata != NULL );
1851 assert( conflictgraph != NULL );
1852 assert( conss != NULL );
1853 assert( naddconss != NULL );
1854 assert( ndelconss != NULL );
1855 assert( nupgdconss != NULL );
1856 assert( nfixedvars != NULL );
1857 assert( nremovedvars != NULL );
1858 assert( result != NULL );
1859
1860 /* create digraph whose nodes represent variables and cliques in the conflict graph */
1861 csize = MAX(1, conshdlrdata->maxextensions) * nconss;
1862 SCIP_CALL( SCIPcreateDigraph(scip, &vertexcliquegraph, nsos1vars + csize) );
1863
1864 /* allocate buffer arrays */
1865 SCIP_CALL( SCIPallocBufferArray(scip, &consvars, nsos1vars) );
1866 SCIP_CALL( SCIPallocBufferArray(scip, &consweights, nsos1vars) );
1867 SCIP_CALL( SCIPallocBufferArray(scip, &newclique, nsos1vars) );
1868 SCIP_CALL( SCIPallocBufferArray(scip, &indconss, csize) );
1869 SCIP_CALL( SCIPallocBufferArray(scip, &lengthconss, csize) );
1870 SCIP_CALL( SCIPallocBufferArray(scip, &comsucc, MAX(nsos1vars, csize)) );
1871
1872 /* Use block memory for cliques, because sizes might be quite different and allocation interfers with workingset. */
1873 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &cliquesizes, csize) );
1874 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &cliques, csize) );
1875
1876 /* get constraint indices and sort them in descending order of their lengths */
1877 for (c = 0; c < nconss; ++c)
1878 {
1879 SCIP_CONSDATA* consdata;
1880
1881 consdata = SCIPconsGetData(conss[c]);
1882 assert( consdata != NULL );
1883
1884 indconss[c] = c;
1885 lengthconss[c] = consdata->nvars;
1886 }
1887 SCIPsortDownIntInt(lengthconss, indconss, nconss);
1888
1889 /* check each constraint */
1890 for (iter = 0; iter < nconss; ++iter)
1891 {
1892 SCIP_CONSDATA* consdata;
1893 SCIP_CONS* cons;
1894 SCIP_Bool substituted;
1895 SCIP_Bool success;
1896 SCIP_Bool cutoff;
1897 int savennupgdconss;
1898 int savendelconss;
1899
1900 SCIP_VAR** vars;
1901 int nvars;
1902
1903 c = indconss[iter];
1904
1905 assert( conss != NULL );
1906 assert( conss[c] != NULL );
1907 cons = conss[c];
1908 consdata = SCIPconsGetData(cons);
1909
1910 assert( consdata != NULL );
1911 assert( consdata->nvars >= 0 );
1912 assert( consdata->nvars <= consdata->maxvars );
1913 assert( ! SCIPconsIsModifiable(cons) );
1914 assert( ncliques < csize );
1915
1916 savendelconss = *ndelconss;
1917 savennupgdconss = *nupgdconss;
1918
1919 /* perform one presolving round for SOS1 constraint */
1920 SCIP_CALL( presolRoundConsSOS1(scip, cons, consdata, eventhdlr, &substituted, &cutoff, &success, ndelconss, nupgdconss, nfixedvars, nremovedvars) );
1921
1922 if ( cutoff )
1923 {
1924 *result = SCIP_CUTOFF;
1925 break;
1926 }
1927
1928 if ( *ndelconss > savendelconss || *nupgdconss > savennupgdconss || substituted )
1929 {
1930 *result = SCIP_SUCCESS;
1931 continue;
1932 }
1933
1934 if ( success )
1935 *result = SCIP_SUCCESS;
1936
1937 /* get number of variables of constraint */
1938 nvars = consdata->nvars;
1939
1940 /* get variables of constraint */
1941 vars = consdata->vars;
1942
1943 if ( nvars > 1 && conshdlrdata->maxextensions != 0 )
1944 {
1945 SCIP_Bool extended = FALSE;
1946 int cliquesize = 0;
1947 int ncomsucc = 0;
1948 int varprobind;
1949 int j;
1950
1951 /* get clique and size of clique */
1952 for (j = 0; j < nvars; ++j)
1953 {
1954 varprobind = varGetNodeSOS1(conshdlrdata, vars[j]);
1955
1956 if ( varprobind >= 0 )
1957 newclique[cliquesize++] = varprobind;
1958 }
1959
1960 if ( cliquesize > 1 )
1961 {
1962 cliquesizes[ncliques] = cliquesize;
1963
1964 /* sort clique vertices */
1965 SCIPsortInt(newclique, cliquesizes[ncliques]);
1966
1967 /* check if clique is contained in an already known clique */
1968 if ( ncliques > 0 )
1969 {
1970 int* succ;
1971 int nsucc;
1972 int v;
1973
1974 varprobind = newclique[0];
1975 ncomsucc = SCIPdigraphGetNSuccessors(vertexcliquegraph, varprobind);
1976 succ = SCIPdigraphGetSuccessors(vertexcliquegraph, varprobind);
1977
1978 /* get all (already processed) cliques that contain 'varpropind' */
1979 for (j = 0; j < ncomsucc; ++j)
1980 {
1981 /* successors should have been sorted in a former step of the algorithm */
1982 assert( j == 0 || succ[j] > succ[j-1] );
1983 comsucc[j] = succ[j];
1984 }
1985
1986 /* loop through remaining nodes of clique (case v = 0 already processed) */
1987 for (v = 1; v < cliquesize && ncomsucc > 0; ++v)
1988 {
1989 varprobind = newclique[v];
1990
1991 /* get all (already processed) cliques that contain 'varpropind' */
1992 nsucc = SCIPdigraphGetNSuccessors(vertexcliquegraph, varprobind);
1993 succ = SCIPdigraphGetSuccessors(vertexcliquegraph, varprobind);
1994 assert( succ != NULL || nsucc == 0 );
1995
1996 if ( nsucc < 1 )
1997 {
1998 ncomsucc = 0;
1999 break;
2000 }
2001
2002 /* get intersection with comsucc */
2003 SCIP_CALL( SCIPcomputeArraysIntersection(comsucc, ncomsucc, succ, nsucc, comsucc, &ncomsucc) );
2004 }
2005 }
2006
2007 /* if constraint is redundand then delete it */
2008 if ( ncomsucc > 0 )
2009 {
2010 assert( ! SCIPconsIsModifiable(cons) );
2011 SCIP_CALL( SCIPdelCons(scip, cons) );
2012 ++(*ndelconss);
2013 *result = SCIP_SUCCESS;
2014 continue;
2015 }
2016
2017 if ( conshdlrdata->maxextensions != 0 && adjacencymatrix != NULL )
2018 {
2019 int maxextensions;
2020 ncomsucc = 0;
2021
2022 /* determine the common successors of the vertices from the considered clique */
2023 SCIP_CALL( cliqueGetCommonSuccessorsSOS1(conshdlrdata, conflictgraph, newclique, vars, nvars, comsucc, &ncomsucc) );
2024
2025 /* find extensions for the clique */
2026 maxextensions = conshdlrdata->maxextensions;
2027 extended = FALSE;
2028 SCIP_CALL( extensionOperatorSOS1(scip, conshdlrdata, adjacencymatrix, vertexcliquegraph, nsos1vars, nconss, cons, consvars, consweights,
2029 TRUE, (maxextensions <= 1) ? FALSE : TRUE, cliques, &ncliques, cliquesizes, newclique, comsucc, ncomsucc, 0, -1, &maxextensions,
2030 naddconss, &extended) );
2031 }
2032
2033 /* if an extension was found for the current clique then free the old SOS1 constraint */
2034 if ( extended )
2035 {
2036 assert( ! SCIPconsIsModifiable(cons) );
2037 SCIP_CALL( SCIPdelCons(scip, cons) );
2038 ++(*ndelconss);
2039 *result = SCIP_SUCCESS;
2040 }
2041 else /* if we keep the constraint */
2042 {
2043 int cliqueind;
2044
2045 cliqueind = nsos1vars + ncliques; /* index of clique in vertex-clique graph */
2046
2047 /* add directed edges to the vertex-clique graph */
2048 assert( cliquesize >= 0 && cliquesize <= nsos1vars );
2049 assert( ncliques < csize );
2050 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &cliques[ncliques], cliquesize) );/*lint !e866*/
2051 for (j = 0; j < cliquesize; ++j)
2052 {
2053 cliques[ncliques][j] = newclique[j];
2054 SCIP_CALL( SCIPdigraphAddArcSafe(vertexcliquegraph, cliques[ncliques][j], cliqueind, NULL) );
2055 }
2056
2057 /* update number of maximal cliques */
2058 ++ncliques;
2059 }
2060 }
2061 }
2062 }
2063
2064 /* free buffer arrays */
2065 for (c = ncliques-1; c >= 0; --c)
2066 SCIPfreeBlockMemoryArray(scip, &cliques[c], cliquesizes[c]);
2067 SCIPfreeBlockMemoryArrayNull(scip, &cliques, csize);
2068 SCIPfreeBlockMemoryArrayNull(scip, &cliquesizes, csize);
2069
2070 SCIPfreeBufferArrayNull(scip, &comsucc);
2071 SCIPfreeBufferArrayNull(scip, &lengthconss);
2072 SCIPfreeBufferArrayNull(scip, &indconss);
2073 SCIPfreeBufferArrayNull(scip, &newclique);
2074 SCIPfreeBufferArrayNull(scip, &consweights);
2075 SCIPfreeBufferArrayNull(scip, &consvars);
2076 SCIPdigraphFree(&vertexcliquegraph);
2077
2078 return SCIP_OKAY;
2079 }
2080
2081
2082 /** performs implication graph analysis
2083 *
2084 * Tentatively fixes a variable to nonzeero and extracts consequences from it:
2085 * - adds (possibly new) complementarity constraints to the problem if variables are implied to be zero
2086 * - returns that the subproblem is infeasible if the domain of a variable turns out to be empty
2087 */
2088 static
performImplicationGraphAnalysis(SCIP * scip,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_DIGRAPH * conflictgraph,SCIP_VAR ** totalvars,SCIP_DIGRAPH * implgraph,SCIP_HASHMAP * implhash,SCIP_Bool ** adjacencymatrix,int givennode,int nonznode,SCIP_Real * impllbs,SCIP_Real * implubs,SCIP_Bool * implnodes,int * naddconss,int * probingdepth,SCIP_Bool * infeasible)2089 SCIP_RETCODE performImplicationGraphAnalysis(
2090 SCIP* scip, /**< SCIP pointer */
2091 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
2092 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
2093 SCIP_VAR** totalvars, /**< problem and SOS1 variables */
2094 SCIP_DIGRAPH* implgraph, /**< implication graph (@p j is successor of @p i if and only if \f$ x_i\not = 0 \Rightarrow x_j\not = 0\f$) */
2095 SCIP_HASHMAP* implhash, /**< hash map from variable to node in implication graph */
2096 SCIP_Bool** adjacencymatrix, /**< adjacencymatrix of the conflict graph (only lower half filled) */
2097 int givennode, /**< node of the conflict graph */
2098 int nonznode, /**< node of the conflict graph that is implied to be nonzero if given node is nonzero */
2099 SCIP_Real* impllbs, /**< current lower variable bounds if given node is nonzero (update possible) */
2100 SCIP_Real* implubs, /**< current upper variable bounds if given node is nonzero (update possible) */
2101 SCIP_Bool* implnodes, /**< indicates which variables are currently implied to be nonzero if given node is nonzero (update possible) */
2102 int* naddconss, /**< pointer to store number of added SOS1 constraints */
2103 int* probingdepth, /**< pointer to store current probing depth */
2104 SCIP_Bool* infeasible /**< pointer to store whether the subproblem gets infeasible if variable to 'nonznode' is nonzero */
2105 )
2106 {
2107 SCIP_SUCCDATA** succdatas;
2108 int succnode;
2109 int* succ;
2110 int nsucc;
2111 int s;
2112
2113 assert( nonznode >= 0 && nonznode < SCIPdigraphGetNNodes(conflictgraph) );
2114
2115 /* check probing depth */
2116 if ( conshdlrdata->depthimplanalysis >= 0 && *probingdepth >= conshdlrdata->depthimplanalysis )
2117 return SCIP_OKAY;
2118 ++(*probingdepth);
2119
2120 /* get successors of 'nonznode' in the conflict graph */
2121 nsucc = SCIPdigraphGetNSuccessors(conflictgraph, nonznode);
2122 succ = SCIPdigraphGetSuccessors(conflictgraph, nonznode);
2123
2124 /* loop through neighbors of 'nonznode' in the conflict graph; these variables are implied to be zero */
2125 for (s = 0; s < nsucc; ++s)
2126 {
2127 succnode = succ[s];
2128
2129 /* if the current variable domain of the successor node does not contain the value zero then return that the problem is infeasible
2130 * else if 'succnode' is not already complementary to 'givennode' then add a new complementarity constraint */
2131 if ( givennode == succnode || SCIPisFeasPositive(scip, impllbs[succnode]) || SCIPisFeasNegative(scip, implubs[succnode]) )
2132 {
2133 *infeasible = TRUE;
2134 return SCIP_OKAY;
2135 }
2136 else if ( ! isConnectedSOS1(adjacencymatrix, NULL, givennode, succnode) )
2137 {
2138 char namesos[SCIP_MAXSTRLEN];
2139 SCIP_CONS* soscons = NULL;
2140 SCIP_VAR* var1;
2141 SCIP_VAR* var2;
2142
2143 /* update implied bounds of succnode */
2144 impllbs[succnode] = 0;
2145 implubs[succnode] = 0;
2146
2147 /* add arcs to the conflict graph */
2148 SCIP_CALL( SCIPdigraphAddArcSafe(conflictgraph, givennode, succnode, NULL) );
2149 SCIP_CALL( SCIPdigraphAddArcSafe(conflictgraph, succnode, givennode, NULL) );
2150
2151 /* resort successors */
2152 SCIPsortInt(SCIPdigraphGetSuccessors(conflictgraph, givennode), SCIPdigraphGetNSuccessors(conflictgraph, givennode));
2153 SCIPsortInt(SCIPdigraphGetSuccessors(conflictgraph, succnode), SCIPdigraphGetNSuccessors(conflictgraph, succnode));
2154
2155 /* update adjacencymatrix */
2156 if ( givennode > succnode )
2157 adjacencymatrix[givennode][succnode] = 1;
2158 else
2159 adjacencymatrix[succnode][givennode] = 1;
2160
2161 var1 = SCIPnodeGetVarSOS1(conflictgraph, givennode);
2162 var2 = SCIPnodeGetVarSOS1(conflictgraph, succnode);
2163
2164 /* create SOS1 constraint */
2165 assert( SCIPgetDepth(scip) == 0 );
2166 (void) SCIPsnprintf(namesos, SCIP_MAXSTRLEN, "presolved_sos1_%s_%s", SCIPvarGetName(var1), SCIPvarGetName(var2) );
2167 SCIP_CALL( SCIPcreateConsSOS1(scip, &soscons, namesos, 0, NULL, NULL, TRUE, TRUE, TRUE, FALSE, TRUE,
2168 FALSE, FALSE, FALSE, FALSE) );
2169
2170 /* add variables to SOS1 constraint */
2171 SCIP_CALL( addVarSOS1(scip, soscons, conshdlrdata, var1, 1.0) );
2172 SCIP_CALL( addVarSOS1(scip, soscons, conshdlrdata, var2, 2.0) );
2173
2174 /* add constraint */
2175 SCIP_CALL( SCIPaddCons(scip, soscons) );
2176
2177 /* release constraint */
2178 SCIP_CALL( SCIPreleaseCons(scip, &soscons) );
2179
2180 ++(*naddconss);
2181 }
2182 }
2183
2184 /* by construction: nodes of SOS1 variables are equal for conflict graph and implication graph */
2185 assert( nonznode == SCIPhashmapGetImageInt(implhash, SCIPnodeGetVarSOS1(conflictgraph, nonznode)) );
2186 succdatas = (SCIP_SUCCDATA**) SCIPdigraphGetSuccessorsData(implgraph, nonznode);
2187 nsucc = SCIPdigraphGetNSuccessors(implgraph, nonznode);
2188 succ = SCIPdigraphGetSuccessors(implgraph, nonznode);
2189
2190 /* go further in implication graph */
2191 for (s = 0; s < nsucc; ++s)
2192 {
2193 SCIP_SUCCDATA* data;
2194 int oldprobingdepth;
2195
2196 succnode = succ[s];
2197 data = succdatas[s];
2198 oldprobingdepth = *probingdepth;
2199
2200 /* if current lower bound is smaller than implied lower bound */
2201 if ( SCIPisFeasLT(scip, impllbs[succnode], data->lbimpl) )
2202 {
2203 impllbs[succnode] = data->lbimpl;
2204
2205 /* if node is SOS1 and implied to be nonzero for the first time, then this recursively may imply further bound changes */
2206 if ( varGetNodeSOS1(conshdlrdata, totalvars[succnode]) >= 0 && ! implnodes[succnode] && SCIPisFeasPositive(scip, data->lbimpl) )
2207 {
2208 /* by construction: nodes of SOS1 variables are equal for conflict graph and implication graph */
2209 assert( succnode == SCIPhashmapGetImageInt(implhash, SCIPnodeGetVarSOS1(conflictgraph, succnode)) );
2210 implnodes[succnode] = TRUE; /* in order to avoid cycling */
2211 SCIP_CALL( performImplicationGraphAnalysis(scip, conshdlrdata, conflictgraph, totalvars, implgraph, implhash, adjacencymatrix, givennode, succnode, impllbs, implubs, implnodes, naddconss, probingdepth, infeasible) );
2212 *probingdepth = oldprobingdepth;
2213
2214 /* return if the subproblem is known to be infeasible */
2215 if ( *infeasible )
2216 return SCIP_OKAY;
2217 }
2218 }
2219
2220 /* if current upper bound is larger than implied upper bound */
2221 if ( SCIPisFeasGT(scip, implubs[succnode], data->ubimpl) )
2222 {
2223 implubs[succnode] = data->ubimpl;
2224
2225 /* if node is SOS1 and implied to be nonzero for the first time, then this recursively may imply further bound changes */
2226 if ( varGetNodeSOS1(conshdlrdata, totalvars[succnode]) >= 0 && ! implnodes[succnode] && SCIPisFeasNegative(scip, data->ubimpl) )
2227 {
2228 /* by construction: nodes of SOS1 variables are equal for conflict graph and implication graph */
2229 assert( succnode == SCIPhashmapGetImageInt(implhash, SCIPnodeGetVarSOS1(conflictgraph, succnode)) );
2230 implnodes[succnode] = TRUE; /* in order to avoid cycling */
2231 SCIP_CALL( performImplicationGraphAnalysis(scip, conshdlrdata, conflictgraph, totalvars, implgraph, implhash, adjacencymatrix, givennode, succnode, impllbs, implubs, implnodes, naddconss, probingdepth, infeasible) );
2232 *probingdepth = oldprobingdepth;
2233
2234 /* return if the subproblem is known to be infeasible */
2235 if ( *infeasible )
2236 return SCIP_OKAY;
2237 }
2238 }
2239 }
2240
2241 return SCIP_OKAY;
2242 }
2243
2244
2245 /** returns whether node is implied to be zero; this information is taken from the input array 'implnodes' */
2246 static
isImpliedZero(SCIP_DIGRAPH * conflictgraph,SCIP_Bool * implnodes,int node)2247 SCIP_Bool isImpliedZero(
2248 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
2249 SCIP_Bool* implnodes, /**< implnodes[i] = TRUE if the SOS1 variable corresponding to node i in the implication graph is implied to be nonzero */
2250 int node /**< node of the conflict graph (or -1) */
2251 )
2252 {
2253 int* succ;
2254 int nsucc;
2255 int s;
2256
2257 if ( node < 0 )
2258 return FALSE;
2259
2260 nsucc = SCIPdigraphGetNSuccessors(conflictgraph, node);
2261 succ = SCIPdigraphGetSuccessors(conflictgraph, node);
2262
2263 /* check whether any successor is implied to be nonzero */
2264 for (s = 0; s < nsucc; ++s)
2265 {
2266 if ( implnodes[succ[s]] )
2267 return TRUE;
2268 }
2269
2270 return FALSE;
2271 }
2272
2273
2274 /** updates arc data of implication graph */
2275 static
updateArcData(SCIP * scip,SCIP_DIGRAPH * implgraph,SCIP_HASHMAP * implhash,SCIP_VAR ** totalvars,SCIP_VAR * varv,SCIP_VAR * varw,SCIP_Real lb,SCIP_Real ub,SCIP_Real newbound,SCIP_Bool lower,int * nchgbds,SCIP_Bool * update,SCIP_Bool * infeasible)2276 SCIP_RETCODE updateArcData(
2277 SCIP* scip, /**< SCIP pointer */
2278 SCIP_DIGRAPH* implgraph, /**< implication graph */
2279 SCIP_HASHMAP* implhash, /**< hash map from variable to node in implication graph */
2280 SCIP_VAR** totalvars, /**< problem and SOS1 variables */
2281 SCIP_VAR* varv, /**< variable that is assumed to be nonzero */
2282 SCIP_VAR* varw, /**< implication variable */
2283 SCIP_Real lb, /**< old lower bound of \f$x_w\f$ */
2284 SCIP_Real ub, /**< old upper bound of \f$x_w\f$ */
2285 SCIP_Real newbound, /**< new bound of \f$x_w\f$ */
2286 SCIP_Bool lower, /**< whether to consider lower bound implication (otherwise upper bound) */
2287 int* nchgbds, /**< pointer to store number of changed bounds */
2288 SCIP_Bool* update, /**< pointer to store whether implication graph has been updated */
2289 SCIP_Bool* infeasible /**< pointer to store whether an infeasibility has been detected */
2290 )
2291 {
2292 SCIP_SUCCDATA** succdatas;
2293 SCIP_SUCCDATA* data = NULL;
2294 int nsucc;
2295 int* succ;
2296 int indv;
2297 int indw;
2298 int s;
2299
2300 assert( scip != NULL );
2301 assert( implgraph != NULL );
2302 assert( implhash != NULL );
2303 assert( totalvars != NULL );
2304 assert( varv != NULL );
2305 assert( varw != NULL );
2306
2307 /* if x_v != 0 turns out to be infeasible then fix x_v = 0 */
2308 if ( ( lower && SCIPisFeasLT(scip, ub, newbound) ) || ( ! lower && SCIPisFeasGT(scip, lb, newbound) ) )
2309 {
2310 SCIP_Bool infeasible1;
2311 SCIP_Bool infeasible2;
2312 SCIP_Bool tightened1;
2313 SCIP_Bool tightened2;
2314
2315 SCIP_CALL( SCIPtightenVarLb(scip, varv, 0.0, FALSE, &infeasible1, &tightened1) );
2316 SCIP_CALL( SCIPtightenVarUb(scip, varv, 0.0, FALSE, &infeasible2, &tightened2) );
2317
2318 if ( infeasible1 || infeasible2 )
2319 {
2320 SCIPdebugMsg(scip, "detected infeasibility while trying to fix variable <%s> to zero\n", SCIPvarGetName(varv));
2321 *infeasible = TRUE;
2322 }
2323
2324 if ( tightened1 || tightened2 )
2325 {
2326 SCIPdebugMsg(scip, "fixed variable %s from lb = %f and ub = %f to 0.0 \n", SCIPvarGetName(varv), lb, ub);
2327 ++(*nchgbds);
2328 }
2329 }
2330
2331 /* get successor information */
2332 indv = SCIPhashmapGetImageInt(implhash, varv); /* get index of x_v in implication graph */
2333 assert( SCIPhashmapGetImageInt(implhash, totalvars[indv]) == indv );
2334 succdatas = (SCIP_SUCCDATA**) SCIPdigraphGetSuccessorsData(implgraph, indv);
2335 nsucc = SCIPdigraphGetNSuccessors(implgraph, indv);
2336 succ = SCIPdigraphGetSuccessors(implgraph, indv);
2337
2338 /* search for nodew in existing successors. If this is the case then check whether the lower implication bound may be updated ... */
2339 indw = SCIPhashmapGetImageInt(implhash, varw);
2340 assert( SCIPhashmapGetImageInt(implhash, totalvars[indw]) == indw );
2341 for (s = 0; s < nsucc; ++s)
2342 {
2343 if ( succ[s] == indw )
2344 {
2345 data = succdatas[s];
2346 assert( data != NULL );
2347 if ( lower && SCIPisFeasLT(scip, data->lbimpl, newbound) )
2348 {
2349 if ( SCIPvarIsIntegral(varw) )
2350 data->lbimpl = SCIPceil(scip, newbound);
2351 else
2352 data->lbimpl = newbound;
2353
2354 *update = TRUE;
2355 SCIPdebugMsg(scip, "updated to implication %s != 0 -> %s >= %f\n", SCIPvarGetName(varv), SCIPvarGetName(varw), newbound);
2356 }
2357 else if ( ! lower && SCIPisFeasGT(scip, data->ubimpl, newbound) )
2358 {
2359 if ( SCIPvarIsIntegral(varw) )
2360 data->ubimpl = SCIPfloor(scip, newbound);
2361 else
2362 data->ubimpl = newbound;
2363
2364 *update = TRUE;
2365 SCIPdebugMsg(scip, "updated to implication %s != 0 -> %s >= %f\n", SCIPvarGetName(varv), SCIPvarGetName(varw), newbound);
2366 }
2367 break;
2368 }
2369 }
2370
2371 /* ..., otherwise if there does not exist an arc between indv and indw already, then create one and add implication */
2372 if ( s == nsucc )
2373 {
2374 assert( data == NULL );
2375 SCIP_CALL( SCIPallocBlockMemory(scip, &data) );
2376 if ( lower )
2377 {
2378 data->lbimpl = newbound;
2379 data->ubimpl = ub;
2380 SCIPdebugMsg(scip, "add implication %s != 0 -> %s >= %f\n", SCIPvarGetName(varv), SCIPvarGetName(varw), newbound);
2381 }
2382 else
2383 {
2384 data->lbimpl = lb;
2385 data->ubimpl = newbound;
2386 SCIPdebugMsg(scip, "add implication %s != 0 -> %s <= %f\n", SCIPvarGetName(varv), SCIPvarGetName(varw), newbound);
2387 }
2388 SCIP_CALL( SCIPdigraphAddArc(implgraph, indv, indw, (void*)data) );
2389 *update = TRUE;
2390 }
2391
2392 return SCIP_OKAY;
2393 }
2394
2395
2396 /** updates implication graph
2397 *
2398 * Assume the variable from the input is nonzero. If this implies that some other variable is also nonzero, then
2399 * store this information in an implication graph
2400 */
2401 static
updateImplicationGraphSOS1(SCIP * scip,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_DIGRAPH * conflictgraph,SCIP_Bool ** adjacencymatrix,SCIP_DIGRAPH * implgraph,SCIP_HASHMAP * implhash,SCIP_Bool * implnodes,SCIP_VAR ** totalvars,int ** cliquecovers,int * cliquecoversizes,int * varincover,SCIP_VAR ** vars,SCIP_Real * coefs,int nvars,SCIP_Real * bounds,SCIP_VAR * var,SCIP_Real bound,SCIP_Real boundnonzero,int ninftynonzero,SCIP_Bool lower,int * nchgbds,SCIP_Bool * update,SCIP_Bool * infeasible)2402 SCIP_RETCODE updateImplicationGraphSOS1(
2403 SCIP* scip, /**< SCIP pointer */
2404 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
2405 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
2406 SCIP_Bool** adjacencymatrix, /**< adjacency matrix of conflict graph (lower half) */
2407 SCIP_DIGRAPH* implgraph, /**< implication graph (@p j is successor of @p i if and only if \f$ x_i\not = 0 \Rightarrow x_j\not = 0\f$) */
2408 SCIP_HASHMAP* implhash, /**< hash map from variable to node in implication graph */
2409 SCIP_Bool* implnodes, /**< implnodes[i] = TRUE if the SOS1 variable corresponding to node i in the implication graph is implied to be nonzero */
2410 SCIP_VAR** totalvars, /**< problem and SOS1 variables */
2411 int** cliquecovers, /**< clique covers of linear constraint */
2412 int* cliquecoversizes, /**< size of clique covers */
2413 int* varincover, /**< array with varincover[i] = cover of SOS1 index @p i */
2414 SCIP_VAR** vars, /**< variables to be checked */
2415 SCIP_Real* coefs, /**< coefficients of variables in linear constraint */
2416 int nvars, /**< number of variables to be checked */
2417 SCIP_Real* bounds, /**< bounds of variables */
2418 SCIP_VAR* var, /**< variable that is assumed to be nonzero */
2419 SCIP_Real bound, /**< bound of variable */
2420 SCIP_Real boundnonzero, /**< bound of variable if it is known to be nonzero if infinity values are not summarized */
2421 int ninftynonzero, /**< number of times infinity/-infinity has to be summarized to boundnonzero */
2422 SCIP_Bool lower, /**< TRUE if lower bounds are consideres; FALSE for upper bounds */
2423 int* nchgbds, /**< pointer to store number of changed bounds */
2424 SCIP_Bool* update, /**< pointer to store whether implication graph has been updated */
2425 SCIP_Bool* infeasible /**< pointer to store whether an infeasibility has been detected */
2426 )
2427 {
2428 int nodev;
2429 int w;
2430
2431 assert( update != NULL );
2432
2433 /* update implication graph if possible */
2434 *update = FALSE;
2435 *infeasible = FALSE;
2436 nodev = varGetNodeSOS1(conshdlrdata, var); /* possibly -1 if var is not involved in an SOS1 constraint */
2437
2438 /* if nodev is an index of an SOS1 variable and at least one lower bound of a variable that is not x_v is infinity */
2439 if ( nodev < 0 || SCIPisInfinity(scip, REALABS(bound)) || ninftynonzero > 1 )
2440 return SCIP_OKAY;
2441
2442 /* for every variable x_w: compute upper bound of a_w * x_w if x_v is known to be nonzero */
2443 for (w = 0; w < nvars; ++w)
2444 {
2445 int newninftynonzero;
2446 SCIP_Bool implinfty = FALSE;
2447 int nodew;
2448
2449 /* get node of x_w in conflict graph: nodew = -1 if it is no SOS1 variable */
2450 nodew = varGetNodeSOS1(conshdlrdata, vars[w]);
2451
2452 newninftynonzero = ninftynonzero;
2453
2454 /* variable should not be fixed to be already zero (note x_v is fixed to be nonzero by assumption) */
2455 if ( nodew < 0 || ( nodev != nodew && ! isConnectedSOS1(adjacencymatrix, NULL, nodev, nodew) && ! isImpliedZero(conflictgraph, implnodes, nodew) ) )
2456 {
2457 SCIP_Real implbound;
2458 SCIP_Bool implcoverw;
2459 int nodecliq;
2460 int indcliq;
2461 int ind;
2462 int j;
2463
2464 /* boundnonzero is the bound of x_v if x_v is nonzero we use this information to get a bound of x_w if x_v is
2465 * nonzero; therefore, we have to perform some recomputations */
2466 implbound = boundnonzero - bound;
2467 ind = varincover[w];
2468 assert( cliquecoversizes[ind] > 0 );
2469
2470 implcoverw = FALSE;
2471 for (j = 0; j < cliquecoversizes[ind]; ++j)
2472 {
2473 indcliq = cliquecovers[ind][j];
2474 assert( 0 <= indcliq && indcliq < nvars );
2475
2476 nodecliq = varGetNodeSOS1(conshdlrdata, vars[indcliq]); /* possibly -1 if variable is not involved in an SOS1 constraint */
2477
2478 /* if nodecliq is not a member of an SOS1 constraint or the variable corresponding to nodecliq is not implied to be zero if x_v != 0 */
2479 if ( nodecliq < 0 || (! isConnectedSOS1(adjacencymatrix, NULL, nodev, nodecliq) && ! isImpliedZero(conflictgraph, implnodes, nodecliq) ) )
2480 {
2481 if ( indcliq == w )
2482 {
2483 if ( !SCIPisInfinity(scip, REALABS(bounds[w])) && !SCIPisInfinity(scip, REALABS(implbound + bounds[w])) )
2484 implbound += bounds[w];
2485 else
2486 --newninftynonzero;
2487 implcoverw = TRUE;
2488 }
2489 else if ( implcoverw )
2490 {
2491 if ( SCIPisInfinity(scip, REALABS(bounds[indcliq])) || SCIPisInfinity(scip, REALABS(implbound - bounds[indcliq])) )
2492 implinfty = TRUE;
2493 else
2494 implbound -= bounds[indcliq];
2495 break;
2496 }
2497 else
2498 {
2499 if ( SCIPisInfinity(scip, REALABS(bounds[indcliq])) )
2500 implinfty = TRUE;
2501 break;
2502 }
2503 }
2504 }
2505
2506 /* check whether x_v != 0 implies a bound change of x_w */
2507 if ( ! implinfty && newninftynonzero == 0 )
2508 {
2509 SCIP_Real newbound;
2510 SCIP_Real coef;
2511 SCIP_Real lb;
2512 SCIP_Real ub;
2513
2514 lb = SCIPvarGetLbLocal(vars[w]);
2515 ub = SCIPvarGetUbLocal(vars[w]);
2516 coef = coefs[w];
2517
2518 if ( SCIPisFeasZero(scip, coef) )
2519 continue;
2520
2521 newbound = implbound / coef;
2522
2523 if ( SCIPisInfinity(scip, newbound) )
2524 continue;
2525
2526 /* check if an implication can be added/updated or assumption x_v != 0 is infeasible */
2527 if ( lower )
2528 {
2529 if ( SCIPisFeasPositive(scip, coef) && SCIPisFeasLT(scip, lb, newbound) )
2530 {
2531 SCIP_CALL( updateArcData(scip, implgraph, implhash, totalvars, var, vars[w], lb, ub, newbound, TRUE, nchgbds, update, infeasible) );
2532 }
2533 else if ( SCIPisFeasNegative(scip, coef) && SCIPisFeasGT(scip, ub, newbound) )
2534 {
2535 SCIP_CALL( updateArcData(scip, implgraph, implhash, totalvars, var, vars[w], lb, ub, newbound, FALSE, nchgbds, update, infeasible) );
2536 }
2537 }
2538 else
2539 {
2540 if ( SCIPisFeasPositive(scip, coef) && SCIPisFeasGT(scip, ub, newbound) )
2541 {
2542 SCIP_CALL( updateArcData(scip, implgraph, implhash, totalvars, var, vars[w], lb, ub, newbound, FALSE, nchgbds, update, infeasible) );
2543 }
2544 else if ( SCIPisFeasNegative(scip, coef) && SCIPisFeasLT(scip, lb, newbound) )
2545 {
2546 SCIP_CALL( updateArcData(scip, implgraph, implhash, totalvars, var, vars[w], lb, ub, newbound, TRUE, nchgbds, update, infeasible) );
2547 }
2548 }
2549 }
2550 }
2551 }
2552
2553 return SCIP_OKAY;
2554 }
2555
2556
2557 /** search new disjoint clique that covers given node
2558 *
2559 * For a given vertex @p v search for a clique of the conflict graph induced by the variables of a linear constraint that
2560 * - covers @p v and
2561 * - has an an empty intersection with already computed clique cover.
2562 */
2563 static
computeVarsCoverSOS1(SCIP * scip,SCIP_DIGRAPH * conflictgraphroot,SCIP_DIGRAPH * conflictgraphlin,SCIP_VAR ** linvars,SCIP_Bool * coveredvars,int * clique,int * cliquesize,int v,SCIP_Bool considersolvals)2564 SCIP_RETCODE computeVarsCoverSOS1(
2565 SCIP* scip, /**< SCIP pointer */
2566 SCIP_DIGRAPH* conflictgraphroot, /**< conflict graph of the root node (nodes: 1, ..., @p nsos1vars) */
2567 SCIP_DIGRAPH* conflictgraphlin, /**< conflict graph of linear constraint (nodes: 1, ..., @p nlinvars) */
2568 SCIP_VAR** linvars, /**< variables in linear constraint */
2569 SCIP_Bool* coveredvars, /**< states which variables of the linear constraint are currently covered by a clique */
2570 int* clique, /**< array to store new clique in cover */
2571 int* cliquesize, /**< pointer to store the size of @p clique */
2572 int v, /**< position of variable in linear constraint that should be covered */
2573 SCIP_Bool considersolvals /**< TRUE if largest auxiliary bigM values of variables should be prefered */
2574 )
2575 {
2576 int nsucc;
2577 int s;
2578
2579 assert( conflictgraphlin != NULL );
2580 assert( linvars != NULL );
2581 assert( coveredvars != NULL );
2582 assert( clique != NULL );
2583 assert( cliquesize != NULL );
2584
2585 assert( ! coveredvars[v] ); /* we should produce a new clique */
2586
2587 /* add index 'v' to the clique cover */
2588 clique[0] = v;
2589 *cliquesize = 1;
2590
2591 nsucc = SCIPdigraphGetNSuccessors(conflictgraphlin, v);
2592 if ( nsucc > 0 )
2593 {
2594 int* extensions;
2595 int nextensions = 0;
2596 int nextensionsnew;
2597 int succnode;
2598 int* succ;
2599
2600 /* allocate buffer array */
2601 SCIP_CALL( SCIPallocBufferArray(scip, &extensions, nsucc) );
2602
2603 succ = SCIPdigraphGetSuccessors(conflictgraphlin, v);
2604
2605 /* compute possible extensions for the clique cover */
2606 for (s = 0; s < nsucc; ++s)
2607 {
2608 succnode = succ[s];
2609 if ( ! coveredvars[succnode] )
2610 extensions[nextensions++] = succ[s];
2611 }
2612
2613 /* while there exist possible extensions for the clique cover */
2614 while ( nextensions > 0 )
2615 {
2616 int bestindex = -1;
2617
2618 if ( considersolvals )
2619 {
2620 SCIP_Real bestbigMval;
2621 SCIP_Real bigMval;
2622
2623 bestbigMval = -SCIPinfinity(scip);
2624
2625 /* search for the extension with the largest absolute value of its LP relaxation solution value */
2626 for (s = 0; s < nextensions; ++s)
2627 {
2628 bigMval = nodeGetSolvalBinaryBigMSOS1(scip, conflictgraphroot, NULL, extensions[s]);
2629 if ( SCIPisFeasLT(scip, bestbigMval, bigMval) )
2630 {
2631 bestbigMval = bigMval;
2632 bestindex = extensions[s];
2633 }
2634 }
2635 }
2636 else
2637 bestindex = extensions[0];
2638
2639 assert( bestindex != -1 );
2640
2641 /* add bestindex to the clique cover */
2642 clique[(*cliquesize)++] = bestindex;
2643
2644 /* compute new 'extensions' array */
2645 nextensionsnew = 0;
2646 for (s = 0; s < nextensions; ++s)
2647 {
2648 if ( s != bestindex && isConnectedSOS1(NULL, conflictgraphlin, bestindex, extensions[s]) )
2649 extensions[nextensionsnew++] = extensions[s];
2650 }
2651 nextensions = nextensionsnew;
2652 }
2653
2654 /* free buffer array */
2655 SCIPfreeBufferArray(scip, &extensions);
2656 }
2657
2658 /* mark covered indices */
2659 for (s = 0; s < *cliquesize; ++s)
2660 {
2661 int ind;
2662
2663 ind = clique[s];
2664 assert( 0 <= ind );
2665 assert( ! coveredvars[ind] );
2666 coveredvars[ind] = TRUE;
2667 }
2668
2669 return SCIP_OKAY;
2670 }
2671
2672
2673 /** try to tighten upper and lower bounds for variables */
2674 static
tightenVarsBoundsSOS1(SCIP * scip,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_DIGRAPH * conflictgraph,SCIP_DIGRAPH * implgraph,SCIP_HASHMAP * implhash,SCIP_Bool ** adjacencymatrix,SCIP_VAR ** totalvars,int ntotalvars,int nsos1vars,int * nchgbds,SCIP_Bool * implupdate,SCIP_Bool * cutoff)2675 SCIP_RETCODE tightenVarsBoundsSOS1(
2676 SCIP* scip, /**< SCIP pointer */
2677 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
2678 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
2679 SCIP_DIGRAPH* implgraph, /**< implication graph (@p j is successor of @p i if and only if \f$ x_i\not = 0 \f$ implies a new lower/upper bound for \f$ x_j\f$) */
2680 SCIP_HASHMAP* implhash, /**< hash map from variable to node in implication graph */
2681 SCIP_Bool** adjacencymatrix, /**< adjacencymatrix of conflict graph */
2682 SCIP_VAR** totalvars, /**< problem and SOS1 vars */
2683 int ntotalvars, /**< number of problem and SOS1 variables*/
2684 int nsos1vars, /**< number of SOS1 variables */
2685 int* nchgbds, /**< pointer to store number of changed bounds */
2686 SCIP_Bool* implupdate, /**< pointer to store whether the implication graph has been updated in this function call */
2687 SCIP_Bool* cutoff /**< pointer to store if current nodes LP is infeasible */
2688 )
2689 {
2690 SCIP_CONSHDLR* conshdlrlinear;
2691 SCIP_CONS** linearconss;
2692 int nlinearconss;
2693
2694 SCIP_Bool* implnodes = NULL; /* implnodes[i] = TRUE if the SOS1 variable corresponding to node i in the implication graph is implied to be nonzero */
2695 SCIP_Bool* coveredvars = NULL; /* coveredvars[i] = TRUE if variable with index i is covered by the clique cover */
2696 int* varindincons = NULL; /* varindincons[i] = position of SOS1 index i in linear constraint (-1 if x_i is not involved in linear constraint) */
2697
2698 SCIP_VAR** trafolinvars = NULL; /* variables of transformed linear constraints without (multi)aggregated variables */
2699 int ntrafolinvars = 0;
2700 SCIP_Real* trafolinvals = NULL;
2701 SCIP_Real* trafoubs = NULL;
2702 SCIP_Real* trafolbs = NULL;
2703 SCIP_Real traforhs;
2704 SCIP_Real trafolhs;
2705
2706 SCIP_VAR** sos1linvars = NULL; /* variables that are not contained in linear constraint, but are in conflict with a variable from the linear constraint */
2707 int nsos1linvars;
2708 int c;
2709
2710 assert( scip != NULL );
2711 assert( conflictgraph != NULL );
2712 assert( adjacencymatrix != NULL );
2713 assert( nchgbds != NULL );
2714 assert( cutoff != NULL );
2715
2716 *cutoff = FALSE;
2717 *implupdate = FALSE;
2718
2719 /* get constraint handler data of linear constraints */
2720 conshdlrlinear = SCIPfindConshdlr(scip, "linear");
2721 if ( conshdlrlinear == NULL )
2722 return SCIP_OKAY;
2723
2724 /* get linear constraints and number of linear constraints */
2725 nlinearconss = SCIPconshdlrGetNConss(conshdlrlinear);
2726 linearconss = SCIPconshdlrGetConss(conshdlrlinear);
2727
2728 /* allocate buffer arrays */
2729 SCIP_CALL( SCIPallocBufferArray(scip, &sos1linvars, nsos1vars) );
2730 SCIP_CALL( SCIPallocBufferArray(scip, &implnodes, nsos1vars) );
2731 SCIP_CALL( SCIPallocBufferArray(scip, &varindincons, nsos1vars) );
2732 SCIP_CALL( SCIPallocBufferArray(scip, &coveredvars, ntotalvars) );
2733 SCIP_CALL( SCIPallocBufferArray(scip, &trafoubs, ntotalvars) );
2734 SCIP_CALL( SCIPallocBufferArray(scip, &trafolbs, ntotalvars) );
2735
2736 /* for every linear constraint and every SOS1 variable */
2737 for (c = 0; c < nlinearconss + nsos1vars && ! (*cutoff); ++c)
2738 {
2739 SCIP_DIGRAPH* conflictgraphlin;
2740 int** cliquecovers = NULL; /* clique covers of indices of variables in linear constraint */
2741 int* cliquecoversizes = NULL; /* size of each cover */
2742 SCIP_VAR* sosvar = NULL;
2743 SCIP_Real* cliquecovervals = NULL;
2744 SCIP_Real constant;
2745 int* varincover = NULL; /* varincover[i] = cover of SOS1 index i */
2746 int ncliquecovers;
2747 int requiredsize;
2748
2749 int v;
2750 int i;
2751 int j;
2752
2753 /* get transformed linear constraints (without aggregated variables) */
2754 if ( c < nlinearconss )
2755 {
2756 SCIP_VAR** origlinvars;
2757 SCIP_Real* origlinvals;
2758
2759 /* get data of linear constraint */
2760 ntrafolinvars = SCIPgetNVarsLinear(scip, linearconss[c]);
2761 if ( ntrafolinvars < 1 )
2762 continue;
2763
2764 origlinvars = SCIPgetVarsLinear(scip, linearconss[c]);
2765 origlinvals = SCIPgetValsLinear(scip, linearconss[c]);
2766 assert( origlinvars != NULL );
2767 assert( origlinvals != NULL );
2768
2769 /* copy variables and coefficients of linear constraint */
2770 SCIP_CALL( SCIPduplicateBufferArray(scip, &trafolinvars, origlinvars, ntrafolinvars) );
2771 SCIP_CALL( SCIPduplicateBufferArray(scip, &trafolinvals, origlinvals, ntrafolinvars) );
2772
2773 trafolhs = SCIPgetLhsLinear(scip, linearconss[c]);
2774 traforhs = SCIPgetRhsLinear(scip, linearconss[c]);
2775 }
2776 else
2777 {
2778 sosvar = SCIPnodeGetVarSOS1(conflictgraph, c - nlinearconss);
2779
2780 if ( SCIPvarGetStatus(sosvar) != SCIP_VARSTATUS_AGGREGATED
2781 && SCIPvarGetStatus(sosvar) != SCIP_VARSTATUS_MULTAGGR
2782 && SCIPvarGetStatus(sosvar) != SCIP_VARSTATUS_NEGATED )
2783 continue;
2784
2785 /* store variable so it will be transformed to active variables below */
2786 ntrafolinvars = 1;
2787 SCIP_CALL( SCIPallocBufferArray(scip, &trafolinvars, ntrafolinvars + 1) );
2788 SCIP_CALL( SCIPallocBufferArray(scip, &trafolinvals, ntrafolinvars + 1) );
2789
2790 trafolinvars[0] = sosvar;
2791 trafolinvals[0] = 1.0;
2792
2793 trafolhs = 0.0;
2794 traforhs = 0.0;
2795 }
2796 assert( ntrafolinvars >= 1 );
2797
2798 /* transform linear constraint */
2799 constant = 0.0;
2800 SCIP_CALL( SCIPgetProbvarLinearSum(scip, trafolinvars, trafolinvals, &ntrafolinvars, ntrafolinvars, &constant, &requiredsize, TRUE) );
2801 if( requiredsize > ntrafolinvars )
2802 {
2803 SCIP_CALL( SCIPreallocBufferArray(scip, &trafolinvars, requiredsize + 1) );
2804 SCIP_CALL( SCIPreallocBufferArray(scip, &trafolinvals, requiredsize + 1) );
2805
2806 SCIP_CALL( SCIPgetProbvarLinearSum(scip, trafolinvars, trafolinvals, &ntrafolinvars, requiredsize, &constant, &requiredsize, TRUE) );
2807 assert( requiredsize <= ntrafolinvars );
2808 }
2809 if( !SCIPisInfinity(scip, -trafolhs) )
2810 trafolhs -= constant;
2811 if( !SCIPisInfinity(scip, traforhs) )
2812 traforhs -= constant;
2813
2814 if ( ntrafolinvars == 0 )
2815 {
2816 SCIPfreeBufferArray(scip, &trafolinvals);
2817 SCIPfreeBufferArray(scip, &trafolinvars);
2818 continue;
2819 }
2820
2821 /* possibly add sos1 variable to create aggregation/multiaggregation/negation equality */
2822 if ( sosvar != NULL )
2823 {
2824 trafolinvals[ntrafolinvars] = -1.0;
2825 trafolinvars[ntrafolinvars] = sosvar;
2826 ++ntrafolinvars;
2827 }
2828
2829 /* compute lower and upper bounds of each term a_i * x_i of transformed constraint */
2830 for (v = 0; v < ntrafolinvars; ++v)
2831 {
2832 SCIP_Real lb;
2833 SCIP_Real ub;
2834
2835 lb = SCIPvarGetLbLocal(trafolinvars[v]);
2836 ub = SCIPvarGetUbLocal(trafolinvars[v]);
2837
2838 if ( trafolinvals[v] < 0.0 )
2839 SCIPswapReals(&lb, &ub);
2840
2841 assert( ! SCIPisInfinity(scip, REALABS(trafolinvals[v])) );
2842
2843 if ( SCIPisInfinity(scip, REALABS(lb)) || SCIPisInfinity(scip, REALABS(lb * trafolinvals[v])) )
2844 trafolbs[v] = -SCIPinfinity(scip);
2845 else
2846 trafolbs[v] = lb * trafolinvals[v];
2847
2848 if ( SCIPisInfinity(scip, REALABS(ub)) || SCIPisInfinity(scip, REALABS(ub * trafolinvals[v])) )
2849 trafoubs[v] = SCIPinfinity(scip);
2850 else
2851 trafoubs[v] = ub * trafolinvals[v];
2852 }
2853
2854 /* initialization: mark all the SOS1 variables as 'not a member of the linear constraint' */
2855 for (v = 0; v < nsos1vars; ++v)
2856 varindincons[v] = -1;
2857
2858 /* save position of SOS1 variables in linear constraint */
2859 for (v = 0; v < ntrafolinvars; ++v)
2860 {
2861 int node;
2862
2863 node = varGetNodeSOS1(conshdlrdata, trafolinvars[v]);
2864
2865 if ( node >= 0 )
2866 varindincons[node] = v;
2867 }
2868
2869 /* create conflict graph of linear constraint */
2870 SCIP_CALL( SCIPcreateDigraph(scip, &conflictgraphlin, ntrafolinvars) );
2871 SCIP_CALL( genConflictgraphLinearCons(conshdlrdata, conflictgraphlin, conflictgraph, trafolinvars, ntrafolinvars, varindincons) );
2872
2873 /* mark all the variables as 'not covered by some clique cover' */
2874 for (i = 0; i < ntrafolinvars; ++i)
2875 coveredvars[i] = FALSE;
2876
2877 /* allocate buffer array */
2878 SCIP_CALL( SCIPallocBufferArray(scip, &cliquecovervals, ntrafolinvars) );
2879 SCIP_CALL( SCIPallocBufferArray(scip, &cliquecoversizes, ntrafolinvars) );
2880 SCIP_CALL( SCIPallocBufferArray(scip, &cliquecovers, ntrafolinvars) );
2881
2882 /* compute distinct cliques that cover all the variables of the linear constraint */
2883 ncliquecovers = 0;
2884 for (v = 0; v < ntrafolinvars; ++v)
2885 {
2886 /* if variable is not already covered by an already known clique cover */
2887 if ( ! coveredvars[v] )
2888 {
2889 SCIP_CALL( SCIPallocBufferArray(scip, &(cliquecovers[ncliquecovers]), ntrafolinvars) ); /*lint !e866*/
2890 SCIP_CALL( computeVarsCoverSOS1(scip, conflictgraph, conflictgraphlin, trafolinvars, coveredvars, cliquecovers[ncliquecovers], &(cliquecoversizes[ncliquecovers]), v, FALSE) );
2891 ++ncliquecovers;
2892 }
2893 }
2894
2895 /* free conflictgraph */
2896 SCIPdigraphFree(&conflictgraphlin);
2897
2898 /* compute variables that are not contained in transformed linear constraint, but are in conflict with a variable from the transformed linear constraint */
2899 nsos1linvars = 0;
2900 for (v = 0; v < ntrafolinvars; ++v)
2901 {
2902 int nodev;
2903
2904 nodev = varGetNodeSOS1(conshdlrdata, trafolinvars[v]);
2905
2906 /* if variable is an SOS1 variable */
2907 if ( nodev >= 0 )
2908 {
2909 int succnode;
2910 int nsucc;
2911 int* succ;
2912 int s;
2913
2914 succ = SCIPdigraphGetSuccessors(conflictgraph, nodev);
2915 nsucc = SCIPdigraphGetNSuccessors(conflictgraph, nodev);
2916
2917 for (s = 0; s < nsucc; ++s)
2918 {
2919 succnode = succ[s];
2920
2921 /* if variable is not a member of linear constraint and not already listed in the array sos1linvars */
2922 if ( varindincons[succnode] == -1 )
2923 {
2924 sos1linvars[nsos1linvars] = SCIPnodeGetVarSOS1(conflictgraph, succnode);
2925 varindincons[succnode] = -2; /* mark variable as listed in array sos1linvars */
2926 ++nsos1linvars;
2927 }
2928 }
2929 }
2930 }
2931
2932 /* try to tighten lower bounds */
2933
2934 /* sort each cliquecover array in ascending order of the lower bounds of a_i * x_i; fill vector varincover */
2935 SCIP_CALL( SCIPallocBufferArray(scip, &varincover, ntrafolinvars) );
2936 for (i = 0; i < ncliquecovers; ++i)
2937 {
2938 for (j = 0; j < cliquecoversizes[i]; ++j)
2939 {
2940 int ind = cliquecovers[i][j];
2941
2942 varincover[ind] = i;
2943 cliquecovervals[j] = trafoubs[ind];
2944 }
2945 SCIPsortDownRealInt(cliquecovervals, cliquecovers[i], cliquecoversizes[i]);
2946 }
2947
2948 /* for every variable in transformed constraint: try lower bound tightening */
2949 for (v = 0; v < ntrafolinvars + nsos1linvars; ++v)
2950 {
2951 SCIP_Real newboundnonzero; /* new bound of a_v * x_v if we assume that x_v != 0 */
2952 SCIP_Real newboundnores; /* new bound of a_v * x_v if we assume that x_v = 0 is possible */
2953 SCIP_Real newbound; /* resulting new bound of x_v */
2954 SCIP_VAR* var;
2955 SCIP_Real trafoubv;
2956 SCIP_Real linval;
2957 SCIP_Real ub;
2958 SCIP_Real lb;
2959 SCIP_Bool tightened;
2960 SCIP_Bool infeasible;
2961 SCIP_Bool inftynores = FALSE;
2962 SCIP_Bool update;
2963 int ninftynonzero = 0;
2964 int nodev;
2965 int w;
2966
2967 if ( v < ntrafolinvars )
2968 {
2969 var = trafolinvars[v];
2970 trafoubv = trafoubs[v];
2971 }
2972 else
2973 {
2974 assert( v >= ntrafolinvars );
2975 var = sos1linvars[v-ntrafolinvars];/*lint !e679*/
2976 trafoubv = 0.0;
2977 }
2978
2979 ub = SCIPvarGetUbLocal(var);
2980 lb = SCIPvarGetLbLocal(var);
2981
2982 if ( SCIPisInfinity(scip, -trafolhs) || SCIPisZero(scip, ub - lb) )
2983 continue;
2984
2985 newboundnonzero = trafolhs;
2986 newboundnores = trafolhs;
2987 nodev = varGetNodeSOS1(conshdlrdata, var); /* possibly -1 if var is not involved in an SOS1 constraint */
2988 assert( nodev < nsos1vars );
2989
2990 /* determine incidence vector of implication variables */
2991 for (w = 0; w < nsos1vars; ++w)
2992 implnodes[w] = FALSE;
2993 SCIP_CALL( getSOS1Implications(scip, conshdlrdata, totalvars, implgraph, implhash, implnodes, SCIPhashmapGetImageInt(implhash, var)) );
2994
2995 /* compute new bound */
2996 for (i = 0; i < ncliquecovers; ++i)
2997 {
2998 int indcliq;
2999 int nodecliq;
3000
3001 assert( cliquecoversizes[i] > 0 );
3002
3003 indcliq = cliquecovers[i][0];
3004 assert( 0 <= indcliq && indcliq < ntrafolinvars );
3005
3006 /* determine maximum without index v (note that the array 'cliquecovers' is sorted by the values of trafoub in non-increasing order) */
3007 if ( v != indcliq )
3008 {
3009 if ( SCIPisInfinity(scip, trafoubs[indcliq]) || SCIPisInfinity(scip, REALABS(newboundnores - trafoubs[indcliq])) )
3010 inftynores = TRUE;
3011 else
3012 newboundnores -= trafoubs[indcliq];
3013 }
3014 else if ( cliquecoversizes[i] > 1 )
3015 {
3016 assert( 0 <= cliquecovers[i][1] && cliquecovers[i][1] < ntrafolinvars );
3017 if ( SCIPisInfinity(scip, trafoubs[cliquecovers[i][1]]) || SCIPisInfinity(scip, REALABS(newboundnores - trafoubs[cliquecovers[i][1]])) )
3018 inftynores = TRUE;
3019 else
3020 newboundnores -= trafoubs[cliquecovers[i][1]];/*lint --e{679}*/
3021 }
3022
3023 /* determine maximum without index v and if x_v is nonzero (note that the array 'cliquecovers' is sorted by the values of trafoub in non-increasing order) */
3024 for (j = 0; j < cliquecoversizes[i]; ++j)
3025 {
3026 indcliq = cliquecovers[i][j];
3027 assert( 0 <= indcliq && indcliq < ntrafolinvars );
3028
3029 nodecliq = varGetNodeSOS1(conshdlrdata, trafolinvars[indcliq]); /* possibly -1 if variable is not involved in an SOS1 constraint */
3030 assert( nodecliq < nsos1vars );
3031
3032 if ( v != indcliq )
3033 {
3034 /* if nodev or nodecliq are not a member of an SOS1 constraint or the variable corresponding to nodecliq is not implied to be zero if x_v != 0 */
3035 if ( nodev < 0 || nodecliq < 0 || (! isConnectedSOS1(adjacencymatrix, NULL, nodev, nodecliq) && ! isImpliedZero(conflictgraph, implnodes, nodecliq) ) )
3036 {
3037 if ( SCIPisInfinity(scip, trafoubs[indcliq]) || SCIPisInfinity(scip, REALABS(newboundnonzero - trafoubs[indcliq])) )
3038 ++ninftynonzero;
3039 else
3040 newboundnonzero -= trafoubs[indcliq];
3041 break; /* break since we are only interested in the maximum upper bound among the variables in the clique cover;
3042 * the variables in the clique cover form an SOS1 constraint, thus only one of them can be nonzero */
3043 }
3044 }
3045 }
3046 }
3047 assert( ninftynonzero == 0 || inftynores );
3048
3049 /* if computed upper bound is not infinity and variable is contained in linear constraint */
3050 if ( ninftynonzero == 0 && v < ntrafolinvars )
3051 {
3052 linval = trafolinvals[v];
3053
3054 if ( SCIPisFeasZero(scip, linval) )
3055 continue;
3056
3057 /* compute new bound */
3058 if ( SCIPisFeasPositive(scip, newboundnores) && ! inftynores )
3059 newbound = newboundnonzero;
3060 else
3061 newbound = MIN(0, newboundnonzero);
3062 newbound /= linval;
3063
3064 if ( SCIPisInfinity(scip, newbound) )
3065 continue;
3066
3067 /* check if new bound is tighter than the old one or problem is infeasible */
3068 if ( SCIPisFeasPositive(scip, linval) && SCIPisFeasLT(scip, lb, newbound) )
3069 {
3070 if ( SCIPisFeasLT(scip, ub, newbound) )
3071 {
3072 *cutoff = TRUE;
3073 break;
3074 }
3075
3076 if ( SCIPvarIsIntegral(var) )
3077 newbound = SCIPceil(scip, newbound);
3078
3079 SCIP_CALL( SCIPtightenVarLb(scip, var, newbound, FALSE, &infeasible, &tightened) );
3080 assert( ! infeasible );
3081
3082 if ( tightened )
3083 {
3084 SCIPdebugMsg(scip, "changed lower bound of variable %s from %f to %f \n", SCIPvarGetName(var), lb, newbound);
3085 ++(*nchgbds);
3086 }
3087 }
3088 else if ( SCIPisFeasNegative(scip, linval) && SCIPisFeasGT(scip, ub, newbound) )
3089 {
3090 /* if assumption a_i * x_i != 0 was not correct */
3091 if ( SCIPisFeasGT(scip, SCIPvarGetLbLocal(var), newbound) )
3092 {
3093 *cutoff = TRUE;
3094 break;
3095 }
3096
3097 if ( SCIPvarIsIntegral(var) )
3098 newbound = SCIPfloor(scip, newbound);
3099
3100 SCIP_CALL( SCIPtightenVarUb(scip, var, newbound, FALSE, &infeasible, &tightened) );
3101 assert( ! infeasible );
3102
3103 if ( tightened )
3104 {
3105 SCIPdebugMsg(scip, "changed upper bound of variable %s from %f to %f \n", SCIPvarGetName(var), ub, newbound);
3106 ++(*nchgbds);
3107 }
3108 }
3109 }
3110
3111 /* update implication graph if possible */
3112 SCIP_CALL( updateImplicationGraphSOS1(scip, conshdlrdata, conflictgraph, adjacencymatrix, implgraph, implhash, implnodes, totalvars, cliquecovers, cliquecoversizes, varincover,
3113 trafolinvars, trafolinvals, ntrafolinvars, trafoubs, var, trafoubv, newboundnonzero, ninftynonzero, TRUE, nchgbds, &update, &infeasible) );
3114 if ( infeasible )
3115 *cutoff = TRUE;
3116 else if ( update )
3117 *implupdate = TRUE;
3118 }
3119
3120 if ( *cutoff == TRUE )
3121 {
3122 /* free memory */
3123 SCIPfreeBufferArrayNull(scip, &varincover);
3124 for (j = ncliquecovers-1; j >= 0; --j)
3125 SCIPfreeBufferArrayNull(scip, &cliquecovers[j]);
3126 SCIPfreeBufferArrayNull(scip, &cliquecovers);
3127 SCIPfreeBufferArrayNull(scip, &cliquecoversizes);
3128 SCIPfreeBufferArrayNull(scip, &cliquecovervals);
3129 SCIPfreeBufferArrayNull(scip, &trafolinvals);
3130 SCIPfreeBufferArrayNull(scip, &trafolinvars);
3131 break;
3132 }
3133
3134 /* try to tighten upper bounds */
3135
3136 /* sort each cliquecover array in ascending order of the lower bounds of a_i * x_i; fill vector varincover */
3137 for (i = 0; i < ncliquecovers; ++i)
3138 {
3139 for (j = 0; j < cliquecoversizes[i]; ++j)
3140 {
3141 int ind = cliquecovers[i][j];
3142
3143 varincover[ind] = i;
3144 cliquecovervals[j] = trafolbs[ind];
3145 }
3146 SCIPsortRealInt(cliquecovervals, cliquecovers[i], cliquecoversizes[i]);
3147 }
3148
3149 /* for every variable that is in transformed constraint or every variable that is in conflict with some variable from trans. cons.:
3150 try upper bound tightening */
3151 for (v = 0; v < ntrafolinvars + nsos1linvars; ++v)
3152 {
3153 SCIP_Real newboundnonzero; /* new bound of a_v*x_v if we assume that x_v != 0 */
3154 SCIP_Real newboundnores; /* new bound of a_v*x_v if there are no restrictions */
3155 SCIP_Real newbound; /* resulting new bound of x_v */
3156 SCIP_VAR* var;
3157 SCIP_Real linval;
3158 SCIP_Real trafolbv;
3159 SCIP_Real lb;
3160 SCIP_Real ub;
3161 SCIP_Bool tightened;
3162 SCIP_Bool infeasible;
3163 SCIP_Bool inftynores = FALSE;
3164 SCIP_Bool update;
3165 int ninftynonzero = 0;
3166 int nodev;
3167 int w;
3168
3169 if ( v < ntrafolinvars )
3170 {
3171 var = trafolinvars[v];
3172 trafolbv = trafolbs[v];
3173 }
3174 else
3175 {
3176 assert( v-ntrafolinvars >= 0 );
3177 var = sos1linvars[v-ntrafolinvars];/*lint !e679*/
3178 trafolbv = 0.0; /* since variable is not a member of linear constraint */
3179 }
3180 lb = SCIPvarGetLbLocal(var);
3181 ub = SCIPvarGetUbLocal(var);
3182 if ( SCIPisInfinity(scip, traforhs) || SCIPisEQ(scip, lb, ub) )
3183 continue;
3184
3185 newboundnonzero = traforhs;
3186 newboundnores = traforhs;
3187 nodev = varGetNodeSOS1(conshdlrdata, var); /* possibly -1 if var is not involved in an SOS1 constraint */
3188 assert( nodev < nsos1vars );
3189
3190 /* determine incidence vector of implication variables (i.e., which SOS1 variables are nonzero if x_v is nonzero) */
3191 for (w = 0; w < nsos1vars; ++w)
3192 implnodes[w] = FALSE;
3193 SCIP_CALL( getSOS1Implications(scip, conshdlrdata, totalvars, implgraph, implhash, implnodes, SCIPhashmapGetImageInt(implhash, var)) );
3194
3195 /* compute new bound */
3196 for (i = 0; i < ncliquecovers; ++i)
3197 {
3198 int indcliq;
3199 int nodecliq;
3200
3201 assert( cliquecoversizes[i] > 0 );
3202
3203 indcliq = cliquecovers[i][0];
3204 assert( 0 <= indcliq && indcliq < ntrafolinvars );
3205
3206 /* determine minimum without index v (note that the array 'cliquecovers' is sorted by the values of trafolb in increasing order) */
3207 if ( v != indcliq )
3208 {
3209 /* if bound would be infinity */
3210 if ( SCIPisInfinity(scip, -trafolbs[indcliq]) || SCIPisInfinity(scip, REALABS(newboundnores - trafolbs[indcliq])) )
3211 inftynores = TRUE;
3212 else
3213 newboundnores -= trafolbs[indcliq];
3214 }
3215 else if ( cliquecoversizes[i] > 1 )
3216 {
3217 assert( 0 <= cliquecovers[i][1] && cliquecovers[i][1] < ntrafolinvars );
3218 if ( SCIPisInfinity(scip, -trafolbs[cliquecovers[i][1]]) || SCIPisInfinity(scip, REALABS(newboundnores - trafolbs[cliquecovers[i][1]])) )
3219 inftynores = TRUE;
3220 else
3221 newboundnores -= trafolbs[cliquecovers[i][1]]; /*lint --e{679}*/
3222 }
3223
3224 /* determine minimum without index v and if x_v is nonzero (note that the array 'cliquecovers' is sorted by the values of trafolb in increasing order) */
3225 for (j = 0; j < cliquecoversizes[i]; ++j)
3226 {
3227 indcliq = cliquecovers[i][j];
3228 assert( 0 <= indcliq && indcliq < ntrafolinvars );
3229
3230 nodecliq = varGetNodeSOS1(conshdlrdata, trafolinvars[indcliq]); /* possibly -1 if variable is not involved in an SOS1 constraint */
3231 assert( nodecliq < nsos1vars );
3232
3233 if ( v != indcliq )
3234 {
3235 /* if nodev or nodecliq are not a member of an SOS1 constraint or the variable corresponding to nodecliq is not implied to be zero if x_v != 0 */
3236 if ( nodev < 0 || nodecliq < 0 || (! isConnectedSOS1(adjacencymatrix, NULL, nodev, nodecliq) && ! isImpliedZero(conflictgraph, implnodes, nodecliq) ) )
3237 {
3238 /* if bound would be infinity */
3239 if ( SCIPisInfinity(scip, -trafolbs[indcliq]) || SCIPisInfinity(scip, REALABS(newboundnonzero - trafolbs[indcliq])) )
3240 ++ninftynonzero;
3241 else
3242 newboundnonzero -= trafolbs[indcliq];
3243 break; /* break since we are only interested in the minimum lower bound among the variables in the clique cover;
3244 * the variables in the clique cover form an SOS1 constraint, thus only one of them can be nonzero */
3245 }
3246 }
3247 }
3248 }
3249 assert( ninftynonzero == 0 || inftynores );
3250
3251 /* if computed bound is not infinity and variable is contained in linear constraint */
3252 if ( ninftynonzero == 0 && v < ntrafolinvars )
3253 {
3254 linval = trafolinvals[v];
3255
3256 if ( SCIPisFeasZero(scip, linval) )
3257 continue;
3258
3259 /* compute new bound */
3260 if ( SCIPisFeasNegative(scip, newboundnores) && ! inftynores )
3261 newbound = newboundnonzero;
3262 else
3263 newbound = MAX(0, newboundnonzero);
3264 newbound /= linval;
3265
3266 if ( SCIPisInfinity(scip, newbound) )
3267 continue;
3268
3269 /* check if new bound is tighter than the old one or problem is infeasible */
3270 if ( SCIPisFeasPositive(scip, linval) && SCIPisFeasGT(scip, ub, newbound) )
3271 {
3272 /* if new upper bound is smaller than the lower bound, we are infeasible */
3273 if ( SCIPisFeasGT(scip, lb, newbound) )
3274 {
3275 *cutoff = TRUE;
3276 break;
3277 }
3278
3279 if ( SCIPvarIsIntegral(var) )
3280 newbound = SCIPfloor(scip, newbound);
3281
3282 SCIP_CALL( SCIPtightenVarUb(scip, var, newbound, FALSE, &infeasible, &tightened) );
3283 assert( ! infeasible );
3284
3285 if ( tightened )
3286 {
3287 SCIPdebugMsg(scip, "changed upper bound of variable %s from %f to %f \n", SCIPvarGetName(var), ub, newbound);
3288 ++(*nchgbds);
3289 }
3290 }
3291 else if ( SCIPisFeasNegative(scip, linval) && SCIPisFeasLT(scip, lb, newbound) )
3292 {
3293 /* if assumption a_i * x_i != 0 was not correct */
3294 if ( SCIPisFeasLT(scip, ub, newbound) )
3295 {
3296 *cutoff = TRUE;
3297 break;
3298 }
3299
3300 if ( SCIPvarIsIntegral(var) )
3301 newbound = SCIPceil(scip, newbound);
3302
3303 SCIP_CALL( SCIPtightenVarLb(scip, var, newbound, FALSE, &infeasible, &tightened) );
3304 assert( ! infeasible );
3305
3306 if ( tightened )
3307 {
3308 SCIPdebugMsg(scip, "changed lower bound of variable %s from %f to %f \n", SCIPvarGetName(var), lb, newbound);
3309 ++(*nchgbds);
3310 }
3311 }
3312 }
3313
3314 /* update implication graph if possible */
3315 SCIP_CALL( updateImplicationGraphSOS1(scip, conshdlrdata, conflictgraph, adjacencymatrix, implgraph, implhash, implnodes, totalvars, cliquecovers, cliquecoversizes, varincover,
3316 trafolinvars, trafolinvals, ntrafolinvars, trafolbs, var, trafolbv, newboundnonzero, ninftynonzero, FALSE, nchgbds, &update, &infeasible) );
3317 if ( infeasible )
3318 *cutoff = TRUE;
3319 else if ( update )
3320 *implupdate = TRUE;
3321 }
3322
3323 /* free memory */
3324 SCIPfreeBufferArrayNull(scip, &varincover);
3325 for (j = ncliquecovers-1; j >= 0; --j)
3326 SCIPfreeBufferArrayNull(scip, &cliquecovers[j]);
3327 SCIPfreeBufferArrayNull(scip, &cliquecovers);
3328 SCIPfreeBufferArrayNull(scip, &cliquecoversizes);
3329 SCIPfreeBufferArrayNull(scip, &cliquecovervals);
3330 SCIPfreeBufferArrayNull(scip, &trafolinvals);
3331 SCIPfreeBufferArrayNull(scip, &trafolinvars);
3332
3333 if ( *cutoff == TRUE )
3334 break;
3335 } /* end for every linear constraint */
3336
3337 /* free buffer arrays */
3338 SCIPfreeBufferArrayNull(scip, &trafolbs);
3339 SCIPfreeBufferArrayNull(scip, &trafoubs);
3340 SCIPfreeBufferArrayNull(scip, &coveredvars);
3341 SCIPfreeBufferArrayNull(scip, &varindincons);
3342 SCIPfreeBufferArrayNull(scip, &implnodes);
3343 SCIPfreeBufferArrayNull(scip, &sos1linvars);
3344
3345 return SCIP_OKAY;
3346 }
3347
3348
3349 /** perform one presolving round for variables
3350 *
3351 * We perform the following presolving steps:
3352 * - Tighten the bounds of the variables
3353 * - Update conflict graph based on bound implications of the variables
3354 */
3355 static
presolRoundVarsSOS1(SCIP * scip,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_DIGRAPH * conflictgraph,SCIP_Bool ** adjacencymatrix,int nsos1vars,int * nfixedvars,int * nchgbds,int * naddconss,SCIP_RESULT * result)3356 SCIP_RETCODE presolRoundVarsSOS1(
3357 SCIP* scip, /**< SCIP pointer */
3358 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
3359 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
3360 SCIP_Bool** adjacencymatrix, /**< adjacencymatrix of conflict graph */
3361 int nsos1vars, /**< number of SOS1 variables */
3362 int* nfixedvars, /**< pointer to store number of fixed variables */
3363 int* nchgbds, /**< pointer to store number of changed bounds */
3364 int* naddconss, /**< pointer to store number of addded constraints */
3365 SCIP_RESULT* result /**< result */
3366 )
3367 {
3368 SCIP_DIGRAPH* implgraph;
3369 SCIP_HASHMAP* implhash;
3370
3371 SCIP_Bool cutoff = FALSE;
3372 SCIP_Bool updateconfl;
3373
3374 SCIP_VAR** totalvars;
3375 SCIP_VAR** probvars;
3376 int ntotalvars = 0;
3377 int nprobvars;
3378 int i;
3379 int j;
3380
3381 /* determine totalvars (union of SOS1 and problem variables) */
3382 probvars = SCIPgetVars(scip);
3383 nprobvars = SCIPgetNVars(scip);
3384 SCIP_CALL( SCIPhashmapCreate(&implhash, SCIPblkmem(scip), nsos1vars + nprobvars) );
3385 SCIP_CALL( SCIPallocBufferArray(scip, &totalvars, nsos1vars + nprobvars) );
3386
3387 for (i = 0; i < nsos1vars; ++i)
3388 {
3389 SCIP_VAR* var;
3390 var = SCIPnodeGetVarSOS1(conflictgraph, i);
3391
3392 /* insert node number to hash map */
3393 assert( ! SCIPhashmapExists(implhash, var) );
3394 SCIP_CALL( SCIPhashmapInsertInt(implhash, var, ntotalvars) );
3395 assert( ntotalvars == SCIPhashmapGetImageInt(implhash, var) );
3396 totalvars[ntotalvars++] = var;
3397 }
3398
3399 for (i = 0; i < nprobvars; ++i)
3400 {
3401 SCIP_VAR* var;
3402 var = probvars[i];
3403
3404 /* insert node number to hash map if not existent */
3405 if ( ! SCIPhashmapExists(implhash, var) )
3406 {
3407 SCIP_CALL( SCIPhashmapInsertInt(implhash, var, ntotalvars) );
3408 assert( ntotalvars == SCIPhashmapGetImageInt(implhash, var) );
3409 totalvars[ntotalvars++] = var;
3410 }
3411 }
3412
3413 /* create implication graph */
3414 SCIP_CALL( SCIPcreateDigraph(scip, &implgraph, ntotalvars) );
3415
3416 /* try to tighten the lower and upper bounds of the variables */
3417 updateconfl = FALSE;
3418 for (j = 0; (j < conshdlrdata->maxtightenbds || conshdlrdata->maxtightenbds == -1 ) && ! cutoff; ++j)
3419 {
3420 SCIP_Bool implupdate;
3421 int nchgbdssave;
3422
3423 nchgbdssave = *nchgbds;
3424
3425 assert( ntotalvars > 0 );
3426 SCIP_CALL( tightenVarsBoundsSOS1(scip, conshdlrdata, conflictgraph, implgraph, implhash, adjacencymatrix, totalvars, ntotalvars, nsos1vars, nchgbds, &implupdate, &cutoff) );
3427 if ( *nchgbds > nchgbdssave )
3428 {
3429 *result = SCIP_SUCCESS;
3430 if ( implupdate )
3431 updateconfl = TRUE;
3432 }
3433 else if ( implupdate )
3434 updateconfl = TRUE;
3435 else
3436 break;
3437 }
3438
3439 /* perform implication graph analysis */
3440 if ( updateconfl && conshdlrdata->perfimplanalysis && ! cutoff )
3441 {
3442 SCIP_Real* implubs;
3443 SCIP_Real* impllbs;
3444 SCIP_Bool* implnodes;
3445 SCIP_Bool infeasible;
3446 SCIP_Bool fixed;
3447 int naddconsssave;
3448 int probingdepth;
3449
3450 /* allocate buffer arrays */
3451 SCIP_CALL( SCIPallocBufferArray(scip, &implnodes, nsos1vars) );
3452 SCIP_CALL( SCIPallocBufferArray(scip, &impllbs, ntotalvars) );
3453 SCIP_CALL( SCIPallocBufferArray(scip, &implubs, ntotalvars) );
3454
3455 naddconsssave = *naddconss;
3456 for (i = 0; i < nsos1vars; ++i)
3457 {
3458 /* initialize data for implication graph analysis */
3459 infeasible = FALSE;
3460 probingdepth = 0;
3461 for (j = 0; j < nsos1vars; ++j)
3462 implnodes[j] = FALSE;
3463 for (j = 0; j < ntotalvars; ++j)
3464 {
3465 impllbs[j] = SCIPvarGetLbLocal(totalvars[j]);
3466 implubs[j] = SCIPvarGetUbLocal(totalvars[j]);
3467 }
3468
3469 /* try to update the conflict graph based on the information of the implication graph */
3470 SCIP_CALL( performImplicationGraphAnalysis(scip, conshdlrdata, conflictgraph, totalvars, implgraph, implhash, adjacencymatrix, i, i, impllbs, implubs, implnodes, naddconss, &probingdepth, &infeasible) );
3471
3472 /* if the subproblem turned out to be infeasible then fix variable to zero */
3473 if ( infeasible )
3474 {
3475 SCIP_CALL( SCIPfixVar(scip, totalvars[i], 0.0, &infeasible, &fixed) );
3476
3477 if ( fixed )
3478 {
3479 SCIPdebugMsg(scip, "fixed variable %s with lower bound %f and upper bound %f to zero\n",
3480 SCIPvarGetName(totalvars[i]), SCIPvarGetLbLocal(totalvars[i]), SCIPvarGetUbLocal(totalvars[i]));
3481 ++(*nfixedvars);
3482 }
3483
3484 if ( infeasible )
3485 cutoff = TRUE;
3486 }
3487 }
3488
3489 if ( *naddconss > naddconsssave )
3490 *result = SCIP_SUCCESS;
3491
3492 /* free buffer arrays */
3493 SCIPfreeBufferArrayNull(scip, &implubs);
3494 SCIPfreeBufferArrayNull(scip, &impllbs);
3495 SCIPfreeBufferArrayNull(scip, &implnodes);
3496 }
3497
3498 /* if an infeasibility has been detected */
3499 if ( cutoff )
3500 {
3501 SCIPdebugMsg(scip, "cutoff \n");
3502 *result = SCIP_CUTOFF;
3503 }
3504
3505 /* free memory */;
3506 for (j = ntotalvars-1; j >= 0; --j)
3507 {
3508 SCIP_SUCCDATA** succdatas;
3509 int nsucc;
3510 int s;
3511
3512 succdatas = (SCIP_SUCCDATA**) SCIPdigraphGetSuccessorsData(implgraph, j);
3513 nsucc = SCIPdigraphGetNSuccessors(implgraph, j);
3514
3515 for (s = nsucc-1; s >= 0; --s)
3516 SCIPfreeBlockMemory(scip, &succdatas[s]);/*lint !e866*/
3517 }
3518 SCIPdigraphFree(&implgraph);
3519 SCIPfreeBufferArrayNull(scip, &totalvars);
3520 SCIPhashmapFree(&implhash);
3521
3522 return SCIP_OKAY;
3523 }
3524
3525
3526 /* ----------------------------- propagation -------------------------------------*/
3527
3528 /** propagate variables of SOS1 constraint */
3529 static
propConsSOS1(SCIP * scip,SCIP_CONS * cons,SCIP_CONSDATA * consdata,SCIP_Bool * cutoff,int * ngen)3530 SCIP_RETCODE propConsSOS1(
3531 SCIP* scip, /**< SCIP pointer */
3532 SCIP_CONS* cons, /**< constraint */
3533 SCIP_CONSDATA* consdata, /**< constraint data */
3534 SCIP_Bool* cutoff, /**< whether a cutoff happened */
3535 int* ngen /**< number of domain changes */
3536 )
3537 {
3538 assert( scip != NULL );
3539 assert( cons != NULL );
3540 assert( consdata != NULL );
3541 assert( cutoff != NULL );
3542 assert( ngen != NULL );
3543
3544 *cutoff = FALSE;
3545
3546 /* if more than one variable is fixed to be nonzero */
3547 if ( consdata->nfixednonzeros > 1 )
3548 {
3549 SCIPdebugMsg(scip, "the node is infeasible, more than 1 variable is fixed to be nonzero.\n");
3550 SCIP_CALL( SCIPresetConsAge(scip, cons) );
3551 *cutoff = TRUE;
3552 return SCIP_OKAY;
3553 }
3554
3555 /* if exactly one variable is fixed to be nonzero */
3556 if ( consdata->nfixednonzeros == 1 )
3557 {
3558 SCIP_VAR** vars;
3559 SCIP_Bool infeasible;
3560 SCIP_Bool tightened;
3561 SCIP_Bool success;
3562 SCIP_Bool allVarFixed;
3563 int firstFixedNonzero;
3564 int nvars;
3565 int j;
3566
3567 firstFixedNonzero = -1;
3568 nvars = consdata->nvars;
3569 vars = consdata->vars;
3570 assert( vars != NULL );
3571
3572 /* search nonzero variable - is needed for propinfo */
3573 for (j = 0; j < nvars; ++j)
3574 {
3575 if ( SCIPisFeasPositive(scip, SCIPvarGetLbLocal(vars[j])) || SCIPisFeasNegative(scip, SCIPvarGetUbLocal(vars[j])) )
3576 {
3577 firstFixedNonzero = j;
3578 break;
3579 }
3580 }
3581 assert( firstFixedNonzero >= 0 );
3582
3583 SCIPdebugMsg(scip, "variable <%s> is fixed nonzero, fixing other variables to 0.\n", SCIPvarGetName(vars[firstFixedNonzero]));
3584
3585 /* fix variables before firstFixedNonzero to 0 */
3586 allVarFixed = TRUE;
3587 for (j = 0; j < firstFixedNonzero; ++j)
3588 {
3589 /* fix variable */
3590 SCIP_CALL( inferVariableZero(scip, vars[j], cons, firstFixedNonzero, &infeasible, &tightened, &success) );
3591 assert( ! infeasible );
3592 allVarFixed = allVarFixed && success;
3593 if ( tightened )
3594 ++(*ngen);
3595 }
3596
3597 /* fix variables after firstFixedNonzero to 0 */
3598 for (j = firstFixedNonzero+1; j < nvars; ++j)
3599 {
3600 /* fix variable */
3601 SCIP_CALL( inferVariableZero(scip, vars[j], cons, firstFixedNonzero, &infeasible, &tightened, &success) );
3602 assert( ! infeasible ); /* there should be no variables after firstFixedNonzero that are fixed to be nonzero */
3603 allVarFixed = allVarFixed && success;
3604 if ( tightened )
3605 ++(*ngen);
3606 }
3607
3608 /* reset constraint age counter */
3609 if ( *ngen > 0 )
3610 {
3611 SCIP_CALL( SCIPresetConsAge(scip, cons) );
3612 }
3613
3614 /* delete constraint locally */
3615 if ( allVarFixed )
3616 {
3617 assert( !SCIPconsIsModifiable(cons) );
3618 SCIP_CALL( SCIPdelConsLocal(scip, cons) );
3619 }
3620 }
3621
3622 return SCIP_OKAY;
3623 }
3624
3625
3626 /** propagate a variable that is known to be nonzero */
3627 static
propVariableNonzero(SCIP * scip,SCIP_DIGRAPH * conflictgraph,SCIP_DIGRAPH * implgraph,SCIP_CONS * cons,int node,SCIP_Bool implprop,SCIP_Bool * cutoff,int * ngen)3628 SCIP_RETCODE propVariableNonzero(
3629 SCIP* scip, /**< SCIP pointer */
3630 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
3631 SCIP_DIGRAPH* implgraph, /**< implication graph */
3632 SCIP_CONS* cons, /**< some arbitrary SOS1 constraint */
3633 int node, /**< conflict graph node of variable that is known to be nonzero */
3634 SCIP_Bool implprop, /**< whether implication graph propagation shall be applied */
3635 SCIP_Bool* cutoff, /**< whether a cutoff happened */
3636 int* ngen /**< number of domain changes */
3637 )
3638 {
3639 int inferinfo;
3640 int* succ;
3641 int nsucc;
3642 int s;
3643
3644 assert( scip != NULL );
3645 assert( conflictgraph != NULL );
3646 assert( cutoff != NULL );
3647 assert( ngen != NULL );
3648 assert( node >= 0 );
3649
3650 *cutoff = FALSE;
3651 inferinfo = -node - 1;
3652
3653 /* by assumption zero is outside the domain of variable */
3654 assert( SCIPisFeasPositive(scip, SCIPvarGetLbLocal(SCIPnodeGetVarSOS1(conflictgraph, node))) || SCIPisFeasNegative(scip, SCIPvarGetUbLocal(SCIPnodeGetVarSOS1(conflictgraph, node))) );
3655
3656 /* apply conflict graph propagation (fix all neighbors in the conflict graph to zero) */
3657 succ = SCIPdigraphGetSuccessors(conflictgraph, node);
3658 nsucc = SCIPdigraphGetNSuccessors(conflictgraph, node);
3659 for (s = 0; s < nsucc; ++s)
3660 {
3661 SCIP_VAR* succvar;
3662 SCIP_Real lb;
3663 SCIP_Real ub;
3664
3665 succvar = SCIPnodeGetVarSOS1(conflictgraph, succ[s]);
3666 lb = SCIPvarGetLbLocal(succvar);
3667 ub = SCIPvarGetUbLocal(succvar);
3668
3669 if ( ! SCIPisFeasZero(scip, lb) || ! SCIPisFeasZero(scip, ub) )
3670 {
3671 SCIP_Bool infeasible;
3672 SCIP_Bool tightened;
3673 SCIP_Bool success;
3674
3675 /* fix variable if it is not multi-aggregated */
3676 SCIP_CALL( inferVariableZero(scip, succvar, cons, inferinfo, &infeasible, &tightened, &success) );
3677
3678 if ( infeasible )
3679 {
3680 /* variable cannot be nonzero */
3681 *cutoff = TRUE;
3682 return SCIP_OKAY;
3683 }
3684 if ( tightened )
3685 ++(*ngen);
3686 assert( success || SCIPvarGetStatus(succvar) == SCIP_VARSTATUS_MULTAGGR );
3687 }
3688 }
3689
3690 /* apply implication graph propagation */
3691 if ( implprop && implgraph != NULL )
3692 {
3693 SCIP_SUCCDATA** succdatas;
3694
3695 #ifndef NDEBUG
3696 SCIP_NODEDATA* nodedbgdata;
3697 nodedbgdata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(implgraph, node);
3698 assert( SCIPvarCompare(nodedbgdata->var, SCIPnodeGetVarSOS1(conflictgraph, node)) == 0 );
3699 #endif
3700
3701 /* get successor datas */
3702 succdatas = (SCIP_SUCCDATA**) SCIPdigraphGetSuccessorsData(implgraph, node);
3703
3704 if ( succdatas != NULL )
3705 {
3706 succ = SCIPdigraphGetSuccessors(implgraph, node);
3707 nsucc = SCIPdigraphGetNSuccessors(implgraph, node);
3708 for (s = 0; s < nsucc; ++s)
3709 {
3710 SCIP_SUCCDATA* succdata;
3711 SCIP_NODEDATA* nodedata;
3712 SCIP_VAR* var;
3713
3714 nodedata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(implgraph, succ[s]);
3715 assert( nodedata != NULL );
3716 succdata = succdatas[s];
3717 assert( succdata != NULL );
3718 var = nodedata->var;
3719 assert( var != NULL );
3720
3721 /* tighten variable if it is not multi-aggregated */
3722 if ( SCIPvarGetStatus(var) != SCIP_VARSTATUS_MULTAGGR )
3723 {
3724 /* check for lower bound implication */
3725 if ( SCIPisFeasLT(scip, SCIPvarGetLbLocal(var), succdata->lbimpl) )
3726 {
3727 SCIP_Bool infeasible;
3728 SCIP_Bool tightened;
3729
3730 SCIP_CALL( SCIPinferVarLbCons(scip, var, succdata->lbimpl, cons, inferinfo, FALSE, &infeasible, &tightened) );
3731 if ( infeasible )
3732 {
3733 *cutoff = TRUE;
3734 return SCIP_OKAY;
3735 }
3736 if ( tightened )
3737 ++(*ngen);
3738 }
3739
3740 /* check for upper bound implication */
3741 if ( SCIPisFeasGT(scip, SCIPvarGetUbLocal(var), succdata->ubimpl) )
3742 {
3743 SCIP_Bool infeasible;
3744 SCIP_Bool tightened;
3745
3746 SCIP_CALL( SCIPinferVarUbCons(scip, var, succdata->ubimpl, cons, inferinfo, FALSE, &infeasible, &tightened) );
3747 if ( infeasible )
3748 {
3749 *cutoff = TRUE;
3750 return SCIP_OKAY;
3751 }
3752 if ( tightened )
3753 ++(*ngen);
3754 }
3755 }
3756 }
3757 }
3758 }
3759
3760 return SCIP_OKAY;
3761 }
3762
3763
3764 /** initialize implication graph
3765 *
3766 * @p j is successor of @p i if and only if \f$ x_i\not = 0 \Rightarrow x_j\not = 0\f$
3767 *
3768 * @note By construction the implication graph is globally valid.
3769 */
3770 static
initImplGraphSOS1(SCIP * scip,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_DIGRAPH * conflictgraph,int nsos1vars,int maxrounds,int * nchgbds,SCIP_Bool * cutoff,SCIP_Bool * success)3771 SCIP_RETCODE initImplGraphSOS1(
3772 SCIP* scip, /**< SCIP pointer */
3773 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
3774 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
3775 int nsos1vars, /**< number of SOS1 variables */
3776 int maxrounds, /**< maximal number of propagation rounds for generating implications */
3777 int* nchgbds, /**< pointer to store number of bound changes */
3778 SCIP_Bool* cutoff, /**< pointer to store whether a cutoff occurred */
3779 SCIP_Bool* success /**< whether initialization was successful */
3780 )
3781 {
3782 SCIP_HASHMAP* implhash = NULL;
3783 SCIP_Bool** adjacencymatrix = NULL;
3784 SCIP_Bool* implnodes = NULL;
3785 SCIP_VAR** implvars = NULL;
3786 SCIP_VAR** probvars;
3787 int nimplnodes;
3788 int nprobvars;
3789 int i;
3790 int j;
3791
3792 assert( scip != NULL );
3793 assert( conshdlrdata != NULL );
3794 assert( conflictgraph != NULL );
3795 assert( conshdlrdata->implgraph == NULL );
3796 assert( conshdlrdata->nimplnodes == 0 );
3797 assert( cutoff != NULL );
3798 assert( nchgbds != NULL );
3799
3800 *nchgbds = 0;
3801 *cutoff = FALSE;
3802
3803 /* we do not create the adjacency matrix of the conflict graph if the number of SOS1 variables is larger than a predefined value */
3804 if ( conshdlrdata->maxsosadjacency != -1 && nsos1vars > conshdlrdata->maxsosadjacency )
3805 {
3806 *success = FALSE;
3807 SCIPdebugMsg(scip, "Implication graph was not created since number of SOS1 variables (%d) is larger than %d.\n", nsos1vars, conshdlrdata->maxsosadjacency);
3808
3809 return SCIP_OKAY;
3810 }
3811 *success = TRUE;
3812
3813 /* only add globally valid implications to implication graph */
3814 assert ( SCIPgetDepth(scip) == 0 );
3815
3816 probvars = SCIPgetVars(scip);
3817 nprobvars = SCIPgetNVars(scip);
3818 nimplnodes = 0;
3819
3820 /* create implication graph */
3821 SCIP_CALL( SCIPcreateDigraph(scip, &conshdlrdata->implgraph, nsos1vars + nprobvars) );
3822
3823 /* create hashmap */
3824 SCIP_CALL( SCIPhashmapCreate(&implhash, SCIPblkmem(scip), nsos1vars + nprobvars) );
3825
3826 /* determine implvars (union of SOS1 and problem variables)
3827 * Note: For separation of implied bound cuts it is important that SOS1 variables are enumerated first
3828 */
3829 SCIP_CALL( SCIPallocBufferArray(scip, &implvars, nsos1vars + nprobvars) );
3830 for (i = 0; i < nsos1vars; ++i)
3831 {
3832 SCIP_VAR* var;
3833 var = SCIPnodeGetVarSOS1(conflictgraph, i);
3834
3835 /* insert node number to hash map */
3836 assert( ! SCIPhashmapExists(implhash, var) );
3837 SCIP_CALL( SCIPhashmapInsertInt(implhash, var, nimplnodes) );
3838 assert( nimplnodes == SCIPhashmapGetImageInt(implhash, var) );
3839 implvars[nimplnodes++] = var;
3840 }
3841
3842 for (i = 0; i < nprobvars; ++i)
3843 {
3844 SCIP_VAR* var;
3845 var = probvars[i];
3846
3847 /* insert node number to hash map if not existent */
3848 if ( ! SCIPhashmapExists(implhash, var) )
3849 {
3850 SCIP_CALL( SCIPhashmapInsertInt(implhash, var, nimplnodes) );
3851 assert( nimplnodes == SCIPhashmapGetImageInt(implhash, var) );
3852 implvars[nimplnodes++] = var;
3853 }
3854 }
3855 conshdlrdata->nimplnodes = nimplnodes;
3856
3857 /* add variables to nodes of implication graph */
3858 for (i = 0; i < nimplnodes; ++i)
3859 {
3860 SCIP_NODEDATA* nodedata = NULL;
3861
3862 /* create node data */
3863 SCIP_CALL( SCIPallocBlockMemory(scip, &nodedata) );
3864 nodedata->var = implvars[i];
3865
3866 /* set node data */
3867 SCIPdigraphSetNodeData(conshdlrdata->implgraph, (void*) nodedata, i);
3868 }
3869
3870 /* allocate buffer arrays */
3871 SCIP_CALL( SCIPallocBufferArray(scip, &implnodes, nsos1vars) );
3872 SCIP_CALL( SCIPallocBufferArray(scip, &adjacencymatrix, nsos1vars) );
3873
3874 for (i = 0; i < nsos1vars; ++i)
3875 SCIP_CALL( SCIPallocBufferArray(scip, &adjacencymatrix[i], i+1) ); /*lint !e866*/
3876
3877 /* create adjacency matrix */
3878 for (i = 0; i < nsos1vars; ++i)
3879 {
3880 for (j = 0; j < i+1; ++j)
3881 adjacencymatrix[i][j] = 0;
3882 }
3883
3884 for (i = 0; i < nsos1vars; ++i)
3885 {
3886 int* succ;
3887 int nsucc;
3888 succ = SCIPdigraphGetSuccessors(conflictgraph, i);
3889 nsucc = SCIPdigraphGetNSuccessors(conflictgraph, i);
3890
3891 for (j = 0; j < nsucc; ++j)
3892 {
3893 if ( i > succ[j] )
3894 adjacencymatrix[i][succ[j]] = 1;
3895 }
3896 }
3897
3898 assert( SCIPgetDepth(scip) == 0 );
3899
3900 /* compute SOS1 implications from linear constraints and tighten bounds of variables */
3901 for (j = 0; (j < maxrounds || maxrounds == -1 ); ++j)
3902 {
3903 SCIP_Bool implupdate;
3904 int nchgbdssave;
3905
3906 nchgbdssave = *nchgbds;
3907
3908 assert( nimplnodes > 0 );
3909 SCIP_CALL( tightenVarsBoundsSOS1(scip, conshdlrdata, conflictgraph, conshdlrdata->implgraph, implhash, adjacencymatrix, implvars, nimplnodes, nsos1vars, nchgbds, &implupdate, cutoff) );
3910 if ( *cutoff || ( ! implupdate && ! ( *nchgbds > nchgbdssave ) ) )
3911 break;
3912 }
3913
3914 /* free memory */
3915 for (i = nsos1vars-1; i >= 0; --i)
3916 SCIPfreeBufferArrayNull(scip, &adjacencymatrix[i]);
3917 SCIPfreeBufferArrayNull(scip, &adjacencymatrix);
3918 SCIPfreeBufferArrayNull(scip, &implnodes);
3919 SCIPfreeBufferArrayNull(scip, &implvars);
3920 SCIPhashmapFree(&implhash);
3921
3922 #ifdef SCIP_DEBUG
3923 /* evaluate results */
3924 if ( cutoff )
3925 {
3926 SCIPdebugMsg(scip, "cutoff \n");
3927 }
3928 else if ( *nchgbds > 0 )
3929 {
3930 SCIPdebugMsg(scip, "found %d bound changes\n", *nchgbds);
3931 }
3932 #endif
3933
3934 assert( conshdlrdata->implgraph != NULL );
3935
3936 return SCIP_OKAY;
3937 }
3938
3939
3940 /** deinitialize implication graph */
3941 static
freeImplGraphSOS1(SCIP * scip,SCIP_CONSHDLRDATA * conshdlrdata)3942 SCIP_RETCODE freeImplGraphSOS1(
3943 SCIP* scip, /**< SCIP pointer */
3944 SCIP_CONSHDLRDATA* conshdlrdata /**< constraint handler data */
3945 )
3946 {
3947 int j;
3948
3949 assert( scip != NULL );
3950 assert( conshdlrdata != NULL );
3951
3952 /* free whole memory of implication graph */
3953 if ( conshdlrdata->implgraph == NULL )
3954 {
3955 assert( conshdlrdata->nimplnodes == 0 );
3956 return SCIP_OKAY;
3957 }
3958
3959 /* free arc data */
3960 for (j = conshdlrdata->nimplnodes-1; j >= 0; --j)
3961 {
3962 SCIP_SUCCDATA** succdatas;
3963 int nsucc;
3964 int s;
3965
3966 succdatas = (SCIP_SUCCDATA**) SCIPdigraphGetSuccessorsData(conshdlrdata->implgraph, j);
3967 nsucc = SCIPdigraphGetNSuccessors(conshdlrdata->implgraph, j);
3968
3969 for (s = nsucc-1; s >= 0; --s)
3970 {
3971 assert( succdatas[s] != NULL );
3972 SCIPfreeBlockMemory(scip, &succdatas[s]);/*lint !e866*/
3973 }
3974 }
3975
3976 /* free node data */
3977 for (j = conshdlrdata->nimplnodes-1; j >= 0; --j)
3978 {
3979 SCIP_NODEDATA* nodedata;
3980 nodedata = (SCIP_NODEDATA*)SCIPdigraphGetNodeData(conshdlrdata->implgraph, j);
3981 assert( nodedata != NULL );
3982 SCIPfreeBlockMemory(scip, &nodedata);
3983 SCIPdigraphSetNodeData(conshdlrdata->implgraph, NULL, j);
3984 }
3985
3986 /* free implication graph */
3987 SCIPdigraphFree(&conshdlrdata->implgraph);
3988 conshdlrdata->nimplnodes = 0;
3989
3990 return SCIP_OKAY;
3991 }
3992
3993
3994 /* ----------------------------- branching -------------------------------------*/
3995
3996 /** get the vertices whose neighbor set covers a subset of the neighbor set of a given other vertex.
3997 *
3998 * This function can be used to compute sets of variables to branch on.
3999 */
4000 static
getCoverVertices(SCIP_DIGRAPH * conflictgraph,SCIP_Bool * verticesarefixed,int vertex,int * neightocover,int nneightocover,int * coververtices,int * ncoververtices)4001 SCIP_RETCODE getCoverVertices(
4002 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
4003 SCIP_Bool* verticesarefixed, /**< array that indicates which variables are currently fixed to zero */
4004 int vertex, /**< vertex (-1 if not needed) */
4005 int* neightocover, /**< neighbors of given vertex to be covered (or NULL if all neighbors shall be covered) */
4006 int nneightocover, /**< number of entries of neightocover (or 0 if all neighbors shall be covered )*/
4007 int* coververtices, /**< array to store the vertices whose neighbor set covers the neighbor set of the given vertex */
4008 int* ncoververtices /**< pointer to store size of coververtices */
4009 )
4010 {
4011 int* succ1;
4012 int nsucc1;
4013 int s;
4014
4015 assert( conflictgraph != NULL );
4016 assert( verticesarefixed != NULL );
4017 assert( coververtices != NULL );
4018 assert( ncoververtices != NULL );
4019
4020 *ncoververtices = 0;
4021
4022 /* if all the neighbors shall be covered */
4023 if ( neightocover == NULL )
4024 {
4025 assert( nneightocover == 0 );
4026 nsucc1 = SCIPdigraphGetNSuccessors(conflictgraph, vertex);
4027 succ1 = SCIPdigraphGetSuccessors(conflictgraph, vertex);
4028 }
4029 else
4030 {
4031 nsucc1 = nneightocover;
4032 succ1 = neightocover;
4033 }
4034
4035 /* determine all the successors of the first unfixed successor */
4036 for (s = 0; s < nsucc1; ++s)
4037 {
4038 int succvertex1 = succ1[s];
4039
4040 if ( ! verticesarefixed[succvertex1] )
4041 {
4042 int succvertex2;
4043 int* succ2;
4044 int nsucc2;
4045 int j;
4046
4047 nsucc2 = SCIPdigraphGetNSuccessors(conflictgraph, succvertex1);
4048 succ2 = SCIPdigraphGetSuccessors(conflictgraph, succvertex1);
4049
4050 /* for the first unfixed vertex */
4051 if ( *ncoververtices == 0 )
4052 {
4053 for (j = 0; j < nsucc2; ++j)
4054 {
4055 succvertex2 = succ2[j];
4056 if ( ! verticesarefixed[succvertex2] )
4057 coververtices[(*ncoververtices)++] = succvertex2;
4058 }
4059 }
4060 else
4061 {
4062 int vv = 0;
4063 int k = 0;
4064 int v;
4065
4066 /* determine all the successors that are in the set "coververtices" */
4067 for (v = 0; v < *ncoververtices; ++v)
4068 {
4069 assert( vv <= v );
4070 for (j = k; j < nsucc2; ++j)
4071 {
4072 succvertex2 = succ2[j];
4073 if ( succvertex2 > coververtices[v] )
4074 {
4075 /* coververtices[v] does not appear in succ2 list, go to next vertex in coververtices */
4076 k = j;
4077 break;
4078 }
4079 else if ( succvertex2 == coververtices[v] )
4080 {
4081 /* vertices are equal, copy to free position vv */
4082 coververtices[vv++] = succvertex2;
4083 k = j + 1;
4084 break;
4085 }
4086 }
4087 }
4088 /* store new size of coververtices */
4089 *ncoververtices = vv;
4090 }
4091 }
4092 }
4093
4094 #ifdef SCIP_DEBUG
4095 /* check sorting */
4096 for (s = 0; s < *ncoververtices; ++s)
4097 {
4098 assert( *ncoververtices <= 1 || coververtices[*ncoververtices - 1] > coververtices[*ncoververtices - 2] );
4099 }
4100 #endif
4101
4102 return SCIP_OKAY;
4103 }
4104
4105
4106 /** get vertices of variables that will be fixed to zero for each node */
4107 static
getBranchingVerticesSOS1(SCIP * scip,SCIP_DIGRAPH * conflictgraph,SCIP_SOL * sol,SCIP_Bool * verticesarefixed,SCIP_Bool bipbranch,int branchvertex,int * fixingsnode1,int * nfixingsnode1,int * fixingsnode2,int * nfixingsnode2)4108 SCIP_RETCODE getBranchingVerticesSOS1(
4109 SCIP* scip, /**< SCIP pointer */
4110 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
4111 SCIP_SOL* sol, /**< solution to be enforced (NULL for LP solution) */
4112 SCIP_Bool* verticesarefixed, /**< vector that indicates which variables are currently fixed to zero */
4113 SCIP_Bool bipbranch, /**< TRUE if bipartite branching method should be used */
4114 int branchvertex, /**< branching vertex */
4115 int* fixingsnode1, /**< vertices of variables that will be fixed to zero for the first node */
4116 int* nfixingsnode1, /**< pointer to store number of fixed variables for the first node */
4117 int* fixingsnode2, /**< vertices of variables that will be fixed to zero for the second node */
4118 int* nfixingsnode2 /**< pointer to store number of fixed variables for the second node */
4119 )
4120 {
4121 SCIP_Bool takeallsucc; /* whether to set fixingsnode1 = neighbors of 'branchvertex' in the conflict graph */
4122 int* succ;
4123 int nsucc;
4124 int j;
4125
4126 assert( scip != NULL );
4127 assert( conflictgraph != NULL );
4128 assert( verticesarefixed != NULL );
4129 assert( ! verticesarefixed[branchvertex] );
4130 assert( fixingsnode1 != NULL );
4131 assert( fixingsnode2 != NULL );
4132 assert( nfixingsnode1 != NULL );
4133 assert( nfixingsnode2 != NULL );
4134
4135 *nfixingsnode1 = 0;
4136 *nfixingsnode2 = 0;
4137 takeallsucc = TRUE;
4138
4139 /* get successors and number of successors of branching vertex */
4140 nsucc = SCIPdigraphGetNSuccessors(conflictgraph, branchvertex);
4141 succ = SCIPdigraphGetSuccessors(conflictgraph, branchvertex);
4142
4143 /* if bipartite branching method is turned on */
4144 if ( bipbranch )
4145 {
4146 SCIP_Real solval;
4147 int cnt = 0;
4148
4149 /* get all the neighbors of the variable with index 'branchvertex' whose solution value is nonzero */
4150 for (j = 0; j < nsucc; ++j)
4151 {
4152 if ( ! SCIPisFeasZero(scip, SCIPgetSolVal(scip, sol, SCIPnodeGetVarSOS1(conflictgraph, succ[j]))) )
4153 {
4154 assert( ! verticesarefixed[succ[j]] );
4155 fixingsnode1[(*nfixingsnode1)++] = succ[j];
4156 }
4157 }
4158
4159 /* if one of the sets fixingsnode1 or fixingsnode2 contains only one variable with a nonzero LP value we perform standard neighborhood branching */
4160 if ( *nfixingsnode1 > 0 )
4161 {
4162 /* get the vertices whose neighbor set cover the selected subset of the neighbors of the given branching vertex */
4163 SCIP_CALL( getCoverVertices(conflictgraph, verticesarefixed, branchvertex, fixingsnode1, *nfixingsnode1, fixingsnode2, nfixingsnode2) );
4164
4165 /* determine the intersection of the neighbors of branchvertex with the intersection of all the neighbors of fixingsnode2 */
4166 SCIP_CALL( getCoverVertices(conflictgraph, verticesarefixed, branchvertex, fixingsnode2, *nfixingsnode2, fixingsnode1, nfixingsnode1) );
4167
4168 for (j = 0; j < *nfixingsnode2; ++j)
4169 {
4170 solval = SCIPgetSolVal(scip, sol, SCIPnodeGetVarSOS1(conflictgraph, fixingsnode2[j]));
4171 if( ! SCIPisFeasZero(scip, solval) )
4172 ++cnt;
4173 }
4174
4175 /* we decide whether to use all successors if one partition of complete bipartite subgraph has only one node */
4176 if ( cnt >= 2 )
4177 {
4178 cnt = 0;
4179 for (j = 0; j < *nfixingsnode1; ++j)
4180 {
4181 solval = SCIPgetSolVal(scip, sol, SCIPnodeGetVarSOS1(conflictgraph, fixingsnode1[j]));
4182 if( ! SCIPisFeasZero(scip, solval) )
4183 ++cnt;
4184 }
4185
4186 if ( cnt >= 2 )
4187 takeallsucc = FALSE;
4188 }
4189 }
4190 }
4191
4192 if ( takeallsucc )
4193 {
4194 /* get all the unfixed neighbors of the branching vertex */
4195 *nfixingsnode1 = 0;
4196 for (j = 0; j < nsucc; ++j)
4197 {
4198 if ( ! verticesarefixed[succ[j]] )
4199 fixingsnode1[(*nfixingsnode1)++] = succ[j];
4200 }
4201
4202 if ( bipbranch )
4203 {
4204 /* get the vertices whose neighbor set covers the neighbor set of a given branching vertex */
4205 SCIP_CALL( getCoverVertices(conflictgraph, verticesarefixed, branchvertex, fixingsnode1, *nfixingsnode1, fixingsnode2, nfixingsnode2) );
4206 }
4207 else
4208 {
4209 /* use neighborhood branching, i.e, for the second node only the branching vertex can be fixed */
4210 fixingsnode2[0] = branchvertex;
4211 *nfixingsnode2 = 1;
4212 }
4213 }
4214
4215 return SCIP_OKAY;
4216 }
4217
4218
4219 /** gets branching priorities for SOS1 variables and applies 'most infeasible selection' rule to determine a vertex for the next branching decision */
4220 static
getBranchingPrioritiesSOS1(SCIP * scip,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_DIGRAPH * conflictgraph,SCIP_SOL * sol,int nsos1vars,SCIP_Bool * verticesarefixed,SCIP_Bool bipbranch,int * fixingsnode1,int * fixingsnode2,SCIP_Real * branchpriors,int * vertexbestprior,SCIP_Bool * relsolfeas)4221 SCIP_RETCODE getBranchingPrioritiesSOS1(
4222 SCIP* scip, /**< SCIP pointer */
4223 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
4224 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
4225 SCIP_SOL* sol, /**< solution to be enforced (NULL for LP solution) */
4226 int nsos1vars, /**< number of SOS1 variables */
4227 SCIP_Bool* verticesarefixed, /**< vector that indicates which variables are currently fixed to zero */
4228 SCIP_Bool bipbranch, /**< TRUE if bipartite branching method should be used */
4229 int* fixingsnode1, /**< vertices of variables that will be fixed to zero for the first node (size = nsos1vars) */
4230 int* fixingsnode2, /**< vertices of variables that will be fixed to zero for the second node (size = nsos1vars) */
4231 SCIP_Real* branchpriors, /**< pointer to store branching priorities (size = nsos1vars) or NULL if not needed */
4232 int* vertexbestprior, /**< pointer to store vertex with the best branching priority or NULL if not needed */
4233 SCIP_Bool* relsolfeas /**< pointer to store if LP relaxation solution is feasible */
4234 )
4235 {
4236 SCIP_Real bestprior;
4237 int i;
4238
4239 assert( scip != NULL );
4240 assert( conshdlrdata != NULL );
4241 assert( conflictgraph != NULL );
4242 assert( verticesarefixed != NULL );
4243 assert( fixingsnode1 != NULL );
4244 assert( fixingsnode2 != NULL );
4245 assert( relsolfeas != NULL );
4246
4247 bestprior = -SCIPinfinity(scip);
4248
4249 /* make sure data is initialized */
4250 if ( vertexbestprior != NULL )
4251 *vertexbestprior = -1;
4252
4253 for (i = 0; i < nsos1vars; ++i)
4254 {
4255 SCIP_Real prior;
4256 SCIP_Real solval;
4257 int nfixingsnode1;
4258 int nfixingsnode2;
4259 int nsucc;
4260 int j;
4261
4262 nsucc = SCIPdigraphGetNSuccessors(conflictgraph, i);
4263
4264 if ( nsucc == 0 || SCIPisFeasZero(scip, SCIPgetSolVal(scip, sol, SCIPnodeGetVarSOS1(conflictgraph, i))) || verticesarefixed[i] )
4265 prior = -SCIPinfinity(scip);
4266 else
4267 {
4268 SCIP_Bool iszero1 = TRUE;
4269 SCIP_Bool iszero2 = TRUE;
4270 SCIP_Real sum1 = 0.0;
4271 SCIP_Real sum2 = 0.0;
4272
4273 /* get vertices of variables that will be fixed to zero for each strong branching execution */
4274 assert( ! verticesarefixed[i] );
4275 SCIP_CALL( getBranchingVerticesSOS1(scip, conflictgraph, sol, verticesarefixed, bipbranch, i, fixingsnode1, &nfixingsnode1, fixingsnode2, &nfixingsnode2) );
4276
4277 for (j = 0; j < nfixingsnode1; ++j)
4278 {
4279 solval = SCIPgetSolVal(scip, sol, SCIPnodeGetVarSOS1(conflictgraph, fixingsnode1[j]));
4280 if ( ! SCIPisFeasZero(scip, solval) )
4281 {
4282 sum1 += REALABS( solval );
4283 iszero1 = FALSE;
4284 }
4285 }
4286
4287 for (j = 0; j < nfixingsnode2; ++j)
4288 {
4289 solval = SCIPgetSolVal(scip, sol, SCIPnodeGetVarSOS1(conflictgraph, fixingsnode2[j]));
4290 if ( ! SCIPisFeasZero(scip, solval) )
4291 {
4292 sum2 += REALABS( solval );
4293 iszero2 = FALSE;
4294 }
4295 }
4296
4297 if ( iszero1 || iszero2 )
4298 prior = -SCIPinfinity(scip);
4299 else
4300 prior = sum1 * sum2;
4301 }
4302
4303 if ( branchpriors != NULL )
4304 branchpriors[i] = prior;
4305 if ( bestprior < prior )
4306 {
4307 bestprior = prior;
4308
4309 if ( vertexbestprior != NULL )
4310 *vertexbestprior = i;
4311 }
4312 }
4313
4314 if ( SCIPisInfinity(scip, -bestprior) )
4315 *relsolfeas = TRUE;
4316 else
4317 *relsolfeas = FALSE;
4318
4319 return SCIP_OKAY;
4320 }
4321
4322
4323 /** performs strong branching with given domain fixings */
4324 static
performStrongbranchSOS1(SCIP * scip,SCIP_DIGRAPH * conflictgraph,int * fixingsexec,int nfixingsexec,int * fixingsop,int nfixingsop,int inititer,SCIP_Bool fixnonzero,int * domainfixings,int * ndomainfixings,SCIP_Bool * infeasible,SCIP_Real * objval,SCIP_Bool * lperror)4325 SCIP_RETCODE performStrongbranchSOS1(
4326 SCIP* scip, /**< SCIP pointer */
4327 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
4328 int* fixingsexec, /**< vertices of variables to be fixed to zero for this strong branching execution */
4329 int nfixingsexec, /**< number of vertices of variables to be fixed to zero for this strong branching execution */
4330 int* fixingsop, /**< vertices of variables to be fixed to zero for the opposite strong branching execution */
4331 int nfixingsop, /**< number of vertices of variables to be fixed to zero for the opposite strong branching execution */
4332 int inititer, /**< maximal number of LP iterations to perform */
4333 SCIP_Bool fixnonzero, /**< shall opposite variable (if positive in sign) fixed to the feasibility tolerance
4334 * (only possible if nfixingsop = 1) */
4335 int* domainfixings, /**< vertices that can be used to reduce the domain (should have size equal to number of variables) */
4336 int* ndomainfixings, /**< pointer to store number of vertices that can be used to reduce the domain, could be filled by earlier calls */
4337 SCIP_Bool* infeasible, /**< pointer to store whether branch is infeasible */
4338 SCIP_Real* objval, /**< pointer to store objective value of LP with fixed variables (SCIP_INVALID if reddomain = TRUE or lperror = TRUE) */
4339 SCIP_Bool* lperror /**< pointer to store whether an unresolved LP error or a strange solution status occurred */
4340 )
4341 {
4342 SCIP_LPSOLSTAT solstat;
4343 int i;
4344
4345 assert( scip != NULL );
4346 assert( conflictgraph != NULL );
4347 assert( fixingsexec != NULL );
4348 assert( nfixingsop > 0 );
4349 assert( fixingsop != NULL );
4350 assert( nfixingsop > 0 );
4351 assert( inititer >= -1 );
4352 assert( domainfixings != NULL );
4353 assert( ndomainfixings != NULL );
4354 assert( *ndomainfixings >= 0 );
4355 assert( infeasible != NULL );
4356 assert( objval != NULL );
4357 assert( lperror != NULL );
4358
4359 *objval = SCIP_INVALID; /* for debugging */
4360 *lperror = FALSE;
4361 *infeasible = FALSE;
4362
4363 /* start probing */
4364 SCIP_CALL( SCIPstartProbing(scip) );
4365
4366 /* perform domain fixings */
4367 if ( fixnonzero && nfixingsop == 1 )
4368 {
4369 SCIP_VAR* var;
4370 SCIP_Real lb;
4371 SCIP_Real ub;
4372
4373 var = SCIPnodeGetVarSOS1(conflictgraph, fixingsop[0]);
4374 lb = SCIPvarGetLbLocal(var);
4375 ub = SCIPvarGetUbLocal(var);
4376
4377 if ( SCIPvarGetStatus(var) != SCIP_VARSTATUS_MULTAGGR )
4378 {
4379 if ( SCIPisZero(scip, lb) )
4380 {
4381 /* fix variable to some very small, but positive number or to 1.0 if variable is integral */
4382 if (SCIPvarIsIntegral(var) )
4383 {
4384 SCIP_CALL( SCIPchgVarLbProbing(scip, var, 1.0) );
4385 }
4386 else
4387 {
4388 SCIP_CALL( SCIPchgVarLbProbing(scip, var, 1.5 * SCIPfeastol(scip)) );
4389 }
4390 }
4391 else if ( SCIPisZero(scip, ub) )
4392 {
4393 /* fix variable to some negative number with small absolute value or to -1.0 if variable is integral */
4394 if (SCIPvarIsIntegral(var) )
4395 {
4396 SCIP_CALL( SCIPchgVarUbProbing(scip, var, -1.0) );
4397 }
4398 else
4399 {
4400 SCIP_CALL( SCIPchgVarUbProbing(scip, var, -1.5 * SCIPfeastol(scip)) );
4401 }
4402 }
4403 }
4404 }
4405
4406 /* injects variable fixings into current probing node */
4407 for (i = 0; i < nfixingsexec && ! *infeasible; ++i)
4408 {
4409 SCIP_VAR* var;
4410
4411 var = SCIPnodeGetVarSOS1(conflictgraph, fixingsexec[i]);
4412 if ( SCIPisFeasGT(scip, SCIPvarGetLbLocal(var), 0.0) || SCIPisFeasLT(scip, SCIPvarGetUbLocal(var), 0.0) )
4413 *infeasible = TRUE;
4414 else
4415 {
4416 SCIP_CALL( SCIPfixVarProbing(scip, var, 0.0) );
4417 }
4418 }
4419
4420 /* apply domain propagation */
4421 if ( ! *infeasible )
4422 {
4423 SCIP_CALL( SCIPpropagateProbing(scip, 0, infeasible, NULL) );
4424 }
4425
4426 if ( *infeasible )
4427 solstat = SCIP_LPSOLSTAT_INFEASIBLE;
4428 else
4429 {
4430 /* solve the probing LP */
4431 SCIP_CALL( SCIPsolveProbingLP(scip, inititer, lperror, NULL) );
4432 if ( *lperror )
4433 {
4434 SCIP_CALL( SCIPendProbing(scip) );
4435 return SCIP_OKAY;
4436 }
4437
4438 /* get solution status */
4439 solstat = SCIPgetLPSolstat(scip);
4440 }
4441
4442 /* if objective limit was reached, then the domain can be reduced */
4443 if ( solstat == SCIP_LPSOLSTAT_OBJLIMIT || solstat == SCIP_LPSOLSTAT_INFEASIBLE )
4444 {
4445 *infeasible = TRUE;
4446
4447 for (i = 0; i < nfixingsop; ++i)
4448 domainfixings[(*ndomainfixings)++] = fixingsop[i];
4449 }
4450 else if ( solstat == SCIP_LPSOLSTAT_OPTIMAL || solstat == SCIP_LPSOLSTAT_TIMELIMIT || solstat == SCIP_LPSOLSTAT_ITERLIMIT )
4451 {
4452 /* get objective value of probing LP */
4453 *objval = SCIPgetLPObjval(scip);
4454 }
4455 else
4456 *lperror = TRUE;
4457
4458 /* end probing */
4459 SCIP_CALL( SCIPendProbing(scip) );
4460
4461 return SCIP_OKAY;
4462 }
4463
4464
4465 /** apply strong branching to determine the vertex for the next branching decision */
4466 static
getBranchingDecisionStrongbranchSOS1(SCIP * scip,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_DIGRAPH * conflictgraph,SCIP_SOL * sol,int nsos1vars,SCIP_Real lpobjval,SCIP_Bool bipbranch,int nstrongrounds,SCIP_Bool * verticesarefixed,int * fixingsnode1,int * fixingsnode2,int * vertexbestprior,SCIP_Real * bestobjval1,SCIP_Real * bestobjval2,SCIP_RESULT * result)4467 SCIP_RETCODE getBranchingDecisionStrongbranchSOS1(
4468 SCIP* scip, /**< SCIP pointer */
4469 SCIP_CONSHDLRDATA* conshdlrdata, /**< SOS1 constraint handler data */
4470 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
4471 SCIP_SOL* sol, /**< solution to be enforced (NULL for LP solution) */
4472 int nsos1vars, /**< number of SOS1 variables */
4473 SCIP_Real lpobjval, /**< current LP relaxation solution */
4474 SCIP_Bool bipbranch, /**< TRUE if bipartite branching method should be used */
4475 int nstrongrounds, /**< number of strong branching rounds */
4476 SCIP_Bool* verticesarefixed, /**< vector that indicates which variables are currently fixed to zero */
4477 int* fixingsnode1, /**< pointer to store vertices of variables that will be fixed to zero for the first node (size = nsos1vars) */
4478 int* fixingsnode2, /**< pointer to store vertices of variables that will be fixed to zero for the second node (size = nsos1vars) */
4479 int* vertexbestprior, /**< pointer to store vertex with the best strong branching priority */
4480 SCIP_Real* bestobjval1, /**< pointer to store LP objective for left child node of branching decision with best priority */
4481 SCIP_Real* bestobjval2, /**< pointer to store LP objective for right child node of branching decision with best priority */
4482 SCIP_RESULT* result /**< pointer to store result of strong branching */
4483 )
4484 {
4485 SCIP_Real* branchpriors = NULL;
4486 int* indsos1vars = NULL;
4487 int* domainfixings = NULL;
4488 int ndomainfixings;
4489 int nfixingsnode1;
4490 int nfixingsnode2;
4491
4492 SCIP_Bool relsolfeas;
4493 SCIP_Real bestscore;
4494 int lastscorechange;
4495 int maxfailures;
4496
4497 SCIP_Longint nlpiterations;
4498 SCIP_Longint nlps;
4499 int inititer;
4500 int j;
4501 int i;
4502
4503 assert( scip != NULL );
4504 assert( conshdlrdata != NULL );
4505 assert( conflictgraph != NULL );
4506 assert( verticesarefixed != NULL );
4507 assert( fixingsnode1 != NULL );
4508 assert( fixingsnode2 != NULL );
4509 assert( vertexbestprior != NULL );
4510 assert( result != NULL );
4511
4512 /* allocate buffer arrays */
4513 SCIP_CALL( SCIPallocBufferArray(scip, &branchpriors, nsos1vars) );
4514
4515 /* get branching priorities */
4516 SCIP_CALL( getBranchingPrioritiesSOS1(scip, conshdlrdata, conflictgraph, sol, nsos1vars, verticesarefixed,
4517 bipbranch, fixingsnode1, fixingsnode2, branchpriors, NULL, &relsolfeas) );
4518
4519 /* if LP relaxation solution is feasible */
4520 if ( relsolfeas )
4521 {
4522 SCIPdebugMsg(scip, "all the SOS1 constraints are feasible.\n");
4523 *vertexbestprior = -1;
4524 *result = SCIP_FEASIBLE;
4525
4526 /* free memory */
4527 SCIPfreeBufferArrayNull(scip, &branchpriors);
4528
4529 return SCIP_OKAY;
4530 }
4531
4532 /* allocate buffer arrays */
4533 SCIP_CALL( SCIPallocBufferArray(scip, &indsos1vars, nsos1vars) );
4534 SCIP_CALL( SCIPallocBufferArray(scip, &domainfixings, nsos1vars) );
4535
4536 /* sort branching priorities (descending order) */
4537 for (j = 0; j < nsos1vars; ++j)
4538 indsos1vars[j] = j;
4539 SCIPsortDownRealInt(branchpriors, indsos1vars, nsos1vars);
4540
4541 /* determine the number of LP iterations to perform in each strong branch */
4542 nlpiterations = SCIPgetNDualResolveLPIterations(scip);
4543 nlps = SCIPgetNDualResolveLPs(scip);
4544 if ( nlps == 0 )
4545 {
4546 nlpiterations = SCIPgetNNodeInitLPIterations(scip);
4547 nlps = SCIPgetNNodeInitLPs(scip);
4548 if ( nlps == 0 )
4549 {
4550 nlpiterations = 1000;
4551 nlps = 1;
4552 }
4553 }
4554 assert(nlps >= 1);
4555
4556 /* compute number of LP iterations performed per strong branching iteration */
4557 if ( conshdlrdata->nstrongiter == -2 )
4558 {
4559 inititer = (int)(2*nlpiterations / nlps);
4560 inititer = (int)((SCIP_Real)inititer * (1.0 + 20.0/SCIPgetNNodes(scip)));
4561 inititer = MAX(inititer, 10);
4562 inititer = MIN(inititer, 500);
4563 }
4564 else
4565 inititer = conshdlrdata->nstrongiter;
4566
4567 /* get current LP relaxation solution */
4568 lpobjval = SCIPgetLPObjval(scip);
4569
4570 /* determine branching variable by strong branching or reduce domain */
4571 ndomainfixings = 0;
4572 lastscorechange = -1;
4573 assert( nsos1vars > 0 );
4574 *vertexbestprior = indsos1vars[0]; /* for the case that nstrongrounds = 0 */
4575 bestscore = -SCIPinfinity(scip);
4576 *bestobjval1 = -SCIPinfinity(scip);
4577 *bestobjval2 = -SCIPinfinity(scip);
4578 maxfailures = nstrongrounds;
4579
4580 /* for each strong branching round */
4581 for (j = 0; j < nstrongrounds; ++j)
4582 {
4583 int testvertex;
4584
4585 /* get branching vertex for the current strong branching iteration */
4586 testvertex = indsos1vars[j];
4587
4588 /* if variable with index 'vertex' does not violate any complementarity in its neighborhood for the current LP relaxation solution */
4589 if ( SCIPisPositive(scip, branchpriors[j]) )
4590 {
4591 SCIP_Bool infeasible1;
4592 SCIP_Bool infeasible2;
4593 SCIP_Bool lperror;
4594 SCIP_Real objval1;
4595 SCIP_Real objval2;
4596 SCIP_Real score;
4597
4598 /* get vertices of variables that will be fixed to zero for each strong branching execution */
4599 assert( ! verticesarefixed[testvertex] );
4600 SCIP_CALL( getBranchingVerticesSOS1(scip, conflictgraph, sol, verticesarefixed, bipbranch, testvertex,
4601 fixingsnode1, &nfixingsnode1, fixingsnode2, &nfixingsnode2) );
4602
4603 /* get information for first strong branching execution */
4604 SCIP_CALL( performStrongbranchSOS1(scip, conflictgraph, fixingsnode1, nfixingsnode1, fixingsnode2, nfixingsnode2,
4605 inititer, conshdlrdata->fixnonzero, domainfixings, &ndomainfixings, &infeasible1, &objval1, &lperror) );
4606 if ( lperror )
4607 continue;
4608
4609 /* get information for second strong branching execution */
4610 SCIP_CALL( performStrongbranchSOS1(scip, conflictgraph, fixingsnode2, nfixingsnode2, fixingsnode1, nfixingsnode1,
4611 inititer, FALSE, domainfixings, &ndomainfixings, &infeasible2, &objval2, &lperror) );
4612 if ( lperror )
4613 continue;
4614
4615 /* if both subproblems are infeasible */
4616 if ( infeasible1 && infeasible2 )
4617 {
4618 SCIPdebugMsg(scip, "detected cutoff.\n");
4619
4620 /* update result */
4621 *result = SCIP_CUTOFF;
4622
4623 /* free memory */
4624 SCIPfreeBufferArrayNull(scip, &domainfixings);
4625 SCIPfreeBufferArrayNull(scip, &indsos1vars);
4626 SCIPfreeBufferArrayNull(scip, &branchpriors);
4627
4628 return SCIP_OKAY;
4629 }
4630 else if ( ! infeasible1 && ! infeasible2 ) /* both subproblems are feasible */
4631 {
4632 /* if domain has not been reduced in this for-loop */
4633 if ( ndomainfixings == 0 )
4634 {
4635 score = MAX( REALABS(objval1 - lpobjval), SCIPfeastol(scip) ) * MAX( REALABS(objval2 - lpobjval), SCIPfeastol(scip) );/*lint !e666*/
4636
4637 if ( SCIPisPositive(scip, score - bestscore) )
4638 {
4639 bestscore = score;
4640 *vertexbestprior = testvertex;
4641 *bestobjval1 = objval1;
4642 *bestobjval2 = objval2;
4643
4644 lastscorechange = j;
4645 }
4646 else if ( j - lastscorechange > maxfailures )
4647 break;
4648 }
4649 }
4650 }
4651 }
4652
4653 /* if variable fixings have been detected by probing, then reduce domain */
4654 if ( ndomainfixings > 0 )
4655 {
4656 SCIP_NODE* node = SCIPgetCurrentNode(scip);
4657 SCIP_Bool infeasible;
4658
4659 for (i = 0; i < ndomainfixings; ++i)
4660 {
4661 SCIP_CALL( fixVariableZeroNode(scip, SCIPnodeGetVarSOS1(conflictgraph, domainfixings[i]), node, &infeasible) );
4662 assert( ! infeasible );
4663 }
4664
4665 SCIPdebugMsg(scip, "found %d domain fixings.\n", ndomainfixings);
4666
4667 /* update result */
4668 *result = SCIP_REDUCEDDOM;
4669 }
4670
4671 /* free buffer arrays */
4672 SCIPfreeBufferArrayNull(scip, &domainfixings);
4673 SCIPfreeBufferArrayNull(scip, &indsos1vars);
4674 SCIPfreeBufferArrayNull(scip, &branchpriors);
4675
4676 return SCIP_OKAY;
4677 }
4678
4679
4680 /** for two given vertices @p v1 and @p v2 search for a clique in the conflict graph that contains these vertices. From
4681 * this clique, we create a bound constraint.
4682 */
4683 static
getBoundConsFromVertices(SCIP * scip,SCIP_DIGRAPH * conflictgraph,SCIP_SOL * sol,int v1,int v2,SCIP_VAR * boundvar,SCIP_Bool extend,SCIP_CONS * cons,SCIP_Real * feas)4684 SCIP_RETCODE getBoundConsFromVertices(
4685 SCIP* scip, /**< SCIP pointer */
4686 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
4687 SCIP_SOL* sol, /**< solution to be enforced (NULL for LP solution) */
4688 int v1, /**< first vertex that shall be contained in bound constraint */
4689 int v2, /**< second vertex that shall be contained in bound constraint */
4690 SCIP_VAR* boundvar, /**< bound variable of @p v1 and @p v2 (or NULL if not existent) */
4691 SCIP_Bool extend, /**< should @p v1 and @p v2 be greedily extended to a clique of larger size */
4692 SCIP_CONS* cons, /**< bound constraint */
4693 SCIP_Real* feas /**< feasibility value of bound constraint */
4694 )
4695 {
4696 SCIP_NODEDATA* nodedata;
4697 SCIP_Bool addv2 = TRUE;
4698 SCIP_Real solval;
4699 SCIP_VAR* var;
4700 SCIP_Real coef = 0.0;
4701 int nsucc;
4702 int s;
4703
4704 int* extensions = NULL;
4705 int nextensions = 0;
4706 int nextensionsnew;
4707 int* succ;
4708
4709 assert( scip != NULL );
4710 assert( conflictgraph != NULL );
4711 assert( cons != NULL );
4712 assert( feas != NULL );
4713
4714 *feas = 0.0;
4715
4716 /* add index 'v1' to the clique */
4717 nodedata = (SCIP_NODEDATA*)SCIPdigraphGetNodeData(conflictgraph, v1);
4718 var = nodedata->var;
4719 assert( boundvar == NULL || SCIPvarCompare(boundvar, nodedata->ubboundvar) == 0 );
4720 solval = SCIPgetSolVal(scip, sol, var);
4721
4722 /* if 'v1' and 'v2' have the same bound variable then the bound cut can be strengthened */
4723 if ( boundvar == NULL )
4724 {
4725 if ( SCIPisFeasPositive(scip, solval) )
4726 {
4727 SCIP_Real ub;
4728 ub = SCIPvarGetUbLocal(var);
4729 assert( SCIPisFeasPositive(scip, ub));
4730
4731 if ( ! SCIPisInfinity(scip, ub) )
4732 coef = 1.0/ub;
4733 }
4734 else if ( SCIPisFeasNegative(scip, solval) )
4735 {
4736 SCIP_Real lb;
4737 lb = SCIPvarGetLbLocal(var);
4738 assert( SCIPisFeasNegative(scip, lb) );
4739 if ( ! SCIPisInfinity(scip, -lb) )
4740 coef = 1.0/lb;
4741 }
4742 }
4743 else if ( boundvar == nodedata->ubboundvar )
4744 {
4745 if ( SCIPisFeasPositive(scip, solval) )
4746 {
4747 SCIP_Real ub;
4748
4749 ub = nodedata->ubboundcoef;
4750 assert( SCIPisFeasPositive(scip, ub) );
4751 if ( ! SCIPisInfinity(scip, ub) )
4752 coef = 1.0/ub;
4753 }
4754 else if ( SCIPisFeasNegative(scip, solval) )
4755 {
4756 SCIP_Real lb;
4757
4758 lb = nodedata->lbboundcoef;
4759 assert( SCIPisFeasPositive(scip, lb) );
4760 if ( ! SCIPisInfinity(scip, lb) )
4761 coef = 1.0/lb;
4762 }
4763 }
4764
4765 if ( ! SCIPisZero(scip, coef) )
4766 {
4767 *feas += coef * solval;
4768 SCIP_CALL( SCIPaddCoefLinear(scip, cons, var, coef) );
4769 }
4770
4771 /* if clique shall be greedily extended to a clique of larger size */
4772 if ( extend )
4773 {
4774 /* get successors */
4775 nsucc = SCIPdigraphGetNSuccessors(conflictgraph, v1);
4776 succ = SCIPdigraphGetSuccessors(conflictgraph, v1);
4777 assert( nsucc > 0 );
4778
4779 /* allocate buffer array */
4780 SCIP_CALL( SCIPallocBufferArray(scip, &extensions, nsucc) );
4781
4782 /* get possible extensions for the clique cover */
4783 for (s = 0; s < nsucc; ++s)
4784 extensions[s] = succ[s];
4785 nextensions = nsucc;
4786 }
4787 else
4788 nextensions = 1;
4789
4790 /* while there exist possible extensions for the clique cover */
4791 while ( nextensions > 0 )
4792 {
4793 SCIP_Real bestbigMval;
4794 SCIP_Real bigMval;
4795 int bestindex = -1;
4796 int ext;
4797
4798 bestbigMval = -SCIPinfinity(scip);
4799
4800 /* if v2 has not been added to clique already */
4801 if ( addv2 )
4802 {
4803 bestindex = v2;
4804 addv2 = FALSE;
4805 }
4806 else /* search for the extension with the largest absolute value of its LP relaxation solution value */
4807 {
4808 assert( extensions != NULL );
4809 for (s = 0; s < nextensions; ++s)
4810 {
4811 ext = extensions[s];
4812 bigMval = nodeGetSolvalBinaryBigMSOS1(scip, conflictgraph, sol, ext);
4813 if ( SCIPisFeasLT(scip, bestbigMval, bigMval) )
4814 {
4815 bestbigMval = bigMval;
4816 bestindex = ext;
4817 }
4818 }
4819 }
4820 assert( bestindex != -1 );
4821
4822 /* add bestindex variable to the constraint */
4823 nodedata = (SCIP_NODEDATA*)SCIPdigraphGetNodeData(conflictgraph, bestindex);
4824 var = nodedata->var;
4825 solval = SCIPgetSolVal(scip, sol, var);
4826 coef = 0.0;
4827 if ( boundvar == NULL )
4828 {
4829 if ( SCIPisFeasPositive(scip, solval) )
4830 {
4831 SCIP_Real ub;
4832 ub = SCIPvarGetUbLocal(var);
4833 assert( SCIPisFeasPositive(scip, ub));
4834
4835 if ( ! SCIPisInfinity(scip, ub) )
4836 coef = 1.0/ub;
4837 }
4838 else if ( SCIPisFeasNegative(scip, solval) )
4839 {
4840 SCIP_Real lb;
4841 lb = SCIPvarGetLbLocal(var);
4842 assert( SCIPisFeasNegative(scip, lb) );
4843 if ( ! SCIPisInfinity(scip, -lb) )
4844 coef = 1.0/lb;
4845 }
4846 }
4847 else if ( boundvar == nodedata->ubboundvar )
4848 {
4849 if ( SCIPisFeasPositive(scip, solval) )
4850 {
4851 SCIP_Real ub;
4852
4853 ub = nodedata->ubboundcoef;
4854 assert( SCIPisFeasPositive(scip, ub) );
4855 if ( ! SCIPisInfinity(scip, ub) )
4856 coef = 1.0/ub;
4857 }
4858 else if ( SCIPisFeasNegative(scip, solval) )
4859 {
4860 SCIP_Real lb;
4861
4862 lb = nodedata->lbboundcoef;
4863 assert( SCIPisFeasPositive(scip, lb) );
4864 if ( ! SCIPisInfinity(scip, -lb) )
4865 coef = 1.0/lb;
4866 }
4867 }
4868 if ( ! SCIPisZero(scip, coef) )
4869 {
4870 *feas += coef * solval;
4871 SCIP_CALL( SCIPaddCoefLinear(scip, cons, var, coef) );
4872 }
4873
4874 if ( extend )
4875 {
4876 assert( extensions != NULL );
4877 /* compute new 'extensions' array */
4878 nextensionsnew = 0;
4879 for (s = 0; s < nextensions; ++s)
4880 {
4881 if ( s != bestindex && isConnectedSOS1(NULL, conflictgraph, bestindex, extensions[s]) )
4882 extensions[nextensionsnew++] = extensions[s];
4883 }
4884 nextensions = nextensionsnew;
4885 }
4886 else
4887 nextensions = 0;
4888 }
4889
4890 /* free buffer array */
4891 if ( extend )
4892 SCIPfreeBufferArray(scip, &extensions);
4893
4894 /* subtract rhs of constraint from feasibility value or add bound variable if existent */
4895 if ( boundvar == NULL )
4896 *feas -= 1.0;
4897 else
4898 {
4899 SCIP_CALL( SCIPaddCoefLinear(scip, cons, boundvar, -1.0) );
4900 *feas -= SCIPgetSolVal(scip, sol, boundvar);
4901 }
4902
4903 return SCIP_OKAY;
4904 }
4905
4906
4907 /** tries to add feasible complementarity constraints to a given child branching node.
4908 *
4909 * @note In this function the conflict graph is updated to the conflict graph of the considered child branching node.
4910 */
4911 static
addBranchingComplementaritiesSOS1(SCIP * scip,SCIP_NODE * node,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_DIGRAPH * conflictgraph,SCIP_DIGRAPH * localconflicts,SCIP_SOL * sol,int nsos1vars,SCIP_Bool * verticesarefixed,int * fixingsnode1,int nfixingsnode1,int * fixingsnode2,int nfixingsnode2,int * naddedconss,SCIP_Bool onlyviolsos1)4912 SCIP_RETCODE addBranchingComplementaritiesSOS1(
4913 SCIP* scip, /**< SCIP pointer */
4914 SCIP_NODE* node, /**< branching node */
4915 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
4916 SCIP_DIGRAPH* conflictgraph, /**< conflict graph of the current node */
4917 SCIP_DIGRAPH* localconflicts, /**< local conflicts (updates to local conflicts of child node) */
4918 SCIP_SOL* sol, /**< solution to be enforced (NULL for LP solution) */
4919 int nsos1vars, /**< number of SOS1 variables */
4920 SCIP_Bool* verticesarefixed, /**< vector that indicates which variables are currently fixed to zerox */
4921 int* fixingsnode1, /**< vertices of variables that will be fixed to zero for the branching node in the input of this function */
4922 int nfixingsnode1, /**< number of entries of array nfixingsnode1 */
4923 int* fixingsnode2, /**< vertices of variables that will be fixed to zero for the other branching node */
4924 int nfixingsnode2, /**< number of entries of array nfixingsnode2 */
4925 int* naddedconss, /**< pointer to store the number of added SOS1 constraints */
4926 SCIP_Bool onlyviolsos1 /**< should only SOS1 constraints be added that are violated by the LP solution */
4927 )
4928 {
4929 assert( scip != NULL );
4930 assert( node != NULL );
4931 assert( conshdlrdata != NULL );
4932 assert( conflictgraph != NULL );
4933 assert( verticesarefixed != NULL );
4934 assert( fixingsnode1 != NULL );
4935 assert( fixingsnode2 != NULL );
4936 assert( naddedconss != NULL );
4937
4938 *naddedconss = 0;
4939
4940 if ( nfixingsnode2 > 1 )
4941 {
4942 int* fixingsnode21; /* first partition of fixingsnode2 */
4943 int* fixingsnode22; /* second partition of fixingsnode2 */
4944 int nfixingsnode21;
4945 int nfixingsnode22;
4946
4947 int* coverarray; /* vertices, not in fixingsnode1 that cover all the vertices in array fixingsnode22 */
4948 int ncoverarray;
4949
4950 SCIP_Bool* mark;
4951 int* succarray;
4952 int nsuccarray;
4953 int* succ;
4954 int nsucc;
4955
4956 int i;
4957 int s;
4958
4959 /* allocate buffer arrays */
4960 SCIP_CALL( SCIPallocBufferArray(scip, &succarray, nsos1vars) );
4961 SCIP_CALL( SCIPallocBufferArray(scip, &mark, nsos1vars) );
4962 SCIP_CALL( SCIPallocBufferArray(scip, &fixingsnode21, nfixingsnode2) );
4963 SCIP_CALL( SCIPallocBufferArray(scip, &fixingsnode22, nfixingsnode2) );
4964
4965 /* mark all the unfixed vertices with FALSE */
4966 for (i = 0; i < nsos1vars; ++i)
4967 mark[i] = (verticesarefixed[i]);
4968
4969 /* mark all the vertices that are in the set fixingsnode1 */
4970 for (i = 0; i < nfixingsnode1; ++i)
4971 {
4972 assert( nfixingsnode1 <= 1 || (fixingsnode1[nfixingsnode1 - 1] > fixingsnode1[nfixingsnode1 - 2]) ); /* test: vertices are sorted */
4973 mark[fixingsnode1[i]] = TRUE;
4974 }
4975
4976 /* mark all the vertices that are in the set fixingsnode2 */
4977 for (i = 0; i < nfixingsnode2; ++i)
4978 {
4979 assert( nfixingsnode2 <= 1 || (fixingsnode2[nfixingsnode2 - 1] > fixingsnode2[nfixingsnode2 - 2]) ); /* test: vertices are sorted */
4980 mark[fixingsnode2[i]] = TRUE;
4981 }
4982
4983 /* compute the set of vertices that have a neighbor in the set fixingsnode2, but are not in the set fixingsnode1 or fixingsnode2 and are not already fixed */
4984 nsuccarray = 0;
4985 for (i = 0; i < nfixingsnode2; ++i)
4986 {
4987 nsucc = SCIPdigraphGetNSuccessors(conflictgraph, fixingsnode2[i]);
4988 succ = SCIPdigraphGetSuccessors(conflictgraph, fixingsnode2[i]);
4989
4990 for (s = 0; s < nsucc; ++s)
4991 {
4992 int succnode = succ[s];
4993
4994 if ( ! mark[succnode] )
4995 {
4996 mark[succnode] = TRUE;
4997 succarray[nsuccarray++] = succnode;
4998 }
4999 }
5000 }
5001
5002 /* allocate buffer array */
5003 SCIP_CALL( SCIPallocBufferArray(scip, &coverarray, nsos1vars) );
5004
5005 /* mark all the vertices with FALSE */
5006 for (i = 0; i < nsos1vars; ++i)
5007 mark[i] = FALSE;
5008
5009 /* mark all the vertices that are in the set fixingsnode2 */
5010 for (i = 0; i < nfixingsnode2; ++i)
5011 mark[fixingsnode2[i]] = TRUE;
5012
5013 /* for every node in succarray */
5014 for (i = 0; i < nsuccarray; ++i)
5015 {
5016 SCIP_Real solval1;
5017 SCIP_VAR* var1;
5018 int vertex1;
5019 int j;
5020
5021 vertex1 = succarray[i];
5022 var1 = SCIPnodeGetVarSOS1(conflictgraph, vertex1);
5023 solval1 = SCIPgetSolVal(scip, sol, var1);
5024
5025 /* we only add complementarity constraints if they are violated by the current LP solution */
5026 if ( ! onlyviolsos1 || ! SCIPisFeasZero(scip, solval1) )
5027 {
5028 /* compute first partition of fixingsnode2 that is the intersection of the neighbors of 'vertex1' with the set fixingsnode2 */
5029 nsucc = SCIPdigraphGetNSuccessors(conflictgraph, vertex1);
5030 succ = SCIPdigraphGetSuccessors(conflictgraph, vertex1);
5031 nfixingsnode21 = 0;
5032
5033 for (s = 0; s < nsucc; ++s)
5034 {
5035 if ( mark[succ[s]] )
5036 {
5037 fixingsnode21[nfixingsnode21++] = succ[s];
5038 assert( nfixingsnode21 == 1 || (fixingsnode21[nfixingsnode21 - 1] > fixingsnode21[nfixingsnode21 - 2]) ); /* test: successor vertices are sorted */
5039 }
5040 }
5041
5042 /* if variable can be fixed to zero */
5043 if ( nfixingsnode21 == nfixingsnode2 )
5044 {
5045 SCIP_Bool infeasible;
5046
5047 SCIP_CALL( fixVariableZeroNode(scip, var1, node, &infeasible) );
5048 assert( ! infeasible );
5049 continue;
5050 }
5051
5052 /* compute second partition of fixingsnode2 (that is fixingsnode2 \setminus fixingsnode21 ) */
5053 SCIP_CALL( SCIPcomputeArraysSetminus(fixingsnode2, nfixingsnode2, fixingsnode21, nfixingsnode21, fixingsnode22, &nfixingsnode22) );
5054 assert ( nfixingsnode22 + nfixingsnode21 == nfixingsnode2 );
5055
5056 /* compute cover set (that are all the vertices not in fixingsnode1 and fixingsnode21, whose neighborhood covers all the vertices of fixingsnode22) */
5057 SCIP_CALL( getCoverVertices(conflictgraph, verticesarefixed, -1, fixingsnode22, nfixingsnode22, coverarray, &ncoverarray) );
5058 SCIP_CALL( SCIPcomputeArraysSetminus(coverarray, ncoverarray, fixingsnode1, nfixingsnode1, coverarray, &ncoverarray) );
5059 SCIP_CALL( SCIPcomputeArraysSetminus(coverarray, ncoverarray, fixingsnode21, nfixingsnode21, coverarray, &ncoverarray) );
5060
5061 for (j = 0; j < ncoverarray; ++j)
5062 {
5063 int vertex2;
5064
5065 vertex2 = coverarray[j];
5066 assert( vertex2 != vertex1 );
5067
5068 /* prevent double enumeration */
5069 if ( vertex2 < vertex1 )
5070 {
5071 SCIP_VAR* var2;
5072 SCIP_Real solval2;
5073
5074 var2 = SCIPnodeGetVarSOS1(conflictgraph, vertex2);
5075 solval2 = SCIPgetSolVal(scip, sol, var2);
5076
5077 if ( onlyviolsos1 && ( SCIPisFeasZero(scip, solval1) || SCIPisFeasZero(scip, solval2) ) )
5078 continue;
5079
5080 if ( ! isConnectedSOS1(NULL, conflictgraph, vertex1, vertex2) )
5081 {
5082 char name[SCIP_MAXSTRLEN];
5083 SCIP_CONS* conssos1 = NULL;
5084 SCIP_Bool takebound = FALSE;
5085 SCIP_Real feas;
5086
5087 SCIP_NODEDATA* nodedata;
5088 SCIP_Real lbboundcoef1;
5089 SCIP_Real lbboundcoef2;
5090 SCIP_Real ubboundcoef1;
5091 SCIP_Real ubboundcoef2;
5092 SCIP_VAR* boundvar1;
5093 SCIP_VAR* boundvar2;
5094
5095 /* get bound variables if available */
5096 nodedata = (SCIP_NODEDATA*)SCIPdigraphGetNodeData(conflictgraph, vertex1);
5097 assert( nodedata != NULL );
5098 boundvar1 = nodedata->ubboundvar;
5099 lbboundcoef1 = nodedata->lbboundcoef;
5100 ubboundcoef1 = nodedata->ubboundcoef;
5101 nodedata = (SCIP_NODEDATA*)SCIPdigraphGetNodeData(conflictgraph, vertex2);
5102 assert( nodedata != NULL );
5103 boundvar2 = nodedata->ubboundvar;
5104 lbboundcoef2 = nodedata->lbboundcoef;
5105 ubboundcoef2 = nodedata->ubboundcoef;
5106
5107 if ( boundvar1 != NULL && boundvar2 != NULL && SCIPvarCompare(boundvar1, boundvar2) == 0 )
5108 takebound = TRUE;
5109
5110 /* add new arc to local conflicts in order to generate tighter bound inequalities */
5111 if ( conshdlrdata->addextendedbds )
5112 {
5113 if ( localconflicts == NULL )
5114 {
5115 SCIP_CALL( SCIPcreateDigraph(scip, &conshdlrdata->localconflicts, nsos1vars) );
5116 localconflicts = conshdlrdata->localconflicts;
5117 }
5118 SCIP_CALL( SCIPdigraphAddArc(localconflicts, vertex1, vertex2, NULL) );
5119 SCIP_CALL( SCIPdigraphAddArc(localconflicts, vertex2, vertex1, NULL) );
5120 SCIP_CALL( SCIPdigraphAddArc(conflictgraph, vertex1, vertex2, NULL) );
5121 SCIP_CALL( SCIPdigraphAddArc(conflictgraph, vertex2, vertex1, NULL) );
5122
5123 /* can sort successors in place - do not use arcdata */
5124 SCIPsortInt(SCIPdigraphGetSuccessors(localconflicts, vertex1), SCIPdigraphGetNSuccessors(localconflicts, vertex1));
5125 SCIPsortInt(SCIPdigraphGetSuccessors(localconflicts, vertex2), SCIPdigraphGetNSuccessors(localconflicts, vertex2));
5126 SCIPsortInt(SCIPdigraphGetSuccessors(conflictgraph, vertex1), SCIPdigraphGetNSuccessors(conflictgraph, vertex1));
5127 SCIPsortInt(SCIPdigraphGetSuccessors(conflictgraph, vertex2), SCIPdigraphGetNSuccessors(conflictgraph, vertex2));
5128
5129 /* mark conflictgraph as not local such that the new arcs are deleted after currents node processing */
5130 conshdlrdata->isconflocal = TRUE;
5131 }
5132
5133 /* measure feasibility of complementarity between var1 and var2 */
5134 if ( ! takebound )
5135 {
5136 feas = -1.0;
5137 if ( SCIPisFeasPositive(scip, solval1) )
5138 {
5139 assert( SCIPisFeasPositive(scip, SCIPvarGetUbLocal(var1)));
5140 if ( ! SCIPisInfinity(scip, SCIPvarGetUbLocal(var1)) )
5141 feas += solval1/SCIPvarGetUbLocal(var1);
5142 }
5143 else if ( SCIPisFeasNegative(scip, solval1) )
5144 {
5145 assert( SCIPisFeasPositive(scip, SCIPvarGetLbLocal(var1)));
5146 if ( ! SCIPisInfinity(scip, -SCIPvarGetLbLocal(var1)) )
5147 feas += solval1/SCIPvarGetLbLocal(var1);
5148 }
5149
5150 if ( SCIPisFeasPositive(scip, solval2) )
5151 {
5152 assert( SCIPisFeasPositive(scip, SCIPvarGetUbLocal(var2)));
5153 if ( ! SCIPisInfinity(scip, SCIPvarGetUbLocal(var2)) )
5154 feas += solval2/SCIPvarGetUbLocal(var2);
5155 }
5156 else if ( SCIPisFeasNegative(scip, solval2) )
5157 {
5158 assert( SCIPisFeasPositive(scip, SCIPvarGetLbLocal(var2)));
5159 if ( ! SCIPisInfinity(scip, -SCIPvarGetLbLocal(var2)) )
5160 feas += solval2/SCIPvarGetLbLocal(var2);
5161 }
5162 }
5163 else
5164 {
5165 feas = -SCIPgetSolVal(scip, sol, boundvar1);
5166 if ( SCIPisFeasPositive(scip, solval1) )
5167 {
5168 assert( SCIPisFeasPositive(scip, ubboundcoef1));
5169 if ( ! SCIPisInfinity(scip, ubboundcoef1) )
5170 feas += solval1/ubboundcoef1;
5171 }
5172 else if ( SCIPisFeasNegative(scip, solval1) )
5173 {
5174 assert( SCIPisFeasPositive(scip, lbboundcoef1));
5175 if ( ! SCIPisInfinity(scip, -lbboundcoef1) )
5176 feas += solval1/lbboundcoef1;
5177 }
5178
5179 if ( SCIPisFeasPositive(scip, solval2) )
5180 {
5181 assert( SCIPisFeasPositive(scip, ubboundcoef2));
5182 if ( ! SCIPisInfinity(scip, ubboundcoef2) )
5183 feas += solval2/ubboundcoef2;
5184 }
5185 else if ( SCIPisFeasNegative(scip, solval2) )
5186 {
5187 assert( SCIPisFeasPositive(scip, lbboundcoef2));
5188 if ( ! SCIPisInfinity(scip, -lbboundcoef2) )
5189 feas += solval2/lbboundcoef2;
5190 }
5191 assert( ! SCIPisFeasNegative(scip, solval2) );
5192 }
5193
5194 if ( SCIPisGT(scip, feas, conshdlrdata->addcompsfeas) )
5195 {
5196 /* create SOS1 constraint */
5197 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "sos1_branchnode_%" SCIP_LONGINT_FORMAT "_no_%i", SCIPnodeGetNumber(node), *naddedconss);
5198 SCIP_CALL( SCIPcreateConsSOS1(scip, &conssos1, name, 0, NULL, NULL, TRUE, TRUE, TRUE, FALSE, TRUE,
5199 TRUE, FALSE, FALSE, FALSE) );
5200
5201 /* add variables to SOS1 constraint */
5202 SCIP_CALL( addVarSOS1(scip, conssos1, conshdlrdata, var1, 1.0) );
5203 SCIP_CALL( addVarSOS1(scip, conssos1, conshdlrdata, var2, 2.0) );
5204
5205 /* add SOS1 constraint to the branching node */
5206 SCIP_CALL( SCIPaddConsNode(scip, node, conssos1, NULL) );
5207 ++(*naddedconss);
5208
5209 /* release constraint */
5210 SCIP_CALL( SCIPreleaseCons(scip, &conssos1) );
5211 }
5212
5213 /* add bound inequality*/
5214 if ( ! SCIPisFeasZero(scip, solval1) && ! SCIPisFeasZero(scip, solval2) )
5215 {
5216 /* possibly create linear constraint of the form x_i/u_i + x_j/u_j <= t if a bound variable t with x_i <= u_i * t and x_j <= u_j * t exists.
5217 * Otherwise try to create a constraint of the form x_i/u_i + x_j/u_j <= 1. Try the same for the lower bounds. */
5218 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "boundcons_branchnode_%" SCIP_LONGINT_FORMAT "_no_%i", SCIPnodeGetNumber(node), *naddedconss);
5219 if ( takebound )
5220 {
5221 /* create constraint with right hand side = 0.0 */
5222 SCIP_CALL( SCIPcreateConsLinear(scip, &conssos1, name, 0, NULL, NULL, -SCIPinfinity(scip), 0.0, TRUE, FALSE, TRUE, FALSE, FALSE,
5223 TRUE, FALSE, FALSE, FALSE, FALSE) );
5224
5225 /* add variables */
5226 SCIP_CALL( getBoundConsFromVertices(scip, conflictgraph, sol, vertex1, vertex2, boundvar1, conshdlrdata->addextendedbds, conssos1, &feas) );
5227 }
5228 else
5229 {
5230 /* create constraint with right hand side = 1.0 */
5231 SCIP_CALL( SCIPcreateConsLinear(scip, &conssos1, name, 0, NULL, NULL, -SCIPinfinity(scip), 1.0, TRUE, FALSE, TRUE, FALSE, FALSE,
5232 TRUE, FALSE, FALSE, FALSE, FALSE) );
5233
5234 /* add variables */
5235 SCIP_CALL( getBoundConsFromVertices(scip, conflictgraph, sol, vertex1, vertex2, NULL, conshdlrdata->addextendedbds, conssos1, &feas) );
5236 }
5237
5238 /* add linear constraint to the branching node if usefull */
5239 if ( SCIPisGT(scip, feas, conshdlrdata->addbdsfeas ) )
5240 {
5241 SCIP_CALL( SCIPaddConsNode(scip, node, conssos1, NULL) );
5242 ++(*naddedconss);
5243 }
5244
5245 /* release constraint */
5246 SCIP_CALL( SCIPreleaseCons(scip, &conssos1) );
5247 }
5248
5249 /* break if number of added constraints exceeds a predefined value */
5250 if ( conshdlrdata->maxaddcomps >= 0 && *naddedconss > conshdlrdata->maxaddcomps )
5251 break;
5252 }
5253 }
5254 }
5255 }
5256
5257 /* break if number of added constraints exceeds a predefined value */
5258 if ( conshdlrdata->maxaddcomps >= 0 && *naddedconss > conshdlrdata->maxaddcomps )
5259 break;
5260 }
5261
5262 /* free buffer array */
5263 SCIPfreeBufferArray(scip, &coverarray);
5264 SCIPfreeBufferArray(scip, &fixingsnode22);
5265 SCIPfreeBufferArray(scip, &fixingsnode21);
5266 SCIPfreeBufferArray(scip, &mark);
5267 SCIPfreeBufferArray(scip, &succarray);
5268 }
5269
5270 return SCIP_OKAY;
5271 }
5272
5273
5274 /** resets local conflict graph to the conflict graph of the root node */
5275 static
resetConflictgraphSOS1(SCIP_DIGRAPH * conflictgraph,SCIP_DIGRAPH * localconflicts,int nsos1vars)5276 SCIP_RETCODE resetConflictgraphSOS1(
5277 SCIP_DIGRAPH* conflictgraph, /**< conflict graph of root node */
5278 SCIP_DIGRAPH* localconflicts, /**< local conflicts that should be removed from conflict graph */
5279 int nsos1vars /**< number of SOS1 variables */
5280 )
5281 {
5282 int j;
5283
5284 for (j = 0; j < nsos1vars; ++j)
5285 {
5286 int nsuccloc;
5287
5288 nsuccloc = SCIPdigraphGetNSuccessors(localconflicts, j);
5289 if ( nsuccloc > 0 )
5290 {
5291 int* succloc;
5292 int* succ;
5293 int nsucc;
5294 int k = 0;
5295
5296 succloc = SCIPdigraphGetSuccessors(localconflicts, j);
5297 succ = SCIPdigraphGetSuccessors(conflictgraph, j);
5298 nsucc = SCIPdigraphGetNSuccessors(conflictgraph, j);
5299
5300 /* reset number of successors */
5301 SCIP_CALL( SCIPcomputeArraysSetminus(succ, nsucc, succloc, nsuccloc, succ, &k) );
5302 SCIP_CALL( SCIPdigraphSetNSuccessors(conflictgraph, j, k) );
5303 SCIP_CALL( SCIPdigraphSetNSuccessors(localconflicts, j, 0) );
5304 }
5305 }
5306
5307 return SCIP_OKAY;
5308 }
5309
5310
5311 /** Conflict graph enforcement method
5312 *
5313 * The conflict graph can be enforced by different branching rules:
5314 *
5315 * - Branch on the neighborhood of a single variable @p i, i.e., in one branch \f$x_i\f$ is fixed to zero and in the
5316 * other its neighbors from the conflict graph.
5317 *
5318 * - Branch on complete bipartite subgraphs of the conflict graph, i.e., in one branch fix the variables from the first
5319 * bipartite partition and the variables from the second bipartite partition in the other.
5320 *
5321 * - In addition to variable domain fixings, it is sometimes also possible to add new SOS1 constraints to the branching
5322 * nodes. This results in a nonstatic conflict graph, which may change dynamically with every branching node.
5323 *
5324 * We make use of different selection rules that define on which system of SOS1 variables to branch next:
5325 *
5326 * - Most infeasible branching: Branch on the system of SOS1 variables with largest violation.
5327 *
5328 * - Strong branching: Here, the LP-relaxation is partially solved for each branching decision among a candidate list.
5329 * Then the decision with best progress is chosen.
5330 */
5331 static
enforceConflictgraph(SCIP * scip,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_CONSHDLR * conshdlr,int nconss,SCIP_CONS ** conss,SCIP_SOL * sol,SCIP_RESULT * result)5332 SCIP_RETCODE enforceConflictgraph(
5333 SCIP* scip, /**< SCIP pointer */
5334 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
5335 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
5336 int nconss, /**< number of constraints */
5337 SCIP_CONS** conss, /**< SOS1 constraints */
5338 SCIP_SOL* sol, /**< solution to be enforced (NULL for LP solution) */
5339 SCIP_RESULT* result /**< result */
5340 )
5341 {
5342 SCIP_DIGRAPH* conflictgraph;
5343 int nsos1vars;
5344
5345 SCIP_Bool* verticesarefixed = NULL;
5346 int* fixingsnode1 = NULL;
5347 int* fixingsnode2 = NULL;
5348 int nfixingsnode1;
5349 int nfixingsnode2;
5350
5351 SCIP_Real bestobjval1 = -SCIPinfinity(scip);
5352 SCIP_Real bestobjval2 = -SCIPinfinity(scip);
5353 SCIP_Real lpobjval = -SCIPinfinity(scip);
5354
5355 SCIP_Bool infeasible;
5356 SCIP_Bool bipbranch = FALSE;
5357 int nstrongrounds;
5358
5359 int branchvertex;
5360 SCIP_NODE* node1;
5361 SCIP_NODE* node2;
5362 SCIP_Real nodeselest;
5363 SCIP_Real objest;
5364
5365 int i;
5366 int j;
5367 int c;
5368
5369 assert( scip != NULL );
5370 assert( conshdlrdata != NULL );
5371 assert( conshdlr != NULL );
5372 assert( conss != NULL );
5373 assert( result != NULL );
5374
5375 SCIPdebugMsg(scip, "Enforcing SOS1 conflict graph <%s>.\n", SCIPconshdlrGetName(conshdlr) );
5376 *result = SCIP_DIDNOTRUN;
5377
5378 /* get number of SOS1 variables */
5379 nsos1vars = conshdlrdata->nsos1vars;
5380
5381 /* exit for trivial cases */
5382 if ( nsos1vars == 0 || nconss == 0 )
5383 {
5384 *result = SCIP_FEASIBLE;
5385 return SCIP_OKAY;
5386 }
5387
5388 /* get conflict graph */
5389 conflictgraph = conshdlrdata->conflictgraph;
5390 assert( ! conshdlrdata->isconflocal ); /* conflictgraph should be the one of the root node */
5391
5392 /* check each constraint and update conflict graph if necessary */
5393 for (c = 0; c < nconss; ++c)
5394 {
5395 SCIP_CONSDATA* consdata;
5396 SCIP_CONS* cons;
5397 SCIP_Bool cutoff;
5398 int ngen = 0;
5399
5400 cons = conss[c];
5401 assert( cons != NULL );
5402 consdata = SCIPconsGetData(cons);
5403 assert( consdata != NULL );
5404
5405 /* do nothing if there are not enough variables - this is usually eliminated by preprocessing */
5406 if ( consdata->nvars < 2 )
5407 continue;
5408
5409 /* first perform propagation (it might happen that standard propagation is turned off) */
5410 SCIP_CALL( propConsSOS1(scip, cons, consdata, &cutoff, &ngen) );
5411 SCIPdebugMsg(scip, "propagating <%s> in enforcing (cutoff: %u, domain reductions: %d).\n", SCIPconsGetName(cons), cutoff, ngen);
5412 if ( cutoff )
5413 {
5414 *result = SCIP_CUTOFF;
5415 break;
5416 }
5417 if ( ngen > 0 )
5418 {
5419 *result = SCIP_REDUCEDDOM;
5420 break;
5421 }
5422 assert( ngen == 0 );
5423
5424 /* add local conflicts to conflict graph and save them in 'localconflicts' */
5425 if ( consdata->local )
5426 {
5427 SCIP_VAR** vars;
5428 int nvars;
5429 int indi;
5430 int indj;
5431
5432 if ( conshdlrdata->localconflicts == NULL )
5433 {
5434 SCIP_CALL( SCIPcreateDigraph(scip, &conshdlrdata->localconflicts, nsos1vars) );
5435 }
5436
5437 vars = consdata->vars;
5438 nvars = consdata->nvars;
5439 for (i = 0; i < nvars-1; ++i)
5440 {
5441 SCIP_VAR* var;
5442
5443 var = vars[i];
5444 indi = varGetNodeSOS1(conshdlrdata, var);
5445
5446 if( indi == -1 )
5447 return SCIP_INVALIDDATA;
5448
5449 if ( ! SCIPisFeasZero(scip, SCIPvarGetUbLocal(var)) || ! SCIPisFeasZero(scip, SCIPvarGetLbLocal(var)) )
5450 {
5451 for (j = i+1; j < nvars; ++j)
5452 {
5453 var = vars[j];
5454 indj = varGetNodeSOS1(conshdlrdata, var);
5455
5456 if( indj == -1 )
5457 return SCIP_INVALIDDATA;
5458
5459 if ( ! SCIPisFeasZero(scip, SCIPvarGetUbLocal(var)) || ! SCIPisFeasZero(scip, SCIPvarGetLbLocal(var)) )
5460 {
5461 if ( ! isConnectedSOS1(NULL, conflictgraph, indi, indj) )
5462 {
5463 SCIP_CALL( SCIPdigraphAddArcSafe(conflictgraph, indi, indj, NULL) );
5464 SCIP_CALL( SCIPdigraphAddArcSafe(conflictgraph, indj, indi, NULL) );
5465
5466 SCIP_CALL( SCIPdigraphAddArcSafe(conshdlrdata->localconflicts, indi, indj, NULL) );
5467 SCIP_CALL( SCIPdigraphAddArcSafe(conshdlrdata->localconflicts, indj, indi, NULL) );
5468
5469 conshdlrdata->isconflocal = TRUE;
5470 }
5471 }
5472 }
5473 }
5474 }
5475 }
5476 }
5477
5478 /* sort successor list of conflict graph if necessary */
5479 if ( conshdlrdata->isconflocal )
5480 {
5481 for (j = 0; j < nsos1vars; ++j)
5482 {
5483 int nsuccloc;
5484
5485 nsuccloc = SCIPdigraphGetNSuccessors(conshdlrdata->localconflicts, j);
5486 if ( nsuccloc > 0 )
5487 {
5488 SCIPsortInt(SCIPdigraphGetSuccessors(conflictgraph, j), SCIPdigraphGetNSuccessors(conflictgraph, j));
5489 SCIPsortInt(SCIPdigraphGetSuccessors(conshdlrdata->localconflicts, j), nsuccloc);
5490 }
5491 }
5492 }
5493
5494 if ( *result == SCIP_CUTOFF || *result == SCIP_REDUCEDDOM )
5495 {
5496 /* remove local conflicts from conflict graph */
5497 if ( conshdlrdata->isconflocal )
5498 {
5499 SCIP_CALL( resetConflictgraphSOS1(conflictgraph, conshdlrdata->localconflicts, nsos1vars) );
5500 conshdlrdata->isconflocal = FALSE;
5501 }
5502 return SCIP_OKAY;
5503 }
5504
5505 /* detect fixed variables */
5506 SCIP_CALL( SCIPallocBufferArray(scip, &verticesarefixed, nsos1vars) );
5507 for (j = 0; j < nsos1vars; ++j)
5508 {
5509 SCIP_VAR* var;
5510 SCIP_Real ub;
5511 SCIP_Real lb;
5512
5513 var = SCIPnodeGetVarSOS1(conflictgraph, j);
5514 ub = SCIPvarGetUbLocal(var);
5515 lb = SCIPvarGetLbLocal(var);
5516 if ( SCIPisFeasZero(scip, ub) && SCIPisFeasZero(scip, lb) )
5517 verticesarefixed[j] = TRUE;
5518 else
5519 verticesarefixed[j] = FALSE;
5520 }
5521
5522 /* should bipartite branching be used? */
5523 if ( conshdlrdata->branchingrule == 'b' )
5524 bipbranch = TRUE;
5525
5526 /* determine number of strong branching iterations */
5527 if ( conshdlrdata->nstrongrounds >= 0 )
5528 nstrongrounds = MIN(conshdlrdata->nstrongrounds, nsos1vars);
5529 else
5530 {
5531 /* determine number depending on depth, based on heuristical considerations */
5532 if ( SCIPgetDepth(scip) <= 10 )
5533 nstrongrounds = MAX(10, (int)SCIPfloor(scip, pow(log((SCIP_Real)nsos1vars), 1.0)));/*lint !e666*/
5534 else if ( SCIPgetDepth(scip) <= 20 )
5535 nstrongrounds = MAX(5, (int)SCIPfloor(scip, pow(log((SCIP_Real)nsos1vars), 0.7)));/*lint !e666*/
5536 else
5537 nstrongrounds = 0;
5538 nstrongrounds = MIN(nsos1vars, nstrongrounds);
5539 }
5540
5541 /* allocate buffer arrays */
5542 SCIP_CALL( SCIPallocBufferArray(scip, &fixingsnode1, nsos1vars) );
5543 if ( bipbranch )
5544 SCIP_CALL( SCIPallocBufferArray(scip, &fixingsnode2, nsos1vars) );
5545 else
5546 SCIP_CALL( SCIPallocBufferArray(scip, &fixingsnode2, 1) );
5547
5548 /* if strongbranching is turned off: use most infeasible branching */
5549 if ( nstrongrounds == 0 )
5550 {
5551 SCIP_Bool relsolfeas;
5552
5553 /* get branching vertex using most infeasible branching */
5554 SCIP_CALL( getBranchingPrioritiesSOS1(scip, conshdlrdata, conflictgraph, sol, nsos1vars, verticesarefixed,
5555 bipbranch, fixingsnode1, fixingsnode2, NULL, &branchvertex, &relsolfeas) );
5556
5557 /* if LP relaxation solution is feasible */
5558 if ( relsolfeas )
5559 {
5560 SCIPdebugMsg(scip, "all the SOS1 constraints are feasible.\n");
5561
5562 /* update result */
5563 *result = SCIP_FEASIBLE;
5564
5565 /* remove local conflicts from conflict graph */
5566 if ( conshdlrdata->isconflocal )
5567 {
5568 SCIP_CALL( resetConflictgraphSOS1(conflictgraph, conshdlrdata->localconflicts, nsos1vars) );
5569 conshdlrdata->isconflocal = FALSE;
5570 }
5571
5572 /* free memory */
5573 SCIPfreeBufferArrayNull(scip, &fixingsnode2);
5574 SCIPfreeBufferArrayNull(scip, &fixingsnode1);
5575 SCIPfreeBufferArrayNull(scip, &verticesarefixed);
5576
5577 return SCIP_OKAY;
5578 }
5579 }
5580 else
5581 {
5582 /* get branching vertex using strong branching */
5583 SCIP_CALL( getBranchingDecisionStrongbranchSOS1(scip, conshdlrdata, conflictgraph, sol, nsos1vars, lpobjval,
5584 bipbranch, nstrongrounds, verticesarefixed, fixingsnode1, fixingsnode2, &branchvertex, &bestobjval1,
5585 &bestobjval2, result) );
5586
5587 if ( *result == SCIP_CUTOFF || *result == SCIP_FEASIBLE || *result == SCIP_REDUCEDDOM )
5588 {
5589 /* remove local conflicts from conflict graph */
5590 if ( conshdlrdata->isconflocal )
5591 {
5592 SCIP_CALL( resetConflictgraphSOS1(conflictgraph, conshdlrdata->localconflicts, nsos1vars) );
5593 conshdlrdata->isconflocal = FALSE;
5594 }
5595
5596 /* free memory */
5597 SCIPfreeBufferArrayNull(scip, &fixingsnode2);
5598 SCIPfreeBufferArrayNull(scip, &fixingsnode1);
5599 SCIPfreeBufferArrayNull(scip, &verticesarefixed);
5600
5601 return SCIP_OKAY;
5602 }
5603 }
5604
5605 /* if we should leave branching decision to branching rules */
5606 if ( ! conshdlrdata->branchsos )
5607 {
5608 /* remove local conflicts from conflict graph */
5609 if ( conshdlrdata->isconflocal )
5610 {
5611 SCIP_CALL( resetConflictgraphSOS1(conflictgraph, conshdlrdata->localconflicts, nsos1vars) );
5612 conshdlrdata->isconflocal = FALSE;
5613 }
5614
5615 /* free memory */
5616 SCIPfreeBufferArrayNull(scip, &fixingsnode2);
5617 SCIPfreeBufferArrayNull(scip, &fixingsnode1);
5618 SCIPfreeBufferArrayNull(scip, &verticesarefixed);
5619
5620 assert( branchvertex >= 0 && branchvertex < nsos1vars );
5621 if ( SCIPvarIsBinary(SCIPnodeGetVarSOS1(conflictgraph, branchvertex)) )
5622 {
5623 *result = SCIP_INFEASIBLE;
5624 return SCIP_OKAY;
5625 }
5626 else
5627 {
5628 SCIPerrorMessage("Incompatible parameter setting: branchsos can only be set to false if all SOS1 variables are binary.\n");
5629 return SCIP_PARAMETERWRONGVAL;
5630 }
5631 }
5632
5633 /* create branching nodes */
5634
5635 /* get vertices of variables that will be fixed to zero for each node */
5636 assert( branchvertex >= 0 && branchvertex < nsos1vars );
5637 assert( ! verticesarefixed[branchvertex] );
5638 SCIP_CALL( getBranchingVerticesSOS1(scip, conflictgraph, sol, verticesarefixed, bipbranch, branchvertex,
5639 fixingsnode1, &nfixingsnode1, fixingsnode2, &nfixingsnode2) );
5640
5641 /* calculate node selection and objective estimate for node 1 */
5642 nodeselest = 0.0;
5643 objest = SCIPgetLocalTransEstimate(scip);
5644 for (j = 0; j < nfixingsnode1; ++j)
5645 {
5646 SCIP_VAR* var;
5647
5648 var = SCIPnodeGetVarSOS1(conflictgraph, fixingsnode1[j]);
5649 objest += SCIPcalcChildEstimateIncrease(scip, var, SCIPgetSolVal(scip, sol, var), 0.0);
5650 nodeselest += SCIPcalcNodeselPriority(scip, var, SCIP_BRANCHDIR_DOWNWARDS, 0.0);
5651 }
5652 assert( objest >= SCIPgetLocalTransEstimate(scip) );
5653
5654 /* create node 1 */
5655 SCIP_CALL( SCIPcreateChild(scip, &node1, nodeselest, objest) );
5656
5657 /* fix variables for the first node */
5658 if ( conshdlrdata->fixnonzero && nfixingsnode2 == 1 )
5659 {
5660 SCIP_VAR* var;
5661 SCIP_Real lb;
5662 SCIP_Real ub;
5663
5664 var = SCIPnodeGetVarSOS1(conflictgraph, fixingsnode2[0]);
5665 lb = SCIPvarGetLbLocal(var);
5666 ub = SCIPvarGetUbLocal(var);
5667
5668 if ( SCIPvarGetStatus(var) != SCIP_VARSTATUS_MULTAGGR )
5669 {
5670 if ( SCIPisZero(scip, lb) )
5671 {
5672 /* fix variable to some very small, but positive number or to 1.0 if variable is integral */
5673 if (SCIPvarIsIntegral(var) )
5674 {
5675 SCIP_CALL( SCIPchgVarLbNode(scip, node1, var, 1.0) );
5676 }
5677 else
5678 {
5679 SCIP_CALL( SCIPchgVarLbNode(scip, node1, var, 1.5 * SCIPfeastol(scip)) );
5680 }
5681 }
5682 else if ( SCIPisZero(scip, ub) )
5683 {
5684 if (SCIPvarIsIntegral(var) )
5685 {
5686 /* fix variable to some negative number with small absolute value to -1.0 if variable is integral */
5687 SCIP_CALL( SCIPchgVarUbNode(scip, node1, var, -1.0) );
5688 }
5689 else
5690 {
5691 /* fix variable to some negative number with small absolute value to -1.0 if variable is integral */
5692 SCIP_CALL( SCIPchgVarUbNode(scip, node1, var, -1.5 * SCIPfeastol(scip)) );
5693 }
5694 }
5695 }
5696 }
5697
5698 for (j = 0; j < nfixingsnode1; ++j)
5699 {
5700 /* fix variable to zero */
5701 SCIP_CALL( fixVariableZeroNode(scip, SCIPnodeGetVarSOS1(conflictgraph, fixingsnode1[j]), node1, &infeasible) );
5702 assert( ! infeasible );
5703 }
5704
5705 /* calculate node selection and objective estimate for node 2 */
5706 nodeselest = 0.0;
5707 objest = SCIPgetLocalTransEstimate(scip);
5708 for (j = 0; j < nfixingsnode2; ++j)
5709 {
5710 SCIP_VAR* var;
5711
5712 var = SCIPnodeGetVarSOS1(conflictgraph, fixingsnode1[j]);
5713 objest += SCIPcalcChildEstimateIncrease(scip, var, SCIPgetSolVal(scip, sol, var), 0.0);
5714 nodeselest += SCIPcalcNodeselPriority(scip, var, SCIP_BRANCHDIR_DOWNWARDS, 0.0);
5715 }
5716 assert( objest >= SCIPgetLocalTransEstimate(scip) );
5717
5718 /* create node 2 */
5719 SCIP_CALL( SCIPcreateChild(scip, &node2, nodeselest, objest) );
5720
5721 /* fix variables to zero */
5722 for (j = 0; j < nfixingsnode2; ++j)
5723 {
5724 SCIP_CALL( fixVariableZeroNode(scip, SCIPnodeGetVarSOS1(conflictgraph, fixingsnode2[j]), node2, &infeasible) );
5725 assert( ! infeasible );
5726 }
5727
5728 /* add complementarity constraints to the branching nodes */
5729 if ( conshdlrdata->addcomps && ( conshdlrdata->addcompsdepth == -1 || conshdlrdata->addcompsdepth >= SCIPgetDepth(scip) ) )
5730 {
5731 int naddedconss;
5732
5733 assert( ! conshdlrdata->fixnonzero );
5734
5735 /* add complementarity constraints to the left branching node */
5736 SCIP_CALL( addBranchingComplementaritiesSOS1(scip, node1, conshdlrdata, conflictgraph, conshdlrdata->localconflicts, sol,
5737 nsos1vars, verticesarefixed, fixingsnode1, nfixingsnode1, fixingsnode2, nfixingsnode2, &naddedconss, TRUE) );
5738
5739 if ( naddedconss == 0 )
5740 {
5741 /* add complementarity constraints to the right branching node */
5742 SCIP_CALL( addBranchingComplementaritiesSOS1(scip, node2, conshdlrdata, conflictgraph, conshdlrdata->localconflicts, sol,
5743 nsos1vars, verticesarefixed, fixingsnode2, nfixingsnode2, fixingsnode1, nfixingsnode1, &naddedconss, TRUE) );
5744 }
5745 }
5746
5747 /* sets node's lower bound to the best known value */
5748 if ( nstrongrounds > 0 )
5749 {
5750 SCIP_CALL( SCIPupdateNodeLowerbound(scip, node1, MAX(lpobjval, bestobjval1) ) );
5751 SCIP_CALL( SCIPupdateNodeLowerbound(scip, node2, MAX(lpobjval, bestobjval2) ) );
5752 }
5753
5754 /* remove local conflicts from conflict graph */
5755 if ( conshdlrdata->isconflocal )
5756 {
5757 SCIP_CALL( resetConflictgraphSOS1(conflictgraph, conshdlrdata->localconflicts, nsos1vars) );
5758 conshdlrdata->isconflocal = FALSE;
5759 }
5760
5761 /* free buffer arrays */
5762 SCIPfreeBufferArrayNull(scip, &fixingsnode2);
5763 SCIPfreeBufferArrayNull(scip, &fixingsnode1);
5764 SCIPfreeBufferArrayNull(scip, &verticesarefixed );
5765 *result = SCIP_BRANCHED;
5766
5767 return SCIP_OKAY;
5768 }
5769
5770
5771 /** SOS1 branching enforcement method
5772 *
5773 * We check whether the current solution is feasible, i.e., contains at most one nonzero
5774 * variable. If not, we branch along the lines indicated by Beale and Tomlin:
5775 *
5776 * We first compute \f$W = \sum_{j=1}^n |x_i|\f$ and \f$w = \sum_{j=1}^n j\, |x_i|\f$. Then we
5777 * search for the index \f$k\f$ that satisfies
5778 * \f[
5779 * k \leq \frac{w}{W} < k+1.
5780 * \f]
5781 * The branches are then
5782 * \f[
5783 * x_1 = 0, \ldots, x_k = 0 \qquad \mbox{and}\qquad x_{k+1} = 0, \ldots, x_n = 0.
5784 * \f]
5785 *
5786 * If the constraint contains two variables, the branching of course simplifies.
5787 *
5788 * Depending on the parameters (@c branchnonzeros, @c branchweight) there are three ways to choose
5789 * the branching constraint.
5790 *
5791 * <TABLE>
5792 * <TR><TD>@c branchnonzeros</TD><TD>@c branchweight</TD><TD>constraint chosen</TD></TR>
5793 * <TR><TD>@c true </TD><TD> ? </TD><TD>most number of nonzeros</TD></TR>
5794 * <TR><TD>@c false </TD><TD> @c true </TD><TD>maximal weight corresponding to nonzero variable</TD></TR>
5795 * <TR><TD>@c false </TD><TD> @c true </TD><TD>largest sum of variable values</TD></TR>
5796 * </TABLE>
5797 *
5798 * @c branchnonzeros = @c false, @c branchweight = @c true allows the user to specify an order for
5799 * the branching importance of the constraints (setting the weights accordingly).
5800 *
5801 * Constraint branching can also be turned off using parameter @c branchsos.
5802 */
5803 static
enforceConssSOS1(SCIP * scip,SCIP_CONSHDLR * conshdlr,int nconss,SCIP_CONS ** conss,SCIP_SOL * sol,SCIP_RESULT * result)5804 SCIP_RETCODE enforceConssSOS1(
5805 SCIP* scip, /**< SCIP pointer */
5806 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
5807 int nconss, /**< number of constraints */
5808 SCIP_CONS** conss, /**< indicator constraints */
5809 SCIP_SOL* sol, /**< solution to be enforced (NULL for LP solution) */
5810 SCIP_RESULT* result /**< result */
5811 )
5812 {
5813 SCIP_CONSHDLRDATA* conshdlrdata;
5814 SCIP_CONSDATA* consdata;
5815 SCIP_NODE* node1;
5816 SCIP_NODE* node2;
5817 SCIP_CONS* branchCons;
5818 SCIP_Real maxWeight;
5819 SCIP_VAR** vars;
5820 int nvars;
5821 int c;
5822
5823 assert( scip != NULL );
5824 assert( conshdlr != NULL );
5825 assert( conss != NULL );
5826 assert( result != NULL );
5827
5828 maxWeight = -SCIP_REAL_MAX;
5829 branchCons = NULL;
5830
5831 SCIPdebugMsg(scip, "Enforcing SOS1 constraints <%s>.\n", SCIPconshdlrGetName(conshdlr) );
5832 *result = SCIP_FEASIBLE;
5833
5834 /* get constraint handler data */
5835 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5836 assert( conshdlrdata != NULL );
5837
5838 /* check each constraint */
5839 for (c = 0; c < nconss; ++c)
5840 {
5841 SCIP_CONS* cons;
5842 SCIP_Bool cutoff;
5843 SCIP_Real weight;
5844 int ngen;
5845 int cnt;
5846 int j;
5847
5848 cons = conss[c];
5849 assert( cons != NULL );
5850 consdata = SCIPconsGetData(cons);
5851 assert( consdata != NULL );
5852
5853 ngen = 0;
5854 cnt = 0;
5855 nvars = consdata->nvars;
5856 vars = consdata->vars;
5857
5858 /* do nothing if there are not enough variables - this is usually eliminated by preprocessing */
5859 if ( nvars < 2 )
5860 continue;
5861
5862 /* first perform propagation (it might happen that standard propagation is turned off) */
5863 SCIP_CALL( propConsSOS1(scip, cons, consdata, &cutoff, &ngen) );
5864 SCIPdebugMsg(scip, "propagating <%s> in enforcing (cutoff: %u, domain reductions: %d).\n", SCIPconsGetName(cons), cutoff, ngen);
5865 if ( cutoff )
5866 {
5867 *result = SCIP_CUTOFF;
5868 return SCIP_OKAY;
5869 }
5870 if ( ngen > 0 )
5871 {
5872 *result = SCIP_REDUCEDDOM;
5873 return SCIP_OKAY;
5874 }
5875 assert( ngen == 0 );
5876
5877 /* check constraint */
5878 weight = 0.0;
5879 for (j = 0; j < nvars; ++j)
5880 {
5881 SCIP_Real val = REALABS(SCIPgetSolVal(scip, sol, vars[j]));
5882
5883 if ( ! SCIPisFeasZero(scip, val) )
5884 {
5885 if ( conshdlrdata->branchnonzeros )
5886 weight += 1.0;
5887 else
5888 {
5889 if ( conshdlrdata->branchweight && consdata->weights != NULL )
5890 {
5891 /* choose maximum nonzero-variable weight */
5892 if ( consdata->weights[j] > weight )
5893 weight = consdata->weights[j];
5894 }
5895 else
5896 weight += val;
5897 }
5898 ++cnt;
5899 }
5900 }
5901 /* if constraint is violated */
5902 if ( cnt > 1 && weight > maxWeight )
5903 {
5904 maxWeight = weight;
5905 branchCons = cons;
5906 }
5907 }
5908
5909 /* if all constraints are feasible */
5910 if ( branchCons == NULL )
5911 {
5912 SCIPdebugMsg(scip, "All SOS1 constraints are feasible.\n");
5913 return SCIP_OKAY;
5914 }
5915
5916 /* if we should leave branching decision to branching rules */
5917 if ( ! conshdlrdata->branchsos )
5918 {
5919 int j;
5920
5921 consdata = SCIPconsGetData(branchCons);
5922 for (j = 0; j < consdata->nvars; ++j)
5923 {
5924 if ( ! SCIPvarIsBinary(consdata->vars[j]) )
5925 break;
5926 }
5927
5928 if ( j == consdata->nvars )
5929 {
5930 *result = SCIP_INFEASIBLE;
5931 return SCIP_OKAY;
5932 }
5933 else
5934 {
5935 SCIPerrorMessage("Incompatible parameter setting: branchsos can only be set to false if all SOS1 variables are binary.\n");
5936 return SCIP_PARAMETERWRONGVAL;
5937 }
5938 }
5939
5940 /* otherwise create branches */
5941 SCIPdebugMsg(scip, "Branching on constraint <%s> (weight: %f).\n", SCIPconsGetName(branchCons), maxWeight);
5942 consdata = SCIPconsGetData(branchCons);
5943 assert( consdata != NULL );
5944 nvars = consdata->nvars;
5945 vars = consdata->vars;
5946
5947 if ( nvars == 2 )
5948 {
5949 SCIP_Bool infeasible;
5950
5951 /* constraint is infeasible: */
5952 assert( ! SCIPisFeasZero(scip, SCIPgetSolVal(scip, sol, vars[0])) && ! SCIPisFeasZero(scip, SCIPgetSolVal(scip, sol, vars[1])) );
5953
5954 /* create branches */
5955 SCIPdebugMsg(scip, "Creating two branches.\n");
5956
5957 SCIP_CALL( SCIPcreateChild(scip, &node1, SCIPcalcNodeselPriority(scip, vars[0], SCIP_BRANCHDIR_DOWNWARDS, 0.0), SCIPcalcChildEstimate(scip, vars[0], 0.0) ) );
5958 SCIP_CALL( fixVariableZeroNode(scip, vars[0], node1, &infeasible) );
5959 assert( ! infeasible );
5960
5961 SCIP_CALL( SCIPcreateChild(scip, &node2, SCIPcalcNodeselPriority(scip, vars[1], SCIP_BRANCHDIR_DOWNWARDS, 0.0), SCIPcalcChildEstimate(scip, vars[1], 0.0) ) );
5962 SCIP_CALL( fixVariableZeroNode(scip, vars[1], node2, &infeasible) );
5963 assert( ! infeasible );
5964 }
5965 else
5966 {
5967 SCIP_Bool infeasible;
5968 SCIP_Real weight1;
5969 SCIP_Real weight2;
5970 SCIP_Real nodeselest;
5971 SCIP_Real objest;
5972 SCIP_Real w;
5973 int j;
5974 int ind;
5975 int cnt;
5976
5977 cnt = 0;
5978
5979 weight1 = 0.0;
5980 weight2 = 0.0;
5981
5982 /* compute weight */
5983 for (j = 0; j < nvars; ++j)
5984 {
5985 SCIP_Real val = REALABS(SCIPgetSolVal(scip, sol, vars[j]));
5986 weight1 += val * (SCIP_Real) j;
5987 weight2 += val;
5988
5989 if ( ! SCIPisFeasZero(scip, val) )
5990 ++cnt;
5991 }
5992
5993 assert( cnt >= 2 );
5994 assert( !SCIPisFeasZero(scip, weight2) );
5995 w = weight1/weight2; /*lint !e795*/
5996
5997 ind = (int) SCIPfloor(scip, w);
5998 assert( 0 <= ind && ind < nvars-1 );
5999
6000 /* branch on variable ind: either all variables up to ind or all variables after ind are zero */
6001 SCIPdebugMsg(scip, "Branching on variable <%s>.\n", SCIPvarGetName(vars[ind]));
6002
6003 /* calculate node selection and objective estimate for node 1 */
6004 nodeselest = 0.0;
6005 objest = SCIPgetLocalTransEstimate(scip);
6006 for (j = 0; j <= ind; ++j)
6007 {
6008 objest += SCIPcalcChildEstimateIncrease(scip, vars[j], SCIPgetSolVal(scip, sol, vars[j]), 0.0);
6009 nodeselest += SCIPcalcNodeselPriority(scip, vars[j], SCIP_BRANCHDIR_DOWNWARDS, 0.0);
6010 }
6011 assert( objest >= SCIPgetLocalTransEstimate(scip) );
6012
6013 /* create node 1 */
6014 SCIP_CALL( SCIPcreateChild(scip, &node1, nodeselest, objest) );
6015 for (j = 0; j <= ind; ++j)
6016 {
6017 SCIP_CALL( fixVariableZeroNode(scip, vars[j], node1, &infeasible) );
6018 assert( ! infeasible );
6019 }
6020
6021 /* calculate node selection and objective estimate for node 1 */
6022 nodeselest = 0.0;
6023 objest = SCIPgetLocalTransEstimate(scip);
6024 for (j = ind+1; j < nvars; ++j)
6025 {
6026 objest += SCIPcalcChildEstimateIncrease(scip, vars[j], SCIPgetSolVal(scip, sol, vars[j]), 0.0);
6027 nodeselest += SCIPcalcNodeselPriority(scip, vars[j], SCIP_BRANCHDIR_DOWNWARDS, 0.0);
6028 }
6029 assert( objest >= SCIPgetLocalTransEstimate(scip) );
6030
6031 /* create node 2 */
6032 SCIP_CALL( SCIPcreateChild(scip, &node2, nodeselest, objest) );
6033 for (j = ind+1; j < nvars; ++j)
6034 {
6035 SCIP_CALL( fixVariableZeroNode(scip, vars[j], node2, &infeasible) );
6036 assert( ! infeasible );
6037 }
6038 }
6039 SCIP_CALL( SCIPresetConsAge(scip, branchCons) );
6040 *result = SCIP_BRANCHED;
6041
6042 return SCIP_OKAY;
6043 }
6044
6045
6046 /** constraint enforcing method of constraint handler */
6047 static
enforceSOS1(SCIP * scip,SCIP_CONSHDLR * conshdlr,int nconss,SCIP_CONS ** conss,SCIP_SOL * sol,SCIP_RESULT * result)6048 SCIP_RETCODE enforceSOS1(
6049 SCIP* scip, /**< SCIP pointer */
6050 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
6051 int nconss, /**< number of constraints */
6052 SCIP_CONS** conss, /**< indicator constraints */
6053 SCIP_SOL* sol, /**< solution to be enforced (NULL for LP solution) */
6054 SCIP_RESULT* result /**< result */
6055 )
6056 {
6057 SCIP_CONSHDLRDATA* conshdlrdata;
6058
6059 assert( scip != NULL );
6060 assert( conshdlr != NULL );
6061 assert( conss != NULL );
6062 assert( result != NULL );
6063
6064 /* get constraint handler data */
6065 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6066 assert( conshdlrdata != NULL );
6067
6068 if ( conshdlrdata->addcomps && conshdlrdata->fixnonzero )
6069 {
6070 SCIPerrorMessage("Incompatible parameter setting: addcomps = TRUE and fixnonzero = TRUE.\n");
6071 return SCIP_PARAMETERWRONGVAL;
6072 }
6073
6074 if ( conshdlrdata->fixnonzero && ( conshdlrdata->branchingrule == 'b' || conshdlrdata->branchingrule == 's' ) )
6075 {
6076 SCIPerrorMessage("Incompatible parameter setting: nonzero fixing is not compatible with bipartite or sos1 branching.\n");
6077 return SCIP_PARAMETERWRONGVAL;
6078 }
6079
6080 if ( conshdlrdata->branchingrule == 's' && conshdlrdata->nstrongrounds != 0 )
6081 {
6082 SCIPerrorMessage("Strong branching is not available for SOS1 branching.\n");
6083 return SCIP_PARAMETERWRONGVAL;
6084 }
6085
6086 if ( conshdlrdata->branchingrule == 's' || conshdlrdata->switchsos1branch )
6087 {
6088 /* enforce SOS1 constraints */
6089 SCIP_CALL( enforceConssSOS1(scip, conshdlr, nconss, conss, sol, result) );
6090 }
6091 else
6092 {
6093 if ( conshdlrdata->branchingrule != 'n' && conshdlrdata->branchingrule != 'b' )
6094 {
6095 SCIPerrorMessage("branching rule %c unknown\n", conshdlrdata->branchingrule);
6096 return SCIP_PARAMETERWRONGVAL;
6097 }
6098
6099 /* enforce conflict graph */
6100 SCIP_CALL( enforceConflictgraph(scip, conshdlrdata, conshdlr, nconss, conss, sol, result) );
6101 }
6102
6103 return SCIP_OKAY;
6104 }
6105
6106
6107 /* ----------------------------- separation ------------------------------------*/
6108
6109 /** initialitze tclique graph and create clique data */
6110 static
initTCliquegraph(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_DIGRAPH * conflictgraph,int nsos1vars)6111 SCIP_RETCODE initTCliquegraph(
6112 SCIP* scip, /**< SCIP pointer */
6113 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
6114 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
6115 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
6116 int nsos1vars /**< number of SOS1 variables */
6117 )
6118 {
6119 TCLIQUE_DATA* tcliquedata;
6120 int j;
6121
6122 /* try to generate bound cuts */
6123 if ( ! tcliqueCreate(&conshdlrdata->tcliquegraph) )
6124 return SCIP_NOMEMORY;
6125
6126 /* add nodes */
6127 for (j = 0; j < nsos1vars; ++j)
6128 {
6129 if ( ! tcliqueAddNode(conshdlrdata->tcliquegraph, j, 0 ) )
6130 return SCIP_NOMEMORY;
6131 }
6132
6133 /* add edges */
6134 for (j = 0; j < nsos1vars; ++j)
6135 {
6136 int* succ;
6137 int nsucc;
6138 int succnode;
6139 int i;
6140
6141 nsucc = SCIPdigraphGetNSuccessors(conflictgraph, j);
6142 succ = SCIPdigraphGetSuccessors(conflictgraph, j);
6143
6144 for (i = 0; i < nsucc; ++i)
6145 {
6146 succnode = succ[i];
6147
6148 if ( succnode > j && SCIPvarIsActive(SCIPnodeGetVarSOS1(conflictgraph, succnode)) )
6149 {
6150 if ( ! tcliqueAddEdge(conshdlrdata->tcliquegraph, j, succnode) )
6151 return SCIP_NOMEMORY;
6152 }
6153 }
6154 }
6155
6156 if ( ! tcliqueFlush(conshdlrdata->tcliquegraph) )
6157 return SCIP_NOMEMORY;
6158
6159 /* allocate clique data */
6160 SCIP_CALL( SCIPallocBlockMemory(scip, &conshdlrdata->tcliquedata) );
6161 tcliquedata = conshdlrdata->tcliquedata;
6162
6163 /* initialize clique data */
6164 tcliquedata->scip = scip;
6165 tcliquedata->sol = NULL;
6166 tcliquedata->conshdlr = conshdlr;
6167 tcliquedata->conflictgraph = conflictgraph;
6168 tcliquedata->scaleval = 1000.0;
6169 tcliquedata->ncuts = 0;
6170 tcliquedata->nboundcuts = conshdlrdata->nboundcuts;
6171 tcliquedata->strthenboundcuts = conshdlrdata->strthenboundcuts;
6172 tcliquedata->maxboundcuts = conshdlrdata->maxboundcutsroot;
6173
6174 return SCIP_OKAY;
6175 }
6176
6177
6178 /** update weights of tclique graph */
6179 static
updateWeightsTCliquegraph(SCIP * scip,SCIP_CONSHDLRDATA * conshdlrdata,TCLIQUE_DATA * tcliquedata,SCIP_DIGRAPH * conflictgraph,SCIP_SOL * sol,int nsos1vars)6180 SCIP_RETCODE updateWeightsTCliquegraph(
6181 SCIP* scip, /**< SCIP pointer */
6182 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
6183 TCLIQUE_DATA* tcliquedata, /**< tclique data */
6184 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
6185 SCIP_SOL* sol, /**< LP solution to be separated (or NULL) */
6186 int nsos1vars /**< number of SOS1 variables */
6187 )
6188 {
6189 SCIP_Real scaleval;
6190 int j;
6191
6192 scaleval = tcliquedata->scaleval;
6193
6194 for (j = 0; j < nsos1vars; ++j)
6195 {
6196 SCIP_Real solval;
6197 SCIP_Real bound;
6198 SCIP_VAR* var;
6199
6200 var = SCIPnodeGetVarSOS1(conflictgraph, j);
6201 solval = SCIPgetSolVal(scip, sol, var);
6202
6203 if ( SCIPisFeasPositive(scip, solval) )
6204 {
6205 if ( conshdlrdata->strthenboundcuts )
6206 bound = REALABS( nodeGetSolvalVarboundUbSOS1(scip, conflictgraph, sol, j) );
6207 else
6208 bound = REALABS( SCIPvarGetUbLocal(var) );
6209 }
6210 else if ( SCIPisFeasNegative(scip, solval) )
6211 {
6212 if ( conshdlrdata->strthenboundcuts )
6213 bound = REALABS( nodeGetSolvalVarboundLbSOS1(scip, conflictgraph, sol, j) );
6214 else
6215 bound = REALABS( SCIPvarGetLbLocal(var) );
6216 }
6217 else
6218 bound = 0.0;
6219
6220 solval = REALABS( solval );
6221
6222 if ( ! SCIPisFeasZero(scip, bound) && ! SCIPisInfinity(scip, bound) )
6223 {
6224 SCIP_Real nodeweight;
6225 nodeweight = REALABS( solval/bound ) * scaleval;/*lint !e414*/
6226 tcliqueChangeWeight(conshdlrdata->tcliquegraph, j, (int)nodeweight);
6227 }
6228 else
6229 {
6230 tcliqueChangeWeight(conshdlrdata->tcliquegraph, j, 0);
6231 }
6232 }
6233
6234 return SCIP_OKAY;
6235 }
6236
6237
6238 /** adds bound cut(s) to separation storage */
6239 static
addBoundCutSepa(SCIP * scip,TCLIQUE_DATA * tcliquedata,SCIP_ROW * rowlb,SCIP_ROW * rowub,SCIP_Bool * success,SCIP_Bool * cutoff)6240 SCIP_RETCODE addBoundCutSepa(
6241 SCIP* scip, /**< SCIP pointer */
6242 TCLIQUE_DATA* tcliquedata, /**< clique data */
6243 SCIP_ROW* rowlb, /**< row for lower bounds (or NULL) */
6244 SCIP_ROW* rowub, /**< row for upper bounds (or NULL) */
6245 SCIP_Bool* success, /**< pointer to store if bound cut was added */
6246 SCIP_Bool* cutoff /**< pointer to store if a cutoff occurred */
6247 )
6248 {
6249 assert( scip != NULL );
6250 assert( tcliquedata != NULL );
6251 assert( success != NULL);
6252 assert( cutoff != NULL );
6253
6254 *success = FALSE;
6255 *cutoff = FALSE;
6256
6257 /* add cut for lower bounds */
6258 if ( rowlb != NULL )
6259 {
6260 if ( ! SCIProwIsInLP(rowlb) && SCIPisCutEfficacious(scip, NULL, rowlb) )
6261 {
6262 SCIP_Bool infeasible;
6263
6264 SCIP_CALL( SCIPaddRow(scip, rowlb, FALSE, &infeasible) );
6265 if ( infeasible )
6266 *cutoff = TRUE;
6267 SCIPdebug( SCIP_CALL( SCIPprintRow(scip, rowlb, NULL) ) );
6268 ++tcliquedata->nboundcuts;
6269 ++tcliquedata->ncuts;
6270 *success = TRUE;
6271 }
6272 }
6273
6274 /* add cut for upper bounds */
6275 if ( rowub != NULL )
6276 {
6277 if ( ! SCIProwIsInLP(rowub) && SCIPisCutEfficacious(scip, NULL, rowub) )
6278 {
6279 SCIP_Bool infeasible;
6280
6281 SCIP_CALL( SCIPaddRow(scip, rowub, FALSE, &infeasible) );
6282 if ( infeasible )
6283 *cutoff = TRUE;
6284 SCIPdebug( SCIP_CALL( SCIPprintRow(scip, rowub, NULL) ) );
6285 ++tcliquedata->nboundcuts;
6286 ++tcliquedata->ncuts;
6287 *success = TRUE;
6288 }
6289 }
6290
6291 return SCIP_OKAY;
6292 }
6293
6294
6295 /** Generate bound constraint
6296 *
6297 * We generate the row corresponding to the following simple valid inequalities:
6298 * \f[
6299 * \frac{x_1}{u_1} + \ldots + \frac{x_n}{u_n} \leq 1\qquad\mbox{and}\qquad
6300 * \frac{x_1}{\ell_1} + \ldots + \frac{x_n}{\ell_1} \leq 1,
6301 * \f]
6302 * where \f$\ell_1, \ldots, \ell_n\f$ and \f$u_1, \ldots, u_n\f$ are the nonzero and finite lower and upper bounds of
6303 * the variables \f$x_1, \ldots, x_n\f$. If an upper bound < 0 or a lower bound > 0, the constraint itself is
6304 * redundant, so the cut is not applied (lower bounds > 0 and upper bounds < 0 are usually detected in presolving or
6305 * propagation). Infinite bounds and zero are skipped. Thus \f$\ell_1, \ldots, \ell_n\f$ are all negative, which
6306 * results in the \f$\leq\f$ inequality. In case of the presence of variable upper bounds, the bound inequality can
6307 * be further strengthened.
6308 *
6309 * Note that in fact, any mixture of nonzero finite lower and upper bounds would lead to a valid inequality as
6310 * above. However, usually either the lower or upper bound is nonzero. Thus, the above inequalities are the most
6311 * interesting.
6312 */
6313 static
generateBoundInequalityFromSOS1Nodes(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_DIGRAPH * conflictgraph,int * nodes,int nnodes,SCIP_Real rhs,SCIP_Bool local,SCIP_Bool global,SCIP_Bool strengthen,SCIP_Bool removable,const char * nameext,SCIP_ROW ** rowlb,SCIP_ROW ** rowub)6314 SCIP_RETCODE generateBoundInequalityFromSOS1Nodes(
6315 SCIP* scip, /**< SCIP pointer */
6316 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
6317 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
6318 int* nodes, /**< conflict graph nodes for bound constraint */
6319 int nnodes, /**< number of conflict graph nodes for bound constraint */
6320 SCIP_Real rhs, /**< right hand side of bound constraint */
6321 SCIP_Bool local, /**< in any case produce a local cut (even if local bounds of variables are valid globally) */
6322 SCIP_Bool global, /**< in any case produce a global cut */
6323 SCIP_Bool strengthen, /**< whether trying to strengthen bound constraint */
6324 SCIP_Bool removable, /**< should the inequality be removed from the LP due to aging or cleanup? */
6325 const char* nameext, /**< part of name of bound constraints */
6326 SCIP_ROW** rowlb, /**< output: row for lower bounds (or NULL if not needed) */
6327 SCIP_ROW** rowub /**< output: row for upper bounds (or NULL if not needed) */
6328 )
6329 {
6330 char name[SCIP_MAXSTRLEN];
6331 SCIP_VAR* lbboundvar = NULL;
6332 SCIP_VAR* ubboundvar = NULL;
6333 SCIP_Bool locallbs;
6334 SCIP_Bool localubs;
6335 SCIP_VAR** vars;
6336 SCIP_Real* vals;
6337
6338 assert( scip != NULL );
6339 assert( conshdlr != NULL );
6340 assert( conflictgraph != NULL );
6341 assert( ! local || ! global );
6342 assert( nodes != NULL );
6343
6344 /* allocate buffer array */
6345 SCIP_CALL( SCIPallocBufferArray(scip, &vars, nnodes+1) );
6346 SCIP_CALL( SCIPallocBufferArray(scip, &vals, nnodes+1) );
6347
6348 /* take care of upper bounds */
6349 if ( rowub != NULL )
6350 {
6351 SCIP_Bool useboundvar;
6352 int cnt = 0;
6353 int j;
6354
6355 /* Loop through all variables. We check whether all bound variables (if existent) are equal; if this is the
6356 * case then the bound constraint can be strengthened */
6357 localubs = local;
6358 useboundvar = strengthen;
6359 for (j = 0; j < nnodes; ++j)
6360 {
6361 SCIP_NODEDATA* nodedata;
6362 SCIP_VAR* var;
6363 SCIP_Real val;
6364
6365 nodedata = (SCIP_NODEDATA*)SCIPdigraphGetNodeData(conflictgraph, nodes[j]);
6366 assert( nodedata != NULL );
6367 var = nodedata->var;
6368 assert( var != NULL );
6369
6370 /* if variable is not involved in a variable bound constraint */
6371 if ( ! useboundvar || nodedata->ubboundvar == NULL )
6372 {
6373 useboundvar = FALSE;
6374 if ( localubs )
6375 {
6376 assert( ! global );
6377 val = SCIPvarGetUbLocal(var);
6378 }
6379 else
6380 {
6381 val = SCIPvarGetUbGlobal(var);
6382 if ( ! global && ! SCIPisFeasEQ(scip, val, SCIPvarGetUbLocal(var)) )
6383 {
6384 localubs = TRUE;
6385 val = SCIPvarGetUbLocal(var);
6386 }
6387 }
6388 }
6389 else
6390 {
6391 /* in this case the cut is always valid globally */
6392
6393 /* if we have a bound variable for the first time */
6394 if ( ubboundvar == NULL )
6395 {
6396 ubboundvar = nodedata->ubboundvar;
6397 val = nodedata->ubboundcoef;
6398 }
6399 /* else if the bound variable equals the stored bound variable */
6400 else if ( ubboundvar == nodedata->ubboundvar )
6401 val = nodedata->ubboundcoef;
6402 else /* else use bounds on the variables */
6403 {
6404 useboundvar = FALSE;
6405
6406 /* restart 'for'-loop */
6407 j = -1; /*lint !e850*/
6408 cnt = 0;
6409 continue;
6410 }
6411 }
6412
6413 /* should not apply the cut if a variable is fixed to be negative -> constraint is redundant */
6414 if ( SCIPisNegative(scip, val) )
6415 break;
6416
6417 /* store variable if relevant for bound inequality */
6418 if ( ! SCIPisInfinity(scip, val) && ! SCIPisZero(scip, val) )
6419 {
6420 vars[cnt] = var;
6421
6422 /* if only two nodes then we scale the cut differently */
6423 if ( nnodes == 2 )
6424 vals[cnt++] = val;
6425 else
6426 vals[cnt++] = 1.0/val;
6427 }
6428 }
6429
6430 /* if cut is meaningful */
6431 if ( j == nnodes && cnt >= 2 )/*lint !e850*/
6432 {
6433 /* if only two nodes then we scale the cut differently */
6434 if ( nnodes == 2 )
6435 {
6436 SCIP_Real save;
6437
6438 save = vals[0];
6439 vals[0] = vals[1];
6440 vals[1] = save;
6441 rhs = rhs * vals[0] * vals[1];
6442 assert( (! useboundvar && cnt == 2 ) || (useboundvar && cnt == 3 ) );
6443 }
6444
6445 if ( useboundvar )
6446 {
6447 /* add bound variable to array */
6448 vars[cnt] = ubboundvar;
6449 vals[cnt++] = -rhs;
6450 assert(ubboundvar != NULL );
6451
6452 /* create upper bound inequality if at least two of the bounds are finite and nonzero */
6453 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "sosub#%s", nameext);
6454 SCIP_CALL( SCIPcreateEmptyRowConshdlr(scip, rowub, conshdlr, name, -SCIPinfinity(scip), 0.0, localubs, FALSE, removable) );
6455 SCIP_CALL( SCIPaddVarsToRow(scip, *rowub, cnt, vars, vals) );
6456 SCIPdebug( SCIP_CALL( SCIPprintRow(scip, *rowub, NULL) ) );
6457 }
6458 else
6459 {
6460 /* create upper bound inequality if at least two of the bounds are finite and nonzero */
6461 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "sosub#%s", nameext);
6462 SCIP_CALL( SCIPcreateEmptyRowConshdlr(scip, rowub, conshdlr, name, -SCIPinfinity(scip), rhs, localubs, FALSE, removable) );
6463 SCIP_CALL( SCIPaddVarsToRow(scip, *rowub, cnt, vars, vals) );
6464 SCIPdebug( SCIP_CALL( SCIPprintRow(scip, *rowub, NULL) ) );
6465 }
6466 }
6467 }
6468
6469 /* take care of lower bounds */
6470 if ( rowlb != NULL )
6471 {
6472 SCIP_Bool useboundvar;
6473 int cnt = 0;
6474 int j;
6475
6476 /* loop through all variables. We check whether all bound variables (if existent) are equal; if this is the
6477 * case then the bound constraint can be strengthened */
6478 locallbs = local;
6479 useboundvar = strengthen;
6480 for (j = 0; j < nnodes; ++j)
6481 {
6482 SCIP_NODEDATA* nodedata;
6483 SCIP_VAR* var;
6484 SCIP_Real val;
6485
6486 nodedata = (SCIP_NODEDATA*)SCIPdigraphGetNodeData(conflictgraph, nodes[j]);
6487 assert( nodedata != NULL );
6488 var = nodedata->var;
6489 assert( var != NULL );
6490
6491 /* if variable is not involved in a variable bound constraint */
6492 if ( ! useboundvar || nodedata->lbboundvar == NULL )
6493 {
6494 useboundvar = FALSE;
6495 if ( locallbs )
6496 {
6497 assert( ! global );
6498 val = SCIPvarGetLbLocal(var);
6499 }
6500 else
6501 {
6502 val = SCIPvarGetLbGlobal(var);
6503 if ( ! global && ! SCIPisFeasEQ(scip, val, SCIPvarGetLbLocal(var)) )
6504 {
6505 locallbs = TRUE;
6506 val = SCIPvarGetLbLocal(var);
6507 }
6508 }
6509 }
6510 else
6511 {
6512 /* in this case the cut is always valid globally */
6513
6514 /* if we have a bound variable for the first time */
6515 if ( lbboundvar == NULL )
6516 {
6517 lbboundvar = nodedata->lbboundvar;
6518 val = nodedata->lbboundcoef;
6519 }
6520 /* else if the bound variable equals the stored bound variable */
6521 else if ( SCIPvarCompare(lbboundvar, nodedata->lbboundvar) == 0 )
6522 {
6523 val = nodedata->lbboundcoef;
6524 }
6525 else /* else use bounds on the variables */
6526 {
6527 useboundvar = FALSE;
6528
6529 /* restart 'for'-loop */
6530 j = -1; /*lint !e850*/
6531 cnt = 0;
6532 continue;
6533 }
6534 }
6535
6536 /* should not apply the cut if a variable is fixed to be positive -> constraint is redundant */
6537 if ( SCIPisPositive(scip, val) )
6538 break;
6539
6540 /* store variable if relevant for bound inequality */
6541 if ( ! SCIPisInfinity(scip, -val) && ! SCIPisZero(scip, val) )
6542 {
6543 vars[cnt] = var;
6544
6545 /* if only two nodes then we scale the cut differently */
6546 if ( nnodes == 2 )
6547 vals[cnt++] = val;
6548 else
6549 vals[cnt++] = 1.0/val;
6550 }
6551 }
6552
6553 /* if cut is meaningful */
6554 if ( j == nnodes && cnt >= 2 )/*lint !e850*/
6555 {
6556 /* if only two nodes then we scale the cut differently */
6557 if ( nnodes == 2 )
6558 {
6559 SCIP_Real save;
6560
6561 save = vals[0];
6562 vals[0] = vals[1];
6563 vals[1] = save;
6564 rhs = rhs * vals[0] * vals[1];
6565 assert( (! useboundvar && cnt == 2 ) || (useboundvar && cnt == 3 ) );
6566 }
6567
6568 if ( useboundvar )
6569 {
6570 /* add bound variable to array */
6571 vars[cnt] = lbboundvar;
6572 vals[cnt++] = -rhs;
6573 assert(lbboundvar != NULL );
6574
6575 /* create upper bound inequality if at least two of the bounds are finite and nonzero */
6576 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "soslb#%s", nameext);
6577 SCIP_CALL( SCIPcreateEmptyRowConshdlr(scip, rowlb, conshdlr, name, -SCIPinfinity(scip), 0.0, locallbs, FALSE, TRUE) );
6578 SCIP_CALL( SCIPaddVarsToRow(scip, *rowlb, cnt, vars, vals) );
6579 SCIPdebug( SCIP_CALL( SCIPprintRow(scip, *rowlb, NULL) ) );
6580 }
6581 else
6582 {
6583 /* create upper bound inequality if at least two of the bounds are finite and nonzero */
6584 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "soslb#%s", nameext);
6585 SCIP_CALL( SCIPcreateEmptyRowConshdlr(scip, rowlb, conshdlr, name, -SCIPinfinity(scip), rhs, locallbs, FALSE, TRUE) );
6586 SCIP_CALL( SCIPaddVarsToRow(scip, *rowlb, cnt, vars, vals) );
6587 SCIPdebug( SCIP_CALL( SCIPprintRow(scip, *rowlb, NULL) ) );
6588 }
6589 }
6590 }
6591
6592 /* free buffer array */
6593 SCIPfreeBufferArray(scip, &vals);
6594 SCIPfreeBufferArray(scip, &vars);
6595
6596 return SCIP_OKAY;
6597 }
6598
6599
6600 /** generates bound cuts using a clique found by algorithm for maximum weight clique
6601 * and decides whether to stop generating cliques with the algorithm for maximum weight clique
6602 */
6603 static
TCLIQUE_NEWSOL(tcliqueNewsolClique)6604 TCLIQUE_NEWSOL(tcliqueNewsolClique)
6605 {
6606 TCLIQUE_WEIGHT minweightinc;
6607
6608 assert( acceptsol != NULL );
6609 assert( stopsolving != NULL );
6610 assert( tcliquedata != NULL );
6611
6612 /* we don't accept the solution as new incumbent, because we want to find many violated clique inequalities */
6613 *acceptsol = FALSE;
6614 *stopsolving = FALSE;
6615
6616 /* slightly increase the minimal weight for additional cliques */
6617 minweightinc = (cliqueweight - *minweight)/10;
6618 minweightinc = MAX(minweightinc, 1);
6619 *minweight += minweightinc;
6620
6621 /* adds cut if weight of the clique is greater than 1 */
6622 if( cliqueweight > tcliquedata->scaleval )
6623 {
6624 SCIP* scip;
6625 SCIP_SOL* sol;
6626 SCIP_Real unscaledweight;
6627 SCIP_Real solval;
6628 SCIP_Real bound;
6629 SCIP_VAR* var;
6630 int node;
6631 int i;
6632
6633 scip = tcliquedata->scip;
6634 sol = tcliquedata->sol;
6635 assert( scip != NULL );
6636
6637 /* calculate the weight of the clique in unscaled fractional variable space */
6638 unscaledweight = 0.0;
6639 for( i = 0; i < ncliquenodes; i++ )
6640 {
6641 node = cliquenodes[i];
6642 var = SCIPnodeGetVarSOS1(tcliquedata->conflictgraph, node);
6643 solval = SCIPgetSolVal(scip, sol, var);
6644
6645 if ( SCIPisFeasPositive(scip, solval) )
6646 {
6647 if ( tcliquedata->strthenboundcuts )
6648 bound = REALABS( nodeGetSolvalVarboundUbSOS1(scip, tcliquedata->conflictgraph, sol, node) );
6649 else
6650 bound = REALABS( SCIPvarGetUbLocal(var) );
6651 }
6652 else if ( SCIPisFeasNegative(scip, solval) )
6653 {
6654 if ( tcliquedata->strthenboundcuts )
6655 bound = REALABS( nodeGetSolvalVarboundLbSOS1(scip, tcliquedata->conflictgraph, sol, node) );
6656 else
6657 bound = REALABS( SCIPvarGetLbLocal(var) );
6658 }
6659 else
6660 bound = 0.0;
6661
6662 solval = REALABS( solval );
6663
6664 if ( ! SCIPisFeasZero(scip, bound) && ! SCIPisInfinity(scip, bound) )
6665 unscaledweight += REALABS( solval/bound );/*lint !e414*/
6666 }
6667
6668 if ( SCIPisEfficacious(scip, unscaledweight - 1.0) )
6669 {
6670 char nameext[SCIP_MAXSTRLEN];
6671 SCIP_ROW* rowlb = NULL;
6672 SCIP_ROW* rowub = NULL;
6673 SCIP_Bool success;
6674 SCIP_Bool cutoff;
6675
6676 /* generate bound inequalities for lower and upper bound case
6677 * NOTE: tests have shown that non-removable rows give the best results */
6678 (void) SCIPsnprintf(nameext, SCIP_MAXSTRLEN, "%d", tcliquedata->nboundcuts);
6679 if ( generateBoundInequalityFromSOS1Nodes(scip, tcliquedata->conshdlr, tcliquedata->conflictgraph,
6680 cliquenodes, ncliquenodes, 1.0, FALSE, FALSE, tcliquedata->strthenboundcuts, FALSE, nameext, &rowlb, &rowub) != SCIP_OKAY )
6681 {
6682 SCIPerrorMessage("Unexpected error in bound cut creation.\n");
6683 SCIPABORT();
6684 return; /*lint !e527*/
6685 }
6686
6687 /* add bound cut(s) to separation storage if existent */
6688 if ( addBoundCutSepa(scip, tcliquedata, rowlb, rowub, &success, &cutoff) != SCIP_OKAY )
6689 {
6690 SCIPerrorMessage("Unexpected error in bound cut creation.\n");
6691 SCIPABORT();
6692 return; /*lint !e527*/
6693 }
6694
6695 if ( rowlb != NULL )
6696 {
6697 if ( SCIPreleaseRow(scip, &rowlb) != SCIP_OKAY )
6698 {
6699 SCIPerrorMessage("Cannot release row,\n");
6700 SCIPABORT();
6701 return; /*lint !e527*/
6702 }
6703 }
6704 if ( rowub != NULL )
6705 {
6706 if ( SCIPreleaseRow(scip, &rowub) != SCIP_OKAY )
6707 {
6708 SCIPerrorMessage("Cannot release row,\n");
6709 SCIPABORT();
6710 return; /*lint !e527*/
6711 }
6712 }
6713
6714 /* if at least one cut has been added */
6715 if ( success )
6716 {
6717 SCIPdebugMsg(scip, " -> found bound cut corresponding to clique (act=%g)\n", unscaledweight);
6718
6719 /* if we found more than half the cuts we are allowed to generate, we accept the clique as new incumbent,
6720 * such that only more violated cuts are generated afterwards
6721 */
6722 if( tcliquedata->maxboundcuts >= 0 )
6723 {
6724 if ( tcliquedata->ncuts > tcliquedata->maxboundcuts/2 )
6725 *acceptsol = TRUE;
6726 if ( tcliquedata->ncuts >= tcliquedata->maxboundcuts )
6727 *stopsolving = TRUE;
6728 }
6729 }
6730 else
6731 *stopsolving = TRUE;
6732 } /*lint !e438*/
6733 }
6734 }
6735
6736
6737 /** separate bound inequalities from conflict graph */
6738 static
sepaBoundInequalitiesFromGraph(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_SOL * sol,int maxboundcuts,int * ngen,SCIP_Bool * cutoff)6739 SCIP_RETCODE sepaBoundInequalitiesFromGraph(
6740 SCIP* scip, /**< SCIP pointer */
6741 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
6742 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
6743 SCIP_SOL* sol, /**< LP solution to be separated (or NULL) */
6744 int maxboundcuts, /**< maximal number of bound cuts separated per separation round (-1: no limit) */
6745 int* ngen, /**< pointer to store number of cuts generated */
6746 SCIP_Bool* cutoff /**< pointer whether a cutoff occurred */
6747 )
6748 {
6749 SCIP_DIGRAPH* conflictgraph;
6750 TCLIQUE_DATA* tcliquedata;
6751 TCLIQUE_WEIGHT cliqueweight;
6752 TCLIQUE_STATUS tcliquestatus;
6753 int nsos1vars;
6754
6755 SCIP_Real scaleval = 1000.0; /* factor for scaling weights */
6756 int maxtreenodes = 10000; /* maximal number of nodes of b&b tree */
6757 int maxzeroextensions = 1000; /* maximal number of zero-valued variables extending the clique (-1: no limit) */
6758 int backtrackfreq = 1000; /* frequency for premature backtracking up to tree level 1 (0: no backtracking) */
6759 int ntreenodes;
6760 int* cliquenodes;
6761 int ncliquenodes;
6762
6763 assert( scip != NULL );
6764 assert( conshdlr != NULL );
6765 assert( conshdlrdata != NULL );
6766 assert( ngen != NULL );
6767
6768 /* get conflict graph */
6769 conflictgraph = SCIPgetConflictgraphSOS1(conshdlr);
6770 assert( conflictgraph != NULL );
6771
6772 /* get number of SOS1 variables */
6773 nsos1vars = SCIPgetNSOS1Vars(conshdlr);
6774
6775 /* initialize data of tclique graph*/
6776 tcliquedata = conshdlrdata->tcliquedata;
6777 tcliquedata->scaleval = scaleval;
6778 tcliquedata->maxboundcuts = maxboundcuts;
6779 tcliquedata->sol = sol;
6780 tcliquedata->ncuts = 0;
6781 tcliquedata->cutoff = FALSE;
6782
6783 /* update the weights of the tclique graph */
6784 SCIP_CALL( updateWeightsTCliquegraph(scip, conshdlrdata, tcliquedata, conflictgraph, sol, nsos1vars) );
6785
6786 /* allocate buffer array */
6787 SCIP_CALL( SCIPallocBufferArray(scip, &cliquenodes, nsos1vars) );
6788
6789 /* start algorithm to find maximum weight cliques and use them to generate bound cuts */
6790 tcliqueMaxClique(tcliqueGetNNodes, tcliqueGetWeights, tcliqueIsEdge, tcliqueSelectAdjnodes,
6791 conshdlrdata->tcliquegraph, tcliqueNewsolClique, tcliquedata,
6792 cliquenodes, &ncliquenodes, &cliqueweight, (int)scaleval-1, (int)scaleval+1,
6793 maxtreenodes, backtrackfreq, maxzeroextensions, -1, &ntreenodes, &tcliquestatus);
6794
6795 /* free buffer array */
6796 SCIPfreeBufferArray(scip, &cliquenodes);
6797
6798 /* get number of cuts of current separation round */
6799 *ngen = tcliquedata->ncuts;
6800
6801 /* store whether a cutoff occurred */
6802 *cutoff = tcliquedata->cutoff;
6803
6804 /* update number of bound cuts in separator data */
6805 conshdlrdata->nboundcuts = tcliquedata->nboundcuts;
6806
6807 return SCIP_OKAY;
6808 }
6809
6810
6811 /** Generate a bound constraint from the variables of an SOS1 constraint (see generateBoundInequalityFromSOS1Nodes() for more information) */
6812 static
generateBoundInequalityFromSOS1Cons(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS * cons,SCIP_Bool local,SCIP_Bool global,SCIP_Bool strengthen,SCIP_Bool removable,SCIP_ROW ** rowlb,SCIP_ROW ** rowub)6813 SCIP_RETCODE generateBoundInequalityFromSOS1Cons(
6814 SCIP* scip, /**< SCIP pointer */
6815 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
6816 SCIP_CONS* cons, /**< SOS1 constraint */
6817 SCIP_Bool local, /**< in any case produce a local cut (even if local bounds of variables are valid globally) */
6818 SCIP_Bool global, /**< in any case produce a global cut */
6819 SCIP_Bool strengthen, /**< whether trying to strengthen bound constraint */
6820 SCIP_Bool removable, /**< should the inequality be removed from the LP due to aging or cleanup? */
6821 SCIP_ROW** rowlb, /**< output: row for lower bounds (or NULL if not needed) */
6822 SCIP_ROW** rowub /**< output: row for upper bounds (or NULL if not needed) */
6823 )
6824 {
6825 SCIP_CONSHDLRDATA* conshdlrdata;
6826 SCIP_CONSDATA* consdata;
6827 int* nodes;
6828 int nvars;
6829 int cnt = 0;
6830 int j;
6831
6832 assert( scip != NULL );
6833 assert( conshdlr != NULL );
6834 assert( cons != NULL );
6835
6836 /* get constraint data */
6837 consdata = SCIPconsGetData(cons);
6838 assert( consdata != NULL );
6839 assert( consdata->vars != NULL );
6840 nvars = consdata->nvars;
6841
6842 /* get constraint handler data */
6843 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6844 assert( conshdlrdata != NULL );
6845 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
6846
6847 /* allocate buffer array */
6848 SCIP_CALL( SCIPallocBufferArray(scip, &nodes, nvars) );
6849
6850 /* get nodes in the conflict graph */
6851 for (j = 0; j < nvars; ++j)
6852 {
6853 if ( SCIPisFeasNegative(scip, SCIPvarGetLbLocal(consdata->vars[j])) || SCIPisFeasPositive(scip, SCIPvarGetUbLocal(consdata->vars[j])) )
6854 {
6855 assert( varGetNodeSOS1(conshdlrdata, consdata->vars[j]) >= 0 );
6856 nodes[cnt++] = varGetNodeSOS1(conshdlrdata, consdata->vars[j]);
6857 }
6858 }
6859
6860 /* generate bound constraint from conflict graph nodes */
6861 if ( cnt > 0 )
6862 {
6863 SCIP_CALL( generateBoundInequalityFromSOS1Nodes(scip, conshdlr, conshdlrdata->conflictgraph, nodes, cnt, 1.0, local, global,
6864 strengthen, removable, SCIPconsGetName(cons), rowlb, rowub) );
6865 }
6866
6867 /* free buffer array */
6868 SCIPfreeBufferArray(scip, &nodes);
6869
6870 return SCIP_OKAY;
6871 }
6872
6873
6874 /** initialize or separate bound inequalities from SOS1 constraints */
6875 static
initsepaBoundInequalityFromSOS1Cons(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_CONS ** conss,int nconss,SCIP_SOL * sol,SCIP_Bool solvedinitlp,int maxboundcuts,int * ngen,SCIP_Bool * cutoff)6876 SCIP_RETCODE initsepaBoundInequalityFromSOS1Cons(
6877 SCIP* scip, /**< SCIP pointer */
6878 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
6879 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
6880 SCIP_CONS** conss, /**< SOS1 constraints */
6881 int nconss, /**< number of SOS1 constraints */
6882 SCIP_SOL* sol, /**< LP solution to be separated (or NULL) */
6883 SCIP_Bool solvedinitlp, /**< TRUE if initial LP relaxation at a node is solved */
6884 int maxboundcuts, /**< maximal number of bound cuts separated per separation round (-1: no limit) */
6885 int* ngen, /**< pointer to store number of cuts generated (or NULL) */
6886 SCIP_Bool* cutoff /**< pointer to store whether a cutoff occurred */
6887 )
6888 {
6889 int cnt = 0;
6890 int c;
6891
6892 assert( scip != NULL );
6893 assert( conshdlrdata != NULL );
6894 assert( conss != NULL );
6895
6896 *cutoff = FALSE;
6897
6898 for (c = 0; c < nconss; ++c)
6899 {
6900 SCIP_CONSDATA* consdata;
6901 SCIP_ROW* rowub = NULL;
6902 SCIP_ROW* rowlb = NULL;
6903 SCIP_Bool release = FALSE;
6904
6905 assert( conss != NULL );
6906 assert( conss[c] != NULL );
6907 consdata = SCIPconsGetData(conss[c]);
6908 assert( consdata != NULL );
6909
6910 if ( solvedinitlp )
6911 {
6912 SCIPdebugMsg(scip, "Separating inequalities for SOS1 constraint <%s>.\n", SCIPconsGetName(conss[c]) );
6913 }
6914 else
6915 {
6916 SCIPdebugMsg(scip, "Checking for initial rows for SOS1 constraint <%s>.\n", SCIPconsGetName(conss[c]) );
6917 }
6918
6919 /* in case that the SOS1 constraint is local, we always generate new rows - the former rows might be invalid;
6920 * otherwise if the SOS1 constraint is global, we only generate rows if not yet done */
6921 if ( consdata->local )
6922 {
6923 SCIP_CALL( generateBoundInequalityFromSOS1Cons(scip, conshdlr, conss[c], TRUE, FALSE, TRUE, FALSE, &rowlb, &rowub) );
6924 release = TRUE;
6925 }
6926 else
6927 {
6928 if ( consdata->rowub == NULL || consdata->rowlb == NULL )
6929 {
6930 SCIP_CALL( generateBoundInequalityFromSOS1Cons(scip, conshdlr, conss[c], FALSE, TRUE, TRUE, FALSE,
6931 (consdata->rowlb == NULL) ? &consdata->rowlb : NULL,
6932 (consdata->rowub == NULL) ? &consdata->rowub : NULL) ); /*lint !e826*/
6933 }
6934 rowub = consdata->rowub;
6935 rowlb = consdata->rowlb;
6936 }
6937
6938 /* put corresponding rows into LP */
6939 if ( rowub != NULL && ! SCIProwIsInLP(rowub) && ( solvedinitlp || SCIPisCutEfficacious(scip, sol, rowub) ) )
6940 {
6941 SCIP_CALL( SCIPaddRow(scip, rowub, FALSE, cutoff) );
6942 SCIPdebug( SCIP_CALL( SCIPprintRow(scip, rowub, NULL) ) );
6943
6944 if ( solvedinitlp )
6945 {
6946 SCIP_CALL( SCIPresetConsAge(scip, conss[c]) );
6947 }
6948 ++cnt;
6949 }
6950
6951 if ( ! (*cutoff) && rowlb != NULL && ! SCIProwIsInLP(rowlb) && ( solvedinitlp || SCIPisCutEfficacious(scip, sol, rowlb) ) )
6952 {
6953 SCIP_CALL( SCIPaddRow(scip, rowlb, FALSE, cutoff) );
6954 SCIPdebug( SCIP_CALL( SCIPprintRow(scip, rowlb, NULL) ) );
6955
6956 if ( solvedinitlp )
6957 {
6958 SCIP_CALL( SCIPresetConsAge(scip, conss[c]) );
6959 }
6960 ++cnt;
6961 }
6962
6963 /* release rows if they are local */
6964 if ( release )
6965 {
6966 if ( rowlb != NULL )
6967 {
6968 SCIP_CALL( SCIPreleaseRow(scip, &rowlb) );
6969 }
6970 if ( rowub != NULL )
6971 {
6972 SCIP_CALL( SCIPreleaseRow(scip, &rowub) );
6973 }
6974 }
6975
6976 if ( *cutoff || ( maxboundcuts >= 0 && cnt >= maxboundcuts ) )
6977 break;
6978 }
6979
6980 /* store number of generated cuts */
6981 if ( ngen != NULL )
6982 *ngen = cnt;
6983
6984 return SCIP_OKAY;
6985 }
6986
6987
6988 /** separate implied bound cuts */
6989 static
sepaImplBoundCutsSOS1(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_SOL * sol,int maxcuts,int * ngen,SCIP_Bool * cutoff)6990 SCIP_RETCODE sepaImplBoundCutsSOS1(
6991 SCIP* scip, /**< SCIP pointer */
6992 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
6993 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
6994 SCIP_SOL* sol, /**< LP solution to be separated (or NULL) */
6995 int maxcuts, /**< maximal number of implied bound cuts separated per separation round (-1: no limit) */
6996 int* ngen, /**< pointer to store number of cuts generated */
6997 SCIP_Bool* cutoff /**< pointer whether a cutoff occurred */
6998 )
6999 {
7000 SCIP_DIGRAPH* implgraph;
7001 SCIP_Bool genbreak;
7002 int nimplnodes;
7003 int i;
7004
7005 assert( scip != NULL);
7006 assert( conshdlrdata != NULL);
7007 assert( conshdlr != NULL);
7008 assert( ngen != NULL);
7009 assert( cutoff != NULL);
7010
7011 *cutoff = FALSE;
7012 *ngen = 0;
7013
7014 /* return if conflict graph is not available */
7015 if ( conshdlrdata->conflictgraph == NULL )
7016 return SCIP_OKAY;
7017
7018 /* get implication graph */
7019 implgraph = conshdlrdata->implgraph;
7020
7021 /* create implication graph if not done already */
7022 if ( implgraph == NULL )
7023 {
7024 int nchbds;
7025
7026 if ( SCIPgetDepth(scip) == 0 )
7027 {
7028 SCIP_Bool success;
7029 SCIP_CALL( initImplGraphSOS1(scip, conshdlrdata, conshdlrdata->conflictgraph, conshdlrdata->nsos1vars, conshdlrdata->maxtightenbds, &nchbds, cutoff, &success) );
7030 if ( *cutoff || ! success )
7031 return SCIP_OKAY;
7032 implgraph = conshdlrdata->implgraph;
7033 }
7034 else
7035 {
7036 return SCIP_OKAY;
7037 }
7038 }
7039 nimplnodes = conshdlrdata->nimplnodes;
7040 assert( implgraph != NULL );
7041 assert( nimplnodes > 0);
7042
7043 /* exit if implication graph has no arcs between its nodes */
7044 if ( SCIPdigraphGetNArcs(implgraph) < 1 )
7045 return SCIP_OKAY;
7046
7047 /* loop through all nodes of the implication graph */
7048 genbreak = FALSE;
7049 for (i = 0; i < nimplnodes && ! genbreak; ++i)
7050 {
7051 SCIP_SUCCDATA** succdatas;
7052 SCIP_NODEDATA* nodedata;
7053 SCIP_Real solval;
7054 SCIP_VAR* var;
7055 int* succ;
7056 int nsucc;
7057 int s;
7058
7059 succdatas = (SCIP_SUCCDATA**) SCIPdigraphGetSuccessorsData(implgraph, i);
7060 nodedata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(implgraph, i);
7061 assert( nodedata != NULL );
7062 var = nodedata->var;
7063 assert( var != NULL );
7064 solval = SCIPgetSolVal(scip, sol, var);
7065
7066 if ( succdatas != NULL && ! SCIPisFeasZero(scip, solval) )
7067 {
7068 succ = SCIPdigraphGetSuccessors(implgraph, i);
7069 nsucc = SCIPdigraphGetNSuccessors(implgraph, i);
7070
7071 for (s = 0; s < nsucc && ! genbreak; ++s)
7072 {
7073 SCIP_SUCCDATA* succdata;
7074 SCIP_VAR* succvar;
7075 SCIP_ROW* cut = NULL;
7076 SCIP_Bool bound1lower;
7077 SCIP_Bool bound2lower;
7078 SCIP_Real solvalsucc;
7079 SCIP_Real bound1;
7080 SCIP_Real bound2;
7081 SCIP_Real lhsrhs;
7082 SCIP_Real impl;
7083 int k;
7084
7085 nodedata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(implgraph, succ[s]);
7086 succdata = succdatas[s];
7087 assert( nodedata != NULL && succdata != NULL && nodedata->var != NULL );
7088 succvar = nodedata->var;
7089 solvalsucc = SCIPgetSolVal(scip, sol, succvar);
7090
7091 /* determine coefficients for bound inequality */
7092 assert( ! SCIPisFeasZero(scip, solval) );
7093 if ( SCIPisFeasNegative(scip, solval) )
7094 {
7095 bound1lower = TRUE;
7096 bound1 = SCIPvarGetLbGlobal(var);
7097 }
7098 else
7099 {
7100 bound1lower = FALSE;
7101 bound1 = SCIPvarGetUbGlobal(var);
7102 }
7103
7104 /* handle lower bound upper bound implications */
7105 for (k = 0; k < 2; ++k)
7106 {
7107 if ( k == 0 )
7108 {
7109 SCIP_Real lbsucc;
7110 lbsucc = SCIPvarGetLbGlobal(succvar);
7111 if ( SCIPisFeasLT(scip, lbsucc, succdata->lbimpl) )
7112 {
7113 impl = succdata->lbimpl;
7114 bound2 = lbsucc;
7115 }
7116 else
7117 continue;
7118 }
7119 else
7120 {
7121 SCIP_Real ubsucc;
7122 ubsucc = SCIPvarGetUbGlobal(succvar);
7123 if ( SCIPisFeasGT(scip, ubsucc, succdata->ubimpl) )
7124 {
7125 impl = succdata->ubimpl;
7126 bound2 = ubsucc;
7127 }
7128 else
7129 continue;
7130 }
7131
7132 if ( SCIPisInfinity(scip, REALABS(bound1)) || SCIPisInfinity(scip, REALABS(bound2)) )
7133 continue;
7134 assert( ! SCIPisInfinity(scip, REALABS(impl)) );
7135
7136 if ( SCIPisFeasNegative(scip, bound2-impl) )
7137 bound2lower = TRUE;
7138 else
7139 bound2lower = FALSE;
7140
7141 /* determine left/right hand side of bound inequality */
7142 lhsrhs = bound1 * bound2;
7143
7144 /* create cut */
7145 if ( bound1lower == bound2lower )
7146 {
7147 if ( SCIPisFeasGT(scip, solval * (bound2-impl) + solvalsucc * bound1, lhsrhs) )
7148 {
7149 SCIP_CALL( SCIPcreateEmptyRowConshdlr(scip, &cut, conshdlr, "", -SCIPinfinity(scip), lhsrhs, FALSE, FALSE, TRUE) );
7150 }
7151 else
7152 continue;
7153 }
7154 else
7155 {
7156 if ( SCIPisFeasLT(scip, solval * (bound2-impl) + solvalsucc * bound1, lhsrhs) )
7157 {
7158 SCIP_CALL( SCIPcreateEmptyRowConshdlr(scip, &cut, conshdlr, "", lhsrhs, SCIPinfinity(scip), FALSE, FALSE, TRUE) );
7159 }
7160 else
7161 continue;
7162 }
7163
7164 /* add coefficients of variables */
7165 SCIP_CALL( SCIPcacheRowExtensions(scip, cut) );
7166 SCIP_CALL( SCIPaddVarToRow(scip, cut, var, bound2-impl) );
7167 SCIP_CALL( SCIPaddVarToRow(scip, cut, succvar, bound1) );
7168 SCIP_CALL( SCIPflushRowExtensions(scip, cut) );
7169
7170 /* add cut if useful */
7171 if ( ! SCIProwIsInLP(cut) && SCIPisCutEfficacious(scip, NULL, cut) )
7172 {
7173 SCIP_Bool infeasible;
7174 SCIP_CALL( SCIPaddRow(scip, cut, FALSE, &infeasible) );
7175 if ( infeasible )
7176 {
7177 genbreak = TRUE;
7178 *cutoff = TRUE;
7179 break;
7180 }
7181 SCIPdebug( SCIP_CALL( SCIPprintRow(scip, cut, NULL) ) );
7182 #ifdef SCIP_DEBUG
7183 if ( k == 0 )
7184 {
7185 SCIPdebugMsg(scip, "added cut for implication %s != 0 -> %s >= %f \n", SCIPvarGetName(var), SCIPvarGetName(succvar), succdata->lbimpl);
7186 }
7187 else
7188 {
7189 SCIPdebugMsg(scip, "added cut for implication %s != 0 -> %s <= %f \n", SCIPvarGetName(var), SCIPvarGetName(succvar), succdata->ubimpl);
7190 }
7191 #endif
7192
7193 ++(*ngen);
7194 }
7195
7196 if ( maxcuts >= 0 && *ngen > maxcuts )
7197 {
7198 genbreak = TRUE;
7199 break;
7200 }
7201 }
7202
7203 if ( cut != NULL )
7204 SCIP_CALL( SCIPreleaseRow(scip, &cut) );
7205 }
7206 }
7207 }
7208
7209 return SCIP_OKAY;
7210 }
7211
7212
7213 /** separates SOS1 constraints for arbitrary solutions */
7214 static
separateSOS1(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_SOL * sol,int nconss,SCIP_CONS ** conss,SCIP_RESULT * result)7215 SCIP_RETCODE separateSOS1(
7216 SCIP* scip, /**< SCIP pointer */
7217 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
7218 SCIP_SOL* sol, /**< solution to be separated (or NULL) */
7219 int nconss, /**< number of constraints */
7220 SCIP_CONS** conss, /**< SOS1 constraints */
7221 SCIP_RESULT* result /**< result */
7222 )
7223 {
7224 SCIP_CONSHDLRDATA* conshdlrdata;
7225 int depth;
7226
7227 assert( scip != NULL );
7228 assert( conshdlr != NULL );
7229 assert( conss != NULL );
7230 assert( result != NULL );
7231
7232 *result = SCIP_DIDNOTRUN;
7233
7234 if ( nconss == 0 )
7235 return SCIP_OKAY;
7236
7237 /* only separate cuts if we are not close to terminating */
7238 if( SCIPisStopped(scip) )
7239 return SCIP_OKAY;
7240
7241 *result = SCIP_DIDNOTFIND;
7242
7243 /* get constraint handler data */
7244 conshdlrdata = SCIPconshdlrGetData(conshdlr);
7245 assert( conshdlrdata != NULL );
7246
7247 /* get node depth */
7248 depth = SCIPgetDepth(scip);
7249
7250 /* separate bound (clique) inequalities */
7251 if ( conshdlrdata->boundcutsfreq >= 0 &&
7252 ( (conshdlrdata->boundcutsfreq == 0 && depth == 0) || (conshdlrdata->boundcutsfreq > 0 && depth % conshdlrdata->boundcutsfreq == 0)) )
7253 {
7254 int maxboundcuts;
7255 int ngen = 0;
7256
7257 /* determine maximal number of cuts*/
7258 if ( depth == 0 )
7259 maxboundcuts = conshdlrdata->maxboundcutsroot;
7260 else
7261 maxboundcuts = conshdlrdata->maxboundcuts;
7262
7263 if ( maxboundcuts >= 1 )
7264 {
7265 /* separate bound inequalities from SOS1 constraints */
7266 if( conshdlrdata->boundcutsfromsos1 || conshdlrdata->switchcutsfromsos1 )
7267 {
7268 SCIP_Bool cutoff;
7269
7270 SCIP_CALL( initsepaBoundInequalityFromSOS1Cons(scip, conshdlr, conshdlrdata, conss, nconss, sol, TRUE, maxboundcuts, &ngen, &cutoff) );
7271 if ( cutoff )
7272 {
7273 *result = SCIP_CUTOFF;
7274 return SCIP_OKAY;
7275 }
7276 }
7277
7278 /* separate bound inequalities from the conflict graph */
7279 if( conshdlrdata->boundcutsfromgraph && ! conshdlrdata->switchcutsfromsos1 )
7280 {
7281 SCIP_Bool cutoff;
7282 SCIP_CALL( sepaBoundInequalitiesFromGraph(scip, conshdlr, conshdlrdata, sol, maxboundcuts, &ngen, &cutoff) );
7283 if ( cutoff )
7284 {
7285 *result = SCIP_CUTOFF;
7286 return SCIP_OKAY;
7287 }
7288 }
7289 }
7290
7291 /* evaluate results */
7292 if ( ngen > 0 )
7293 *result = SCIP_SEPARATED;
7294 SCIPdebugMsg(scip, "Separated %d bound (clique) inequalities.\n", ngen);
7295 }
7296
7297 /* separate implied bound inequalities */
7298 if ( conshdlrdata->implcutsfreq >= 0 &&
7299 ( (conshdlrdata->implcutsfreq == 0 && depth == 0) || (conshdlrdata->implcutsfreq > 0 && depth % conshdlrdata->implcutsfreq == 0)) )
7300 {
7301 int maximplcuts;
7302 int ngen = 0;
7303
7304 /* determine maximal number of cuts*/
7305 if ( depth == 0 )
7306 maximplcuts = conshdlrdata->maximplcutsroot;
7307 else
7308 maximplcuts = conshdlrdata->maximplcuts;
7309
7310 /* call separator for implied bound cuts */
7311 if ( maximplcuts >= 1 )
7312 {
7313 SCIP_Bool cutoff;
7314 SCIP_CALL( sepaImplBoundCutsSOS1(scip, conshdlr, conshdlrdata, sol, maximplcuts, &ngen, &cutoff) );
7315 if ( cutoff )
7316 {
7317 *result = SCIP_CUTOFF;
7318 return SCIP_OKAY;
7319 }
7320 }
7321
7322 /* evaluate results */
7323 if ( ngen > 0 )
7324 *result = SCIP_SEPARATED;
7325 SCIPdebugMsg(scip, "Separated %d implied bound inequalities.\n", ngen);
7326 }
7327
7328 return SCIP_OKAY;
7329 }
7330
7331
7332 /* -------------------------- heuristic methods --------------------------------*/
7333
7334 /** gets weights determining an order of the variables in a heuristic for the maximum weighted independent set problem */
7335 static
getVectorOfWeights(SCIP * scip,SCIP_SOL * sol,SCIP_DIGRAPH * conflictgraph,int nsos1vars,SCIP_Bool * indicatorzero,SCIP_Real * weights)7336 SCIP_RETCODE getVectorOfWeights(
7337 SCIP* scip, /**< SCIP pointer */
7338 SCIP_SOL* sol, /**< primal solution or NULL for current LP solution */
7339 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
7340 int nsos1vars, /**< number of SOS1 variables */
7341 SCIP_Bool* indicatorzero, /**< vector that indicates which variables are currently fixed to zero */
7342 SCIP_Real* weights /**< pointer to store weights determining the order of the variables (length = nsos1vars) */
7343 )
7344 {
7345 SCIP_VAR* var;
7346 SCIP_Real val;
7347 SCIP_Real sum;
7348 int nviols;
7349 int* succ;
7350 int nsucc;
7351 int i;
7352 int j;
7353
7354 assert( scip != NULL );
7355 assert( conflictgraph != NULL );
7356 assert( indicatorzero != NULL );
7357 assert( weights != NULL );
7358
7359 for (i = 0; i < nsos1vars; ++i)
7360 {
7361 nsucc = SCIPdigraphGetNSuccessors(conflictgraph, i);
7362
7363 if( nsucc == 0 || indicatorzero[i] )
7364 weights[i] = 0.0;
7365 else
7366 {
7367 var = SCIPnodeGetVarSOS1(conflictgraph, i);
7368 val = REALABS( SCIPgetSolVal(scip, sol, var) );
7369 if ( SCIPisFeasZero(scip, val) )
7370 weights[i] = 0.0;
7371 else
7372 {
7373 succ = SCIPdigraphGetSuccessors(conflictgraph, i);
7374
7375 nviols = 0;
7376 sum = 0.0;
7377 for (j = 0; j < nsucc; ++j)
7378 {
7379 SCIP_Real valsucc;
7380
7381 valsucc = REALABS( SCIPgetSolVal(scip, sol, SCIPnodeGetVarSOS1(conflictgraph, succ[j])) );
7382 if( ! SCIPisFeasZero(scip, valsucc) )
7383 {
7384 sum += MIN(10E05, valsucc);
7385 ++nviols;
7386 }
7387 }
7388
7389 if ( nviols == 0 )
7390 weights[i] = 0.0;
7391 else
7392 {
7393 assert( SCIPisFeasPositive(scip, sum * (SCIP_Real)nviols));
7394 val = MIN(1e6, val);
7395 weights[i] = ( val + SCIPsumepsilon(scip) ) / ( sum * (SCIP_Real)nviols + SCIPsumepsilon(scip) );
7396 }
7397 }
7398 }
7399 }
7400
7401 return SCIP_OKAY;
7402 }
7403
7404
7405 /* marks neighbors of a given node as not a member of the maximal independent set */
7406 static
markNeighborsMWISHeuristic(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_DIGRAPH * conflictgraph,int node,SCIP_Bool * mark,SCIP_Bool * indset,int * cnt,SCIP_Bool * cutoff)7407 SCIP_RETCODE markNeighborsMWISHeuristic(
7408 SCIP* scip, /**< SCIP pointer */
7409 SCIP_CONSHDLR* conshdlr, /**< SOS1 constraint handler */
7410 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
7411 int node, /**< node of the conflict graph */
7412 SCIP_Bool* mark, /**< indicator vector of processed nodes */
7413 SCIP_Bool* indset, /**< indicator vector of current independent */
7414 int* cnt, /**< pointer to store number of marked nodes */
7415 SCIP_Bool* cutoff /**< pointer to store whether operation is infeasible */
7416 )
7417 {
7418 int nsucc;
7419 int* succ;
7420 int j;
7421
7422 assert( scip != NULL );
7423 assert( conflictgraph != NULL );
7424 assert( mark != NULL );
7425 assert( indset != NULL );
7426 assert( cutoff != NULL );
7427 assert( cnt != NULL );
7428
7429 *cutoff = FALSE;
7430
7431 nsucc = SCIPdigraphGetNSuccessors(conflictgraph, node);
7432 succ = SCIPdigraphGetSuccessors(conflictgraph, node);
7433
7434 /* for all successors */
7435 for (j = 0; j < nsucc && !(*cutoff); ++j)
7436 {
7437 int succj;
7438
7439 succj = succ[j];
7440 assert( indset[succj] == 0 );
7441 if( ! mark[succj] )
7442 {
7443 SCIP_VARSTATUS varstatus;
7444 SCIP_VAR* var;
7445
7446 /* mark node as processed */
7447 mark[succj] = TRUE;
7448 ++(*cnt);
7449
7450 /* get variable and variable status corresponding to successor node */
7451 var = SCIPnodeGetVarSOS1(conflictgraph, succj);
7452 varstatus = SCIPvarGetStatus(var);
7453
7454 /* if variable is aggregated */
7455 if ( varstatus == SCIP_VARSTATUS_AGGREGATED )
7456 {
7457 int aggrnode;
7458
7459 aggrnode = SCIPvarGetNodeSOS1(conshdlr, SCIPvarGetAggrVar(var));
7460
7461 /* if aggregated variable is an SOS1 variable */
7462 if ( aggrnode >= 0 )
7463 {
7464 /* if aggregated variable is implied to be zero */
7465 if ( SCIPisFeasZero(scip, SCIPvarGetAggrConstant(var)) )
7466 {
7467 if ( ! mark[aggrnode] )
7468 {
7469 mark[aggrnode] = TRUE;
7470 ++(*cnt);
7471 }
7472 else if ( indset[aggrnode] == 1 )
7473 {
7474 *cutoff = TRUE;
7475 return SCIP_OKAY;
7476 }
7477 }
7478 else
7479 {
7480 /* if aggregated variable is not already a member of the maximal independent set */
7481 if ( indset[aggrnode] == 0 )
7482 {
7483 /* if variable is already marked */
7484 if ( mark[aggrnode] )
7485 {
7486 *cutoff = TRUE;
7487 return SCIP_OKAY;
7488 }
7489 else
7490 {
7491 indset[aggrnode] = 1;
7492 mark[aggrnode] = TRUE;
7493 ++(*cnt);
7494 }
7495
7496 /* mark neighbors of aggregated variable */
7497 SCIP_CALL( markNeighborsMWISHeuristic(scip, conshdlr, conflictgraph, aggrnode, mark, indset, cnt, cutoff) );
7498 }
7499 }
7500 }
7501 }
7502 else if ( varstatus == SCIP_VARSTATUS_NEGATED )
7503 {
7504 int negnode;
7505
7506 negnode = SCIPvarGetNodeSOS1(conshdlr, SCIPvarGetNegationVar(var));
7507
7508 /* if negated variable is an SOS1 variable */
7509 if ( negnode >= 0 )
7510 {
7511 if ( SCIPisFeasZero(scip, SCIPvarGetNegationConstant(var) ) )
7512 {
7513 if ( indset[negnode] == 1 )
7514 {
7515 *cutoff = TRUE;
7516 return SCIP_OKAY;
7517 }
7518 else if ( ! mark[negnode] )
7519 {
7520 mark[negnode] = TRUE;
7521 ++(*cnt);
7522 }
7523 }
7524 }
7525 }
7526 }
7527 }
7528
7529 return SCIP_OKAY;
7530 }
7531
7532
7533 /** calls greedy algorithm for the maximum weighted independent set problem (MWIS)
7534 *
7535 * We compute a feasible solution to
7536 * \f[
7537 * \begin{array}{ll}
7538 * \min\limits_{z} & {x^*}^T z \\
7539 * & z_i + z_j \leq 1, \qquad (i,j)\in E \\
7540 * & z_i \in \{0,1\}, \qquad\quad i\in V
7541 * \end{array}
7542 * \f]
7543 * by the algorithm GGWMIN of Shuichi Sakai, Mitsunori Togasaki and Koichi Yamazaki in "A note on greedy algorithms for the
7544 * maximum weighted independent set problem", Discrete Applied Mathematics. Here \f$x^*\f$ denotes the current LP
7545 * relaxation solution. Note that the solution of the MWIS is the indicator vector of an independent set.
7546 */
7547 static
maxWeightIndSetHeuristic(SCIP * scip,SCIP_SOL * sol,SCIP_CONSHDLR * conshdlr,SCIP_DIGRAPH * conflictgraph,int nsos1vars,SCIP_Bool * indicatorzero,SCIP_Bool * indset)7548 SCIP_RETCODE maxWeightIndSetHeuristic(
7549 SCIP* scip, /**< SCIP pointer */
7550 SCIP_SOL* sol, /**< primal solution or NULL for current LP solution */
7551 SCIP_CONSHDLR* conshdlr, /**< SOS1 constraint handler */
7552 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
7553 int nsos1vars, /**< number of SOS1 variables */
7554 SCIP_Bool* indicatorzero, /**< vector that indicates which variables are currently fixed to zero */
7555 SCIP_Bool* indset /**< pointer to store indicator vector of an independent set */
7556 )
7557 {
7558 SCIP_Bool* mark = NULL;
7559 SCIP_Real* weights = NULL;
7560 int* indscipvars = NULL;
7561 int ind;
7562 int nsucc;
7563 int i;
7564 int k;
7565
7566 assert( scip != NULL );
7567 assert( conflictgraph != NULL );
7568 assert( indicatorzero != NULL );
7569 assert( indset != NULL );
7570
7571 /* allocate buffer arrays */
7572 SCIP_CALL( SCIPallocBufferArray(scip, &mark, nsos1vars) );
7573 SCIP_CALL( SCIPallocBufferArray(scip, &weights, nsos1vars) );
7574 SCIP_CALL( SCIPallocBufferArray(scip, &indscipvars, nsos1vars) );
7575
7576 /* sort SOS1 variables in nonincreasing order of weights */
7577 for (i = 0; i < nsos1vars; ++i)
7578 indscipvars[i] = i;
7579
7580 SCIP_CALL( getVectorOfWeights(scip, sol, conflictgraph, nsos1vars, indicatorzero, weights) );
7581 SCIPsortDownRealInt(weights, indscipvars, nsos1vars);
7582
7583 /* mark fixed variables and variables without any neighbors in the conflict graph */
7584 k = 0;
7585 for (i = 0; i < nsos1vars; ++i)
7586 {
7587 nsucc = SCIPdigraphGetNSuccessors(conflictgraph, i);
7588
7589 if ( indset[i] == 0 )
7590 {
7591 if( indicatorzero[i] )
7592 {
7593 mark[i] = TRUE;
7594 ++k;
7595 }
7596 else if ( nsucc == 0 )
7597 {
7598 indset[i] = 1;
7599 mark[i] = TRUE;
7600 ++k;
7601 }
7602 else
7603 mark[i] = FALSE;
7604 }
7605 else
7606 {
7607 SCIP_Bool cutoff;
7608
7609 ++k;
7610 mark[i] = TRUE;
7611
7612 SCIP_CALL( markNeighborsMWISHeuristic(scip, conshdlr, conflictgraph, i, mark, indset, &k, &cutoff) );
7613 assert( ! cutoff );
7614 }
7615 }
7616
7617 /* mark vertices in the order of their largest weight */
7618 for (i = 0; k < nsos1vars; ++i) /*lint !e440*/
7619 {
7620 assert( i < nsos1vars );
7621
7622 ind = indscipvars[i];
7623
7624 if ( ! mark[ind] )
7625 {
7626 SCIP_Bool cutoff;
7627
7628 /* mark ind */
7629 indset[ind] = 1;
7630 mark[ind] = TRUE;
7631 ++k;
7632
7633 SCIP_CALL( markNeighborsMWISHeuristic(scip, conshdlr, conflictgraph, ind, mark, indset, &k, &cutoff) );
7634 if ( cutoff )
7635 indset[ind] = 0;
7636 }
7637 }
7638 assert( k == nsos1vars );
7639
7640 /* free buffer arrays */
7641 SCIPfreeBufferArrayNull(scip, &indscipvars);
7642 SCIPfreeBufferArrayNull(scip, &weights);
7643 SCIPfreeBufferArrayNull(scip, &mark);
7644
7645 return SCIP_OKAY;
7646 }
7647
7648
7649 /** based on solution values of the variables, fixes variables of the conflict graph to zero to turn all SOS1 constraints feasible
7650 *
7651 * if the SOS1 constraints do not overlap, the method makeSOS1constraintsFeasible() may be faster
7652 */
7653 static
makeSOS1conflictgraphFeasible(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_SOL * sol,SCIP_Bool * changed,SCIP_Bool * allroundable)7654 SCIP_RETCODE makeSOS1conflictgraphFeasible(
7655 SCIP* scip, /**< SCIP pointer */
7656 SCIP_CONSHDLR* conshdlr, /**< SOS1 constraint handler */
7657 SCIP_SOL* sol, /**< solution */
7658 SCIP_Bool* changed, /**< pointer to store whether the solution has been changed */
7659 SCIP_Bool* allroundable /**< pointer to store whether all variables are roundable */
7660 )
7661 {
7662 SCIP_DIGRAPH* conflictgraph; /* conflict graph for SOS1 constraints */
7663 SCIP_Bool* indicatorzero; /* indicates which solution values are zero */
7664 SCIP_Bool* indset; /* indicator vector of feasible solution; i.e., an independent set */
7665 int nsos1vars;
7666 int j;
7667
7668 assert( scip != NULL );
7669 assert( conshdlr != NULL );
7670 assert( sol != NULL );
7671 assert( changed != NULL );
7672 assert( allroundable != NULL );
7673
7674 *allroundable = TRUE;
7675 *changed = FALSE;
7676
7677 /* get number of SOS1 variables */
7678 nsos1vars = SCIPgetNSOS1Vars(conshdlr);
7679 assert( nsos1vars >= 0 );
7680
7681 /* get conflict graph */
7682 conflictgraph = SCIPgetConflictgraphSOS1(conshdlr);
7683 assert( conflictgraph != NULL );
7684
7685 /* allocate buffer arrays */
7686 SCIP_CALL( SCIPallocBufferArray(scip, &indset, nsos1vars) );
7687 SCIP_CALL( SCIPallocBufferArray(scip, &indicatorzero, nsos1vars) );
7688
7689 /* determine if variables with nonzero solution value are roundable */
7690 for (j = 0; j < nsos1vars; ++j)
7691 {
7692 SCIP_VAR* var;
7693 SCIP_Real lb;
7694 SCIP_Real ub;
7695
7696 var = SCIPnodeGetVarSOS1(conflictgraph, j);
7697 lb = SCIPvarGetLbLocal(var);
7698 ub = SCIPvarGetUbLocal(var);
7699 indset[j] = 0;
7700
7701 /* if solution value of variable is zero */
7702 if ( SCIPisFeasZero(scip, SCIPgetSolVal(scip, sol, var)) )
7703 indicatorzero[j] = TRUE;
7704 else
7705 {
7706 indicatorzero[j] = FALSE;
7707
7708 /* if variable is not roundable */
7709 if ( ! SCIPvarMayRoundDown(var) && ! SCIPvarMayRoundUp(var) )
7710 {
7711 *allroundable = FALSE;
7712 break;
7713 }
7714
7715 /* if bounds of variable are fixed to zero */
7716 if ( SCIPisFeasZero(scip, ub) && SCIPisFeasZero(scip, lb) )
7717 indicatorzero[j] = TRUE;
7718 else if ( SCIPisFeasPositive(scip, lb) || SCIPisFeasNegative(scip, ub) ) /* if variable is fixed to be nonzero */
7719 indset[j] = 1;
7720 }
7721 }
7722
7723 /* return if at least one SOS1 variable is not roundable */
7724 if ( ! (*allroundable) )
7725 {
7726 SCIPfreeBufferArray(scip, &indicatorzero);
7727 SCIPfreeBufferArray(scip, &indset);
7728 return SCIP_OKAY;
7729 }
7730
7731 /* call greedy algorithm for the maximum weighted independent set problem */
7732 SCIP_CALL( maxWeightIndSetHeuristic(scip, sol, conshdlr, conflictgraph, nsos1vars, indicatorzero, indset) );
7733
7734 /* make solution feasible */
7735 for (j = 0; j < nsos1vars; ++j)
7736 {
7737 if ( indset[j] == 0 )
7738 {
7739 SCIP_CALL( SCIPsetSolVal(scip, sol, SCIPnodeGetVarSOS1(conflictgraph, j), 0.0) );
7740 *changed = TRUE;
7741 }
7742 }
7743
7744 /* free buffer arrays */
7745 SCIPfreeBufferArray(scip, &indicatorzero);
7746 SCIPfreeBufferArray(scip, &indset);
7747
7748 #ifdef SCIP_NDEBUG
7749 {
7750 SCIP_CONSDATA* consdata;
7751 SCIP_CONS** conss;
7752 int nconss;
7753 int c;
7754
7755 conss = SCIPconshdlrGetConss(conshdlr);
7756 nconss = SCIPconshdlrGetNConss(conshdlr);
7757 for (c = 0; c < nconss; ++c)
7758 {
7759 int cnt = 0;
7760 consdata = SCIPconsGetData(conss[c]);
7761 assert( consdata != NULL );
7762
7763 for (j = 0; j < consdata->nvars; ++j)
7764 {
7765 if ( ! SCIPisFeasZero(scip, SCIPgetSolVal(scip, sol, consdata->vars[j])) )
7766 {
7767 ++cnt;
7768 }
7769 }
7770 assert( cnt < 2 );
7771 }
7772 }
7773 #endif
7774
7775 return SCIP_OKAY;
7776 }
7777
7778
7779 /** based on solution values of the variables, fixes variables of the SOS1 constraints to zero to turn these constraints feasible
7780 *
7781 * if the SOS1 constraints overlap, the method makeSOS1constraintsFeasible() may result in better primal solutions
7782 */
7783 static
makeSOS1constraintsFeasible(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_SOL * sol,SCIP_Bool * changed,SCIP_Bool * allroundable)7784 SCIP_RETCODE makeSOS1constraintsFeasible(
7785 SCIP* scip, /**< SCIP pointer */
7786 SCIP_CONSHDLR* conshdlr, /**< SOS1 constraint handler */
7787 SCIP_SOL* sol, /**< solution */
7788 SCIP_Bool* changed, /**< pointer to store whether the solution has been changed */
7789 SCIP_Bool* allroundable /**< pointer to store whether all variables are roundable */
7790 )
7791 {
7792 SCIP_CONSDATA* consdata;
7793 SCIP_CONS** conss;
7794 int nconss;
7795 int c;
7796
7797 assert( scip != NULL );
7798 assert( conshdlr != NULL );
7799 assert( sol != NULL );
7800 assert( changed != NULL );
7801 assert( allroundable != NULL );
7802
7803 *allroundable = TRUE;
7804 *changed = FALSE;
7805
7806 /* get SOS1 constraints and number of SOS1 constraints */
7807 conss = SCIPconshdlrGetConss(conshdlr);
7808 nconss = SCIPconshdlrGetNConss(conshdlr);
7809 assert( nconss > 0 );
7810
7811 /* loop through all SOS1 constraints */
7812 for (c = 0; c < nconss && *allroundable; ++c)
7813 {
7814 SCIP_CONS* cons;
7815 SCIP_VAR** vars;
7816 SCIP_Bool varisfixed = FALSE;
7817 SCIP_Real maxval = 0.0;
7818 int pos = -1;
7819 int nvars;
7820 int j;
7821
7822 cons = conss[c];
7823 assert( cons != NULL );
7824 consdata = SCIPconsGetData(cons);
7825 assert( consdata != NULL );
7826
7827 nvars = consdata->nvars;
7828 vars = consdata->vars;
7829
7830 /* search for maximum solution value */
7831 for (j = 0; j < nvars; ++j)
7832 {
7833 SCIP_VAR* var;
7834
7835 var = vars[j];
7836
7837 if ( ! SCIPisFeasZero(scip, SCIPgetSolVal(scip, sol, var)) )
7838 {
7839 SCIP_Real lb;
7840 SCIP_Real ub;
7841
7842 lb = SCIPvarGetLbLocal(var);
7843 ub = SCIPvarGetUbLocal(var);
7844
7845 /* if variable is not roundable */
7846 if ( ! SCIPvarMayRoundDown(var) && ! SCIPvarMayRoundUp(var) )
7847 {
7848 *allroundable = FALSE;
7849 break;
7850 }
7851
7852 /* it is possible that the bounds were proagated to zero although the current solution value is nonzero
7853 * in this case fix the solution value to zero */
7854 if ( SCIPisFeasZero(scip, ub) && SCIPisFeasZero(scip, lb) )
7855 {
7856 SCIP_CALL( SCIPsetSolVal(scip, sol, var, 0.0) );
7857 *changed = TRUE;
7858 }
7859 else if ( SCIPisFeasPositive(scip, lb) || SCIPisFeasNegative(scip, ub) ) /* if variable is fixed to be nonzero */
7860 {
7861 assert( ! varisfixed );
7862 varisfixed = TRUE;
7863 maxval = SCIPgetSolVal(scip, sol, var);
7864 pos = j;
7865 }
7866 else if ( ! varisfixed && SCIPisFeasGT(scip, REALABS(SCIPgetSolVal(scip, sol, var)), REALABS(maxval)) ) /* search for variable with maximum solution value */
7867 {
7868 maxval = SCIPgetSolVal(scip, sol, var);
7869 pos = j;
7870 }
7871
7872 /* fix variable to zero; the solution value of the variable with maximum solution value
7873 * will be restored in a later step */
7874 SCIP_CALL( SCIPsetSolVal(scip, sol, var, 0.0) );
7875 *changed = TRUE;
7876 }
7877 }
7878
7879 if ( ! (*allroundable) )
7880 break;
7881 else if ( pos >= 0 ) /* restore solution of variable with maximum solution value */
7882 {
7883 SCIP_CALL( SCIPsetSolVal(scip, sol, vars[pos], maxval) );
7884 }
7885 }
7886
7887 #ifdef SCIP_NDEBUG
7888 if ( *allroundable )
7889 {
7890 for (c = 0; c < nconss; ++c)
7891 {
7892 int cnt = 0;
7893 int j;
7894
7895 consdata = SCIPconsGetData(conss[c]);
7896 assert( consdata != NULL );
7897
7898 for (j = 0; j < consdata->nvars; ++j)
7899 {
7900 if ( ! SCIPisFeasZero(scip, SCIPgetSolVal(scip, sol, consdata->vars[j])) )
7901 {
7902 ++cnt;
7903 }
7904 }
7905 assert( cnt < 2 );
7906 }
7907 }
7908 #endif
7909
7910 return SCIP_OKAY;
7911 }
7912
7913
7914 /** determine a diving variables and boundchanges of diving variables by analyzing the conflict graph
7915 *
7916 * if the SOS1 constraints do not overlap, the method getDiveBdChgsSOS1constraints() may be faster
7917 */
7918 static
getDiveBdChgsSOS1conflictgraph(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_DIVESET * diveset,SCIP_SOL * sol,SCIP_Bool * success)7919 SCIP_RETCODE getDiveBdChgsSOS1conflictgraph(
7920 SCIP* scip, /**< SCIP pointer */
7921 SCIP_CONSHDLR* conshdlr, /**< SOS1 constraint handler */
7922 SCIP_DIVESET* diveset, /**< diving settings */
7923 SCIP_SOL* sol, /**< solution */
7924 SCIP_Bool* success /**< pointer to store */
7925 )
7926 {
7927 SCIP_DIGRAPH* conflictgraph;
7928 SCIP_VAR* bestvar = NULL;
7929 SCIP_Bool bestvarfixneigh = FALSE;
7930 SCIP_Real bestscore = SCIP_REAL_MIN;
7931 int bestnode = -1;
7932 int nsos1vars;
7933 int v;
7934
7935 assert( scip != NULL );
7936 assert( conshdlr != NULL );
7937 assert( diveset != NULL );
7938 assert( success != NULL );
7939
7940 *success = FALSE;
7941
7942 /* get number of SOS1 variables */
7943 nsos1vars = SCIPgetNSOS1Vars(conshdlr);
7944
7945 /* get conflict graph of SOS1 constraints */
7946 conflictgraph = SCIPgetConflictgraphSOS1(conshdlr);
7947
7948 /* loop over SOS1 variables */
7949 for (v = 0; v < nsos1vars; ++v)
7950 {
7951 /* check whether the variable violates an SOS1 constraint together with at least one other variable */
7952 if ( isViolatedSOS1(scip, conflictgraph, v, sol) )
7953 {
7954 SCIP_VAR* var;
7955 SCIP_Real solval;
7956 SCIP_Real score;
7957 SCIP_Real bound;
7958 SCIP_Real fracval;
7959 SCIP_Bool fixneigh;
7960
7961 var = SCIPnodeGetVarSOS1(conflictgraph, v);
7962 solval = SCIPgetSolVal(scip, sol, var);
7963
7964 /* compute (variable) bound of candidate */
7965 if ( SCIPisFeasNegative(scip, solval) )
7966 bound = nodeGetSolvalVarboundLbSOS1(scip, conflictgraph, sol, v);
7967 else
7968 bound = nodeGetSolvalVarboundUbSOS1(scip, conflictgraph, sol, v);
7969
7970 /* ensure finiteness */
7971 bound = MIN(DIVINGCUTOFFVALUE, REALABS(bound)); /*lint !e666*/
7972 fracval = MIN(DIVINGCUTOFFVALUE, REALABS(solval)); /*lint !e666*/
7973 assert( ! SCIPisInfinity(scip, bound) );
7974 assert( ! SCIPisInfinity(scip, fracval) );
7975 assert( SCIPisPositive(scip, bound) );
7976
7977 /* bound may have changed in propagation; ensure that fracval <= 1 */
7978 if ( SCIPisFeasLT(scip, bound, fracval) )
7979 bound = fracval;
7980
7981 /* get fractionality of candidate */
7982 fracval /= (bound + SCIPsumepsilon(scip));
7983
7984 /* should SOS1 variables be scored by the diving heuristics specific score function;
7985 * otherwise use the score function of the SOS1 constraint handler */
7986 if ( SCIPdivesetSupportsType(diveset, SCIP_DIVETYPE_SOS1VARIABLE) )
7987 {
7988 SCIP_Bool roundup;
7989
7990 SCIP_CALL( SCIPgetDivesetScore(scip, diveset, SCIP_DIVETYPE_SOS1VARIABLE, var, solval, fracval,
7991 &score, &roundup) );
7992
7993 fixneigh = roundup;
7994 if ( SCIPisFeasNegative(scip, solval) )
7995 fixneigh = !fixneigh;
7996 }
7997 else
7998 {
7999 /* we always fix the candidates neighbors in the conflict graph to zero */
8000 fixneigh = TRUE;
8001
8002 /* score fractionality of candidate */
8003 score = fracval;
8004 }
8005
8006 /* best candidate maximizes the score */
8007 if ( score > bestscore )
8008 {
8009 bestscore = score;
8010
8011 *success = TRUE;
8012 bestvar = var;
8013 bestnode = v;
8014 bestvarfixneigh = fixneigh;
8015 }
8016 }
8017 }
8018 assert( !(*success) || bestvar != NULL );
8019
8020 if ( *success )
8021 {
8022 int* succ;
8023 int nsucc;
8024 int s;
8025
8026 assert( bestnode >= 0 && bestnode < nsos1vars );
8027
8028 nsucc = SCIPdigraphGetNSuccessors(conflictgraph, bestnode);
8029 succ = SCIPdigraphGetSuccessors(conflictgraph, bestnode);
8030
8031 /* if the diving score voted for fixing the best variable to 0.0, we add this as the preferred bound change;
8032 * otherwise, fixing the neighbors in the conflict graph to 0.0 is the preferred bound change.
8033 */
8034 assert( SCIPisFeasNegative(scip, SCIPvarGetLbLocal(bestvar)) || SCIPisFeasPositive(scip, SCIPvarGetUbLocal(bestvar)) );
8035 SCIP_CALL( SCIPaddDiveBoundChange(scip, bestvar, SCIP_BRANCHDIR_FIXED, 0.0, !bestvarfixneigh) );
8036 for (s = 0; s < nsucc; ++s)
8037 {
8038 SCIP_VAR* var;
8039
8040 var = SCIPnodeGetVarSOS1(conflictgraph, succ[s]);
8041
8042 /* if variable is not already fixed */
8043 if ( SCIPisFeasNegative(scip, SCIPvarGetLbLocal(var)) || SCIPisFeasPositive(scip, SCIPvarGetUbLocal(var)) )
8044 {
8045 SCIP_CALL( SCIPaddDiveBoundChange(scip, var, SCIP_BRANCHDIR_FIXED, 0.0, bestvarfixneigh) );
8046 }
8047 }
8048 }
8049
8050 return SCIP_OKAY;
8051 }
8052
8053
8054 /** determine a diving variables and boundchanges of diving variables by analyzing the SOS1 constraints
8055 *
8056 * if the SOS1 constraints overlap, the method getDiveBdChgsSOS1conflictgraph() may produce better results (e.g., due to more
8057 * diving candidates)
8058 */
8059 static
getDiveBdChgsSOS1constraints(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_DIVESET * diveset,SCIP_SOL * sol,SCIP_Bool * success)8060 SCIP_RETCODE getDiveBdChgsSOS1constraints(
8061 SCIP* scip, /**< SCIP pointer */
8062 SCIP_CONSHDLR* conshdlr, /**< SOS1 constraint handler */
8063 SCIP_DIVESET* diveset, /**< diving settings */
8064 SCIP_SOL* sol, /**< solution */
8065 SCIP_Bool* success /**< pointer to store */
8066 )
8067 {
8068 SCIP_VAR* bestvar = NULL;
8069 SCIP_Bool bestvarfixcomp = FALSE;
8070 SCIP_Real bestscore = SCIP_REAL_MIN;
8071 SCIP_CONSDATA* consdata;
8072 SCIP_CONS** conss;
8073 int nconss;
8074 int bestcons = -1;
8075 int c;
8076
8077 assert( scip != NULL );
8078 assert( conshdlr != NULL );
8079 assert( diveset != NULL );
8080 assert( success != NULL );
8081
8082 *success = FALSE;
8083
8084 /* get SOS1 constraints and number of SOS1 constraints */
8085 conss = SCIPconshdlrGetConss(conshdlr);
8086 nconss = SCIPconshdlrGetNConss(conshdlr);
8087
8088 /* loop through all SOS1 constraints */
8089 for (c = 0; c < nconss; ++c)
8090 {
8091 SCIP_VAR** vars;
8092 int nvars;
8093 int cnt = 0;
8094 int j;
8095
8096 consdata = SCIPconsGetData(conss[c]);
8097 assert( consdata != NULL );
8098
8099 nvars = consdata->nvars;
8100 vars = consdata->vars;
8101
8102 /* check whether SOS1 constraint is violated */
8103 for (j = 0; j < nvars && cnt < 2; ++j)
8104 {
8105 SCIP_VAR* var;
8106
8107 var = vars[j];
8108
8109 /* check whether variable is nonzero w.r.t. sol and the bounds have not been fixed to zero by propagation */
8110 if ( !SCIPisFeasZero(scip, SCIPgetSolVal(scip, sol, var))
8111 && (!SCIPisFeasZero(scip, SCIPvarGetLbLocal(var)) || !SCIPisFeasZero(scip, SCIPvarGetUbLocal(var))) )
8112 ++cnt;
8113 }
8114
8115 /* if SOS1 constraint is not violated then continue with the next SOS1 constraint */
8116 if ( cnt < 2 )
8117 continue;
8118
8119 /* get diving score of every variable in constraint */
8120 for (j = 0; j < nvars; ++j)
8121 {
8122 SCIP_VAR* var;
8123 SCIP_Real solval;
8124 SCIP_Real score;
8125 SCIP_Real bound;
8126 SCIP_Real fracval;
8127 SCIP_Real lb;
8128 SCIP_Real ub;
8129 SCIP_Bool fixcomp; /* whether to fix the complementary variables of the candidate in the SOS1 constraint to zero */
8130
8131 var = vars[j];
8132 solval = SCIPgetSolVal(scip, sol, var);
8133 lb = SCIPvarGetLbLocal(var);
8134 ub = SCIPvarGetUbLocal(var);
8135
8136 /* check whether variable is nonzero w.r.t. sol and the bounds have not been fixed to zero by propagation */
8137 if ( ! SCIPisFeasZero(scip, solval) && ( ! SCIPisFeasZero(scip, lb) || ! SCIPisFeasZero(scip, ub) ) )
8138 {
8139 /* compute (variable) bound of candidate */
8140 if ( SCIPisFeasNegative(scip, solval) )
8141 bound = lb;
8142 else
8143 bound = ub;
8144
8145 /* bound may have changed in propagation; ensure that fracval <= 1 */
8146 if ( SCIPisFeasLT(scip, REALABS(bound), REALABS(solval)) )
8147 bound = solval;
8148
8149 /* ensure finiteness */
8150 bound = MIN(DIVINGCUTOFFVALUE, REALABS(bound)); /*lint !e666*/
8151 fracval = MIN(DIVINGCUTOFFVALUE, REALABS(solval)); /*lint !e666*/
8152 assert( ! SCIPisInfinity(scip, bound) );
8153 assert( ! SCIPisInfinity(scip, fracval) );
8154 assert( SCIPisPositive(scip, bound) );
8155
8156 /* get fractionality of candidate */
8157 fracval /= (bound + SCIPsumepsilon(scip));
8158
8159 /* should SOS1 variables be scored by the diving heuristics specific score function;
8160 * otherwise use the score function of the SOS1 constraint handler
8161 */
8162 if ( SCIPdivesetSupportsType(diveset, SCIP_DIVETYPE_SOS1VARIABLE) )
8163 {
8164 SCIP_Bool roundup;
8165
8166 SCIP_CALL( SCIPgetDivesetScore(scip, diveset, SCIP_DIVETYPE_SOS1VARIABLE, var, solval, fracval,
8167 &score, &roundup) );
8168
8169 fixcomp = roundup;
8170 if ( SCIPisFeasNegative(scip, solval) )
8171 fixcomp = !fixcomp;
8172 }
8173 else
8174 {
8175 /* we always fix the complementary variables of the candidate in the SOS1 constraint to zero */
8176 fixcomp = TRUE;
8177
8178 /* score fractionality of candidate */
8179 score = fracval;
8180 }
8181
8182 /* best candidate maximizes the score */
8183 if ( score > bestscore )
8184 {
8185 bestscore = score;
8186
8187 *success = TRUE;
8188 bestvar = var;
8189 bestcons = c;
8190 bestvarfixcomp = fixcomp;
8191 }
8192 }
8193 }
8194 }
8195 assert( !(*success) || bestvar != NULL );
8196
8197 if ( *success )
8198 {
8199 SCIP_VAR** vars;
8200 int nvars;
8201 int j;
8202
8203 consdata = SCIPconsGetData(conss[bestcons]);
8204 assert( consdata != NULL );
8205
8206 nvars = consdata->nvars;
8207 vars = consdata->vars;
8208
8209 assert( bestcons >= 0 && bestcons < nconss );
8210
8211 /* if the diving score voted for fixing the best variable to 0.0, we add this as the preferred bound change;
8212 * otherwise, fixing the complementary variables of the candidate in the SOS1 constraint to 0.0 is the preferred bound change.
8213 */
8214 assert( SCIPisFeasNegative(scip, SCIPvarGetLbLocal(bestvar)) || SCIPisFeasPositive(scip, SCIPvarGetUbLocal(bestvar)) );
8215
8216 SCIP_CALL( SCIPaddDiveBoundChange(scip, bestvar, SCIP_BRANCHDIR_FIXED, 0.0, !bestvarfixcomp) );
8217 for (j = 0; j < nvars; ++j)
8218 {
8219 SCIP_VAR* var;
8220
8221 var = vars[j];
8222
8223 /* if variable is not already fixed and is not the candidate variable */
8224 if ( var != bestvar && ( SCIPisFeasNegative(scip, SCIPvarGetLbLocal(var)) || SCIPisFeasPositive(scip, SCIPvarGetUbLocal(var)) ) )
8225 {
8226 SCIP_CALL( SCIPaddDiveBoundChange(scip, var, SCIP_BRANCHDIR_FIXED, 0.0, bestvarfixcomp) );
8227 }
8228 }
8229 }
8230
8231 return SCIP_OKAY;
8232 }
8233
8234
8235 /* --------------------initialization/deinitialization ------------------------*/
8236
8237 /** check whether \f$x_1\f$ is a bound variable of \f$x_0\f$; i.e., \f$x_0 \leq c\cdot x_1\f$ or \f$x_0 \geq d\cdot x_1\f$
8238 * for positive values \f$c, d\f$. If true, then add this information to the node data of the conflict graph.
8239 */
8240 static
detectVarboundSOS1(SCIP * scip,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_VAR * var0,SCIP_VAR * var1,SCIP_Real val0,SCIP_Real val1)8241 SCIP_RETCODE detectVarboundSOS1(
8242 SCIP* scip, /**< SCIP pointer */
8243 SCIP_CONSHDLRDATA* conshdlrdata, /**< SOS1 constraint handler data */
8244 SCIP_VAR* var0, /**< first variable */
8245 SCIP_VAR* var1, /**< second variable */
8246 SCIP_Real val0, /**< first coefficient */
8247 SCIP_Real val1 /**< second coefficient */
8248 )
8249 {
8250 int node0;
8251
8252 assert( scip != NULL );
8253 assert( conshdlrdata != NULL );
8254 assert( var0 != NULL && var1 != NULL );
8255
8256 /* get nodes of variable in the conflict graph (node = -1 if no SOS1 variable) */
8257 node0 = varGetNodeSOS1(conshdlrdata, var0);
8258
8259 /* if var0 is an SOS1 variable */
8260 if ( node0 >= 0 )
8261 {
8262 SCIP_Real val;
8263
8264 assert( ! SCIPisFeasZero(scip, val0) );
8265 val = -val1/val0;
8266
8267 /* check variable bound relation of variables */
8268
8269 /* handle lower bound case */
8270 if ( SCIPisFeasNegative(scip, val0) && SCIPisFeasNegative(scip, val) )
8271 {
8272 SCIP_NODEDATA* nodedata;
8273
8274 /* get node data of the conflict graph */
8275 nodedata = (SCIP_NODEDATA*)SCIPdigraphGetNodeData(conshdlrdata->conflictgraph, node0);
8276
8277 /* @todo: maybe save multiple variable bounds for each SOS1 variable */
8278 if ( nodedata->lbboundvar == NULL )
8279 {
8280 /* add variable bound information to node data */
8281 nodedata->lbboundvar = var1;
8282 nodedata->lbboundcoef = val;
8283
8284 SCIPdebugMsg(scip, "detected variable bound constraint %s >= %f %s.\n", SCIPvarGetName(var0), val, SCIPvarGetName(var1));
8285 }
8286 }
8287 /* handle upper bound case */
8288 else if ( SCIPisFeasPositive(scip, val0) && SCIPisFeasPositive(scip, val) )
8289 {
8290 SCIP_NODEDATA* nodedata;
8291 assert( SCIPisFeasPositive(scip, val0) );
8292
8293 /* get node data of the conflict graph */
8294 nodedata = (SCIP_NODEDATA*)SCIPdigraphGetNodeData(conshdlrdata->conflictgraph, node0);
8295
8296 if ( nodedata->ubboundvar == NULL )
8297 {
8298 /* add variable bound information to node data */
8299 nodedata->ubboundvar = var1;
8300 nodedata->ubboundcoef = val;
8301
8302 SCIPdebugMsg(scip, "detected variable bound constraint %s <= %f %s.\n", SCIPvarGetName(var0), val, SCIPvarGetName(var1));
8303 }
8304 }
8305 }
8306
8307 return SCIP_OKAY;
8308 }
8309
8310
8311 /** pass connected component \f$C\f$ of the conflict graph and check whether all the variables correspond to a unique variable upper bound variable \f$z\f$,
8312 * i.e., \f$x_i \leq u_i z\f$ for every \f$i\in C\f$.
8313 *
8314 * @note if the bound variable is unique, then bound inequalities can be strengthened.
8315 */
8316 static
passConComponentVarbound(SCIP * scip,SCIP_DIGRAPH * conflictgraph,int node,SCIP_VAR * boundvar,SCIP_Bool checklb,SCIP_Bool * processed,int * concomp,int * nconcomp,SCIP_Bool * unique)8317 SCIP_RETCODE passConComponentVarbound(
8318 SCIP* scip, /**< SCIP pointer */
8319 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
8320 int node, /**< current node of connected component */
8321 SCIP_VAR* boundvar, /**< bound variable of connected component */
8322 SCIP_Bool checklb, /**< whether to check lower bound variable (else upper bound variable) */
8323 SCIP_Bool* processed, /**< states for each variable whether it has been processed */
8324 int* concomp, /**< current connected component */
8325 int* nconcomp, /**< pointer to store number of elements of connected component */
8326 SCIP_Bool* unique /**< pointer to store whether bound variable is unique */
8327 )
8328 {
8329 int* succ;
8330 int nsucc;
8331 int s;
8332
8333 assert( scip != NULL );
8334 assert( conflictgraph != NULL );
8335 assert( processed != NULL );
8336 assert( concomp != NULL );
8337 assert( nconcomp != NULL );
8338 assert( unique != NULL );
8339
8340 processed[node] = TRUE;/*lint !e737*/
8341 concomp[(*nconcomp)++] = node;
8342
8343 /* if bound variable of connected component without new node is unique */
8344 if ( *unique )
8345 {
8346 SCIP_NODEDATA* nodedata;
8347 SCIP_VAR* comparevar;
8348 nodedata = (SCIP_NODEDATA*)SCIPdigraphGetNodeData(conflictgraph, node);
8349 assert( nodedata != NULL );
8350
8351 if ( checklb )
8352 comparevar = nodedata->lbboundvar;
8353 else
8354 comparevar = nodedata->ubboundvar;
8355
8356 /* check whether bound variable is unique for connected component without new node */
8357 if ( boundvar == NULL )
8358 {
8359 if ( comparevar != NULL )
8360 *unique = FALSE;
8361 }
8362 else
8363 {
8364 if ( comparevar == NULL )
8365 *unique = FALSE;
8366 else if ( SCIPvarCompare(boundvar, comparevar) != 0 )
8367 *unique = FALSE;
8368 }
8369 }
8370
8371 /* pass through successor variables */
8372 nsucc = SCIPdigraphGetNSuccessors(conflictgraph, node);
8373 succ = SCIPdigraphGetSuccessors(conflictgraph, node);
8374 for (s = 0; s < nsucc; ++s)
8375 {
8376 if ( ! processed[succ[s]] )
8377 SCIP_CALL( passConComponentVarbound(scip, conflictgraph, succ[s], boundvar, checklb, processed, concomp, nconcomp, unique) );
8378 }
8379
8380 return SCIP_OKAY;
8381 }
8382
8383
8384 /** for each connected component \f$C\f$ of the conflict graph check whether all the variables correspond to a unique variable upper bound variable \f$z\f$
8385 * (e.g., for the upper bound case this means that \f$x_i \leq u_i z\f$ for every \f$i\in C\f$).
8386 *
8387 * @note if the bound variable is unique, then bound inequalities can be strengthened.
8388 */
8389 static
checkConComponentsVarbound(SCIP * scip,SCIP_DIGRAPH * conflictgraph,int nsos1vars,SCIP_Bool checklb)8390 SCIP_RETCODE checkConComponentsVarbound(
8391 SCIP* scip, /**< SCIP pointer */
8392 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
8393 int nsos1vars, /**< number of SOS1 variables */
8394 SCIP_Bool checklb /**< whether to check lower bound variable (else check upper bound variable) */
8395 )
8396 {
8397 SCIP_Bool* processed; /* states for each variable whether it has been processed */
8398 int* concomp; /* current connected component */
8399 int nconcomp;
8400 int j;
8401
8402 assert( scip != NULL );
8403 assert( conflictgraph != NULL );
8404
8405 /* allocate buffer arrays and initialize 'processed' array */
8406 SCIP_CALL( SCIPallocBufferArray(scip, &processed, nsos1vars) );
8407 SCIP_CALL( SCIPallocBufferArray(scip, &concomp, nsos1vars) );
8408 for (j = 0; j < nsos1vars; ++j)
8409 processed[j] = FALSE;
8410
8411 /* run through all SOS1 variables */
8412 for (j = 0; j < nsos1vars; ++j)
8413 {
8414 /* if variable belongs to a connected component that has not been processed so far */
8415 if ( ! processed[j] )
8416 {
8417 SCIP_NODEDATA* nodedata;
8418 SCIP_VAR* boundvar;
8419 SCIP_Bool unique;
8420 int* succ;
8421 int nsucc;
8422 int s;
8423
8424 nodedata = (SCIP_NODEDATA*)SCIPdigraphGetNodeData(conflictgraph, j);
8425 assert( nodedata != NULL );
8426
8427 if ( checklb )
8428 boundvar = nodedata->lbboundvar;
8429 else
8430 boundvar = nodedata->ubboundvar;
8431 unique = TRUE;
8432
8433 processed[j] = TRUE;
8434 concomp[0] = j;
8435 nconcomp = 1;
8436
8437 /* pass through successor variables */
8438 nsucc = SCIPdigraphGetNSuccessors(conflictgraph, j);
8439 succ = SCIPdigraphGetSuccessors(conflictgraph, j);
8440 for (s = 0; s < nsucc; ++s)
8441 {
8442 if ( ! processed[succ[s]] )
8443 {
8444 SCIP_CALL( passConComponentVarbound(scip, conflictgraph, succ[s], boundvar, checklb, processed, concomp, &nconcomp, &unique) );
8445 }
8446 }
8447
8448 /* if the connected component has a unique bound variable */
8449 if ( unique && boundvar != NULL )
8450 {
8451 for (s = 0; s < nconcomp; ++s)
8452 {
8453 nodedata = (SCIP_NODEDATA*)SCIPdigraphGetNodeData(conflictgraph, concomp[s]);
8454 assert( processed[concomp[s]] == TRUE );
8455 assert( nodedata != NULL );
8456
8457 if ( checklb )
8458 nodedata->lbboundcomp = TRUE;
8459 else
8460 nodedata->ubboundcomp = TRUE;
8461 }
8462 SCIPdebugMsg(scip, "Found a connected component of size <%i> with unique bound variable.\n", nconcomp);
8463 }
8464 }
8465 }
8466
8467 /* free buffer arrays */
8468 SCIPfreeBufferArray(scip, &concomp);
8469 SCIPfreeBufferArray(scip, &processed);
8470
8471 return SCIP_OKAY;
8472 }
8473
8474
8475 /** check all linear constraints for variable bound constraints of the form \f$c\cdot z \leq x \leq d\cdot z\f$, where @p x is some SOS1
8476 * variable and @p z is some arbitrary variable (not necessarily binary)
8477 */
8478 static
checkLinearConssVarboundSOS1(SCIP * scip,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_CONS ** linconss,int nlinconss)8479 SCIP_RETCODE checkLinearConssVarboundSOS1(
8480 SCIP* scip, /**< SCIP pointer */
8481 SCIP_CONSHDLRDATA* conshdlrdata, /**< SOS1 constraint handler data */
8482 SCIP_CONS** linconss, /**< linear constraints */
8483 int nlinconss /**< number of linear constraints */
8484 )
8485 {
8486 int c;
8487
8488 /* loop through linear constraints */
8489 for (c = 0; c < nlinconss; ++c)
8490 {
8491 SCIP_CONS* lincons;
8492 int nvars;
8493
8494 lincons = linconss[c];
8495
8496 /* variable bound constraints only contain two variables */
8497 nvars = SCIPgetNVarsLinear(scip, lincons);
8498 if ( nvars == 2 )
8499 {
8500 SCIP_VAR** vars;
8501 SCIP_Real* vals;
8502 SCIP_VAR* var0;
8503 SCIP_VAR* var1;
8504 SCIP_Real lhs;
8505 SCIP_Real rhs;
8506
8507 /* get constraint data */
8508 vars = SCIPgetVarsLinear(scip, lincons);
8509 vals = SCIPgetValsLinear(scip, lincons);
8510 lhs = SCIPgetLhsLinear(scip, lincons);
8511 rhs = SCIPgetRhsLinear(scip, lincons);
8512
8513 var0 = vars[0];
8514 var1 = vars[1];
8515 assert( var0 != NULL && var1 != NULL );
8516
8517 /* at least one variable should be an SOS1 variable */
8518 if ( varIsSOS1(conshdlrdata, var0) || varIsSOS1(conshdlrdata, var1) )
8519 {
8520 SCIP_Real val0;
8521 SCIP_Real val1;
8522
8523 /* check whether right hand side or left hand side of constraint is zero */
8524 if ( SCIPisFeasZero(scip, lhs) )
8525 {
8526 val0 = -vals[0];
8527 val1 = -vals[1];
8528
8529 /* check whether the two variables are in a variable bound relation */
8530 SCIP_CALL( detectVarboundSOS1(scip, conshdlrdata, var0, var1, val0, val1) );
8531 SCIP_CALL( detectVarboundSOS1(scip, conshdlrdata, var1, var0, val1, val0) );
8532 }
8533 else if( SCIPisFeasZero(scip, rhs) )
8534 {
8535 val0 = vals[0];
8536 val1 = vals[1];
8537
8538 /* check whether the two variables are in a variable bound relation */
8539 SCIP_CALL( detectVarboundSOS1(scip, conshdlrdata, var0, var1, val0, val1) );
8540 SCIP_CALL( detectVarboundSOS1(scip, conshdlrdata, var1, var0, val1, val0) );
8541 }
8542 }
8543 }
8544 }
8545
8546 return SCIP_OKAY;
8547 }
8548
8549
8550 /** switch to SOS1 branching and separating bound iniqualities from SOS1 constraints if the SOS1 constraints do not overlap */
8551 static
checkSwitchNonoverlappingSOS1Methods(SCIP * scip,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_DIGRAPH * conflictgraph,SCIP_CONS ** conss,int nconss)8552 SCIP_RETCODE checkSwitchNonoverlappingSOS1Methods(
8553 SCIP* scip, /**< SCIP pointer */
8554 SCIP_CONSHDLRDATA* conshdlrdata, /**< SOS1 constraint handler data */
8555 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
8556 SCIP_CONS** conss, /**< SOS1 constraints */
8557 int nconss /**< number of SOS1 constraints */
8558 )
8559 {
8560 SCIP_Bool nonoverlap = TRUE;
8561 int c;
8562
8563 /* loop through all SOS1 constraints */
8564 if ( conshdlrdata->nsos1vars > 0 )
8565 {
8566 for (c = 0; c < nconss && nonoverlap; ++c)
8567 {
8568 SCIP_CONSDATA* consdata;
8569 SCIP_VAR** vars;
8570 int notfixed = 0;
8571 int nvars;
8572 int i;
8573
8574 assert( conss[c] != NULL );
8575
8576 /* get constraint data field of the constraint */
8577 consdata = SCIPconsGetData(conss[c]);
8578 assert( consdata != NULL );
8579
8580 /* get variables and number of variables of constraint */
8581 nvars = consdata->nvars;
8582 vars = consdata->vars;
8583
8584 /* get number of variables of SOS1 constraint that are not fixed to zero */
8585 for (i = 0; i < nvars; ++i)
8586 {
8587 if ( ! SCIPisFeasZero(scip, SCIPvarGetLbLocal(vars[i])) || ! SCIPisFeasZero(scip, SCIPvarGetUbLocal(vars[i])) )
8588 ++notfixed;
8589 }
8590
8591 /* check variables of SOS1 constraint */
8592 for (i = 0; i < nvars; ++i)
8593 {
8594 int node;
8595
8596 assert( vars[i] != NULL );
8597
8598 node = varGetNodeSOS1(conshdlrdata, vars[i]);
8599 assert( node >= 0 || ( SCIPisFeasZero(scip, SCIPvarGetLbLocal(vars[i])) && SCIPisFeasZero(scip, SCIPvarGetUbLocal(vars[i]))) );
8600 assert( node < conshdlrdata->nsos1vars );
8601 assert( node < 0 || SCIPdigraphGetNSuccessors(conflictgraph, node) >= notfixed-1 );
8602 if ( node >= 0 && SCIPdigraphGetNSuccessors(conflictgraph, node) > notfixed-1 )
8603 {
8604 nonoverlap = FALSE;
8605 break;
8606 }
8607 }
8608 }
8609 }
8610
8611 /* if the SOS1 constraints do not overlap */
8612 if ( nonoverlap )
8613 {
8614 if ( conshdlrdata->autosos1branch )
8615 {
8616 conshdlrdata->switchsos1branch = TRUE;
8617 SCIPdebugMsg(scip, "Switched to SOS1 branching, since the SOS1 constraints do not overlap\n");
8618 }
8619
8620 if ( conshdlrdata->autocutsfromsos1 )
8621 {
8622 conshdlrdata->switchcutsfromsos1 = TRUE;
8623 SCIPdebugMsg(scip, "Switched to separating bound cuts from SOS1 constraints (and not from the conflict graph), since the SOS1 constraints do not overlap\n");
8624 }
8625 }
8626
8627 return SCIP_OKAY;
8628 }
8629
8630
8631 /** sets node data of conflict graph nodes */
8632 static
computeNodeDataSOS1(SCIP * scip,SCIP_CONSHDLRDATA * conshdlrdata,int nsos1vars)8633 SCIP_RETCODE computeNodeDataSOS1(
8634 SCIP* scip, /**< SCIP pointer */
8635 SCIP_CONSHDLRDATA* conshdlrdata, /**< SOS1 constraint handler data */
8636 int nsos1vars /**< number of SOS1 variables */
8637 )
8638 {
8639 SCIP_CONSHDLR* linconshdlr;
8640 SCIP_CONS** linconss;
8641 int nlinconss;
8642
8643 /* if no SOS1 variables exist -> exit */
8644 if ( nsos1vars == 0 )
8645 return SCIP_OKAY;
8646
8647 /* get constraint handler data of linear constraints */
8648 linconshdlr = SCIPfindConshdlr(scip, "linear");
8649 if ( linconshdlr == NULL )
8650 return SCIP_OKAY;
8651
8652 /* get linear constraints and number of linear constraints */
8653 nlinconss = SCIPconshdlrGetNConss(linconshdlr);
8654 linconss = SCIPconshdlrGetConss(linconshdlr);
8655
8656 /* check linear constraints for variable bound constraints */
8657 SCIP_CALL( checkLinearConssVarboundSOS1(scip, conshdlrdata, linconss, nlinconss) );
8658
8659 /* for each connected component of the conflict graph check whether all the variables correspond to a unique variable
8660 * upper bound variable */
8661 SCIP_CALL( checkConComponentsVarbound(scip, conshdlrdata->conflictgraph, conshdlrdata->nsos1vars, TRUE) );
8662 SCIP_CALL( checkConComponentsVarbound(scip, conshdlrdata->conflictgraph, conshdlrdata->nsos1vars, FALSE) );
8663
8664 return SCIP_OKAY;
8665 }
8666
8667
8668 /** initialize conflictgraph and create hashmap for SOS1 variables */
8669 static
initConflictgraph(SCIP * scip,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_CONS ** conss,int nconss)8670 SCIP_RETCODE initConflictgraph(
8671 SCIP* scip, /**< SCIP pointer */
8672 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
8673 SCIP_CONS** conss, /**< SOS1 constraints */
8674 int nconss /**< number of SOS1 constraints */
8675 )
8676 {
8677 SCIP_Bool* nodecreated; /* nodecreated[i] = TRUE if a node in the conflict graph is already created for index i
8678 * (with i index of the original variables) */
8679 int* nodeorig; /* nodeorig[i] = node of original variable x_i in the conflict graph */
8680 int ntotalvars;
8681 int cntsos;
8682 int i;
8683 int j;
8684 int c;
8685
8686 assert( conshdlrdata != NULL );
8687 assert( nconss == 0 || conss != NULL );
8688
8689 /* get the number of original problem variables */
8690 ntotalvars = SCIPgetNTotalVars(scip);
8691
8692 /* initialize vector 'nodecreated' */
8693 SCIP_CALL( SCIPallocBufferArray(scip, &nodeorig, ntotalvars) );
8694 SCIP_CALL( SCIPallocBufferArray(scip, &nodecreated, ntotalvars) );
8695 for (i = 0; i < ntotalvars; ++i)
8696 nodecreated[i] = FALSE;
8697
8698 /* compute number of SOS1 variables */
8699 cntsos = 0;
8700 for (c = 0; c < nconss; ++c)
8701 {
8702 SCIP_CONSDATA* consdata;
8703 SCIP_VAR** vars;
8704 int nvars;
8705
8706 assert( conss[c] != NULL );
8707
8708 /* get constraint data field of the constraint */
8709 consdata = SCIPconsGetData(conss[c]);
8710 assert( consdata != NULL );
8711
8712 /* get variables and number of variables of constraint */
8713 nvars = consdata->nvars;
8714 vars = consdata->vars;
8715
8716 /* update number of SOS1 variables */
8717 for (i = 0; i < nvars; ++i)
8718 {
8719 SCIP_VAR* var;
8720
8721 var = vars[i];
8722
8723 /* if the variable is not fixed to zero */
8724 if ( ! SCIPisFeasZero(scip, SCIPvarGetLbLocal(var)) || ! SCIPisFeasZero(scip, SCIPvarGetUbLocal(var)) )
8725 {
8726 int ind;
8727
8728 ind = SCIPvarGetIndex(var);
8729 assert( ind >= 0 && ind < ntotalvars );
8730 if ( ! nodecreated[ind] )
8731 {
8732 nodecreated[ind] = TRUE; /* mark node as counted */
8733 nodeorig[ind] = cntsos;
8734 ++cntsos;
8735 }
8736 }
8737 }
8738 }
8739 if ( cntsos <= 0 )
8740 {
8741 /* free buffer arrays */
8742 SCIPfreeBufferArray(scip, &nodecreated);
8743 SCIPfreeBufferArray(scip, &nodeorig);
8744 conshdlrdata->nsos1vars = 0;
8745 return SCIP_OKAY;
8746 }
8747
8748 /* reinitialize vector 'nodecreated' */
8749 for (i = 0; i < ntotalvars; ++i)
8750 nodecreated[i] = FALSE;
8751
8752 /* create conflict graph */
8753 SCIP_CALL( SCIPcreateDigraph(scip, &conshdlrdata->conflictgraph, cntsos) );
8754
8755 /* set up hash map */
8756 SCIP_CALL( SCIPhashmapCreate(&conshdlrdata->varhash, SCIPblkmem(scip), cntsos) );
8757
8758 /* for every SOS1 constraint */
8759 cntsos = 0;
8760 for (c = 0; c < nconss; ++c)
8761 {
8762 SCIP_CONSDATA* consdata;
8763 SCIP_VAR** vars;
8764 int nvars;
8765
8766 assert( conss[c] != NULL );
8767
8768 /* get constraint data field of the constraint */
8769 consdata = SCIPconsGetData(conss[c]);
8770 assert( consdata != NULL );
8771
8772 /* get variables and number of variables of constraint */
8773 nvars = consdata->nvars;
8774 vars = consdata->vars;
8775
8776 /* add edges to the conflict graph and create node data for each of its nodes */
8777 for (i = 0; i < nvars; ++i)
8778 {
8779 SCIP_VAR* var;
8780
8781 var = vars[i];
8782
8783 /* if the variable is not fixed to zero */
8784 if ( ! SCIPisFeasZero(scip, SCIPvarGetLbLocal(var)) || ! SCIPisFeasZero(scip, SCIPvarGetUbLocal(var)) )
8785 {
8786 int indi;
8787
8788 indi = SCIPvarGetIndex(var);
8789
8790 if ( ! nodecreated[indi] )
8791 {
8792 SCIP_NODEDATA* nodedata = NULL;
8793
8794 /* insert node number to hash map */
8795 assert( ! SCIPhashmapExists(conshdlrdata->varhash, var) );
8796 SCIP_CALL( SCIPhashmapInsertInt(conshdlrdata->varhash, var, cntsos) );
8797 assert( cntsos == SCIPhashmapGetImageInt(conshdlrdata->varhash, var) );
8798 assert( SCIPhashmapExists(conshdlrdata->varhash, var) );
8799
8800 /* create node data */
8801 SCIP_CALL( SCIPallocBlockMemory(scip, &nodedata) );
8802 nodedata->var = var;
8803 nodedata->lbboundvar = NULL;
8804 nodedata->ubboundvar = NULL;
8805 nodedata->lbboundcoef = 0.0;
8806 nodedata->ubboundcoef = 0.0;
8807 nodedata->lbboundcomp = FALSE;
8808 nodedata->ubboundcomp = FALSE;
8809
8810 /* set node data */
8811 SCIPdigraphSetNodeData(conshdlrdata->conflictgraph, (void*)nodedata, cntsos);
8812
8813 /* mark node and var data of node as created and update SOS1 counter */
8814 nodecreated[indi] = TRUE;
8815 ++cntsos;
8816 }
8817
8818 /* add edges to the conflict graph */
8819 for (j = i+1; j < nvars; ++j)
8820 {
8821 var = vars[j];
8822
8823 /* if the variable is not fixed to zero */
8824 if ( ! SCIPisFeasZero(scip, SCIPvarGetLbLocal(var)) || ! SCIPisFeasZero(scip, SCIPvarGetUbLocal(var)) )
8825 {
8826 int indj;
8827
8828 indj = SCIPvarGetIndex(var);
8829
8830 /* in case indi = indj the variable will be deleted in the presolving step */
8831 if ( indi != indj )
8832 {
8833 /* arcs have to be added 'safe' */
8834 SCIP_CALL( SCIPdigraphAddArcSafe(conshdlrdata->conflictgraph, nodeorig[indi], nodeorig[indj], NULL) );
8835 SCIP_CALL( SCIPdigraphAddArcSafe(conshdlrdata->conflictgraph, nodeorig[indj], nodeorig[indi], NULL) );
8836 }
8837 }
8838 }
8839 }
8840 }
8841 }
8842
8843 /* set number of problem variables that are contained in at least one SOS1 constraint */
8844 conshdlrdata->nsos1vars = cntsos;
8845
8846 /* free buffer arrays */
8847 SCIPfreeBufferArray(scip, &nodecreated);
8848 SCIPfreeBufferArray(scip, &nodeorig);
8849
8850 /* sort successors in ascending order */
8851 for (j = 0; j < conshdlrdata->nsos1vars; ++j)
8852 {
8853 int nsucc;
8854
8855 nsucc = SCIPdigraphGetNSuccessors(conshdlrdata->conflictgraph, j);
8856 SCIPsortInt(SCIPdigraphGetSuccessors(conshdlrdata->conflictgraph, j), nsucc);
8857 }
8858
8859 return SCIP_OKAY;
8860 }
8861
8862
8863 /** free conflict graph, nodedata and hashmap */
8864 static
freeConflictgraph(SCIP * scip,SCIP_CONSHDLRDATA * conshdlrdata)8865 SCIP_RETCODE freeConflictgraph(
8866 SCIP* scip, /**< SCIP pointer */
8867 SCIP_CONSHDLRDATA* conshdlrdata /**< constraint handler data */
8868 )
8869 {
8870 int j;
8871
8872 if ( conshdlrdata->conflictgraph == NULL )
8873 {
8874 assert( conshdlrdata->nsos1vars == 0 );
8875 return SCIP_OKAY;
8876 }
8877
8878 /* for every SOS1 variable */
8879 assert( conshdlrdata->nsos1vars > 0 );
8880 for (j = 0; j < conshdlrdata->nsos1vars; ++j)
8881 {
8882 SCIP_NODEDATA* nodedata;
8883
8884 /* get node data */
8885 assert( conshdlrdata->conflictgraph != NULL );
8886 nodedata = (SCIP_NODEDATA*)SCIPdigraphGetNodeData(conshdlrdata->conflictgraph, j);
8887 assert( nodedata != NULL );
8888
8889 /* free node data */
8890 SCIPfreeBlockMemory(scip, &nodedata);
8891 SCIPdigraphSetNodeData(conshdlrdata->conflictgraph, NULL, j);
8892 }
8893
8894 /* free conflict graph and hash map */
8895 assert( conshdlrdata->varhash != NULL );
8896 SCIPhashmapFree(&conshdlrdata->varhash);
8897 SCIPdigraphFree(&conshdlrdata->conflictgraph);
8898 conshdlrdata->nsos1vars = 0;
8899
8900 assert( conshdlrdata->varhash == NULL );
8901 assert( conshdlrdata->conflictgraph == NULL );
8902
8903 return SCIP_OKAY;
8904 }
8905
8906
8907 /* ---------------------------- constraint handler callback methods ----------------------*/
8908
8909 /** copy method for constraint handler plugins (called when SCIP copies plugins) */
8910 static
SCIP_DECL_CONSHDLRCOPY(conshdlrCopySOS1)8911 SCIP_DECL_CONSHDLRCOPY(conshdlrCopySOS1)
8912 { /*lint --e{715}*/
8913 assert( scip != NULL );
8914 assert( conshdlr != NULL );
8915 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
8916
8917 /* call inclusion method of constraint handler */
8918 SCIP_CALL( SCIPincludeConshdlrSOS1(scip) );
8919
8920 *valid = TRUE;
8921
8922 return SCIP_OKAY;
8923 }
8924
8925
8926 /** destructor of constraint handler to free constraint handler data (called when SCIP is exiting) */
8927 static
SCIP_DECL_CONSFREE(consFreeSOS1)8928 SCIP_DECL_CONSFREE(consFreeSOS1)
8929 {
8930 SCIP_CONSHDLRDATA* conshdlrdata;
8931
8932 assert( scip != NULL );
8933 assert( conshdlr != NULL );
8934 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
8935
8936 conshdlrdata = SCIPconshdlrGetData(conshdlr);
8937 assert(conshdlrdata != NULL);
8938
8939 /* free stack of variables fixed to nonzero (usually already freed in consExitsolSOS1 unless instance was solved during presolving) */
8940 SCIPfreeBlockMemoryArrayNull(scip, &conshdlrdata->fixnonzerovars, conshdlrdata->maxnfixnonzerovars); /*lint !e737*/
8941
8942 SCIPfreeBlockMemory(scip, &conshdlrdata);
8943
8944 return SCIP_OKAY;
8945 }
8946
8947
8948 /** solving process initialization method of constraint handler (called when branch and bound process is about to begin) */
8949 static
SCIP_DECL_CONSINITSOL(consInitsolSOS1)8950 SCIP_DECL_CONSINITSOL(consInitsolSOS1)
8951 { /*lint --e{715}*/
8952 SCIP_CONSHDLRDATA* conshdlrdata;
8953
8954 assert( scip != NULL );
8955 assert( conshdlr != NULL );
8956 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
8957
8958 conshdlrdata = SCIPconshdlrGetData(conshdlr);
8959 assert( conshdlrdata != NULL );
8960
8961 conshdlrdata->nsos1vars = 0;
8962 conshdlrdata->varhash = NULL;
8963
8964 if ( nconss > 0 )
8965 {
8966 /* initialize conflict graph and hashmap for SOS1 variables */
8967 SCIP_CALL( initConflictgraph(scip, conshdlrdata, conss, nconss) );
8968
8969 /* add data to conflict graph nodes */
8970 SCIP_CALL( computeNodeDataSOS1(scip, conshdlrdata, conshdlrdata->nsos1vars) );
8971
8972 if ( ( conshdlrdata->autosos1branch || conshdlrdata->autocutsfromsos1 )
8973 && ( ! conshdlrdata->switchsos1branch || ! conshdlrdata->switchcutsfromsos1 )
8974 )
8975 {
8976 /* switch to nonoverlapping methods if the SOS1 constraints do not overlap */
8977 SCIP_CALL( checkSwitchNonoverlappingSOS1Methods(scip, conshdlrdata, conshdlrdata->conflictgraph, conss, nconss) );
8978 }
8979
8980 /* initialize tclique graph */
8981 SCIP_CALL( initTCliquegraph(scip, conshdlr, conshdlrdata, conshdlrdata->conflictgraph, conshdlrdata->nsos1vars) );
8982
8983 /* create local conflict graph if needed */
8984 if ( conshdlrdata->addcomps )
8985 {
8986 SCIP_CALL( SCIPcreateDigraph(scip, &conshdlrdata->localconflicts, conshdlrdata->nsos1vars) );
8987 }
8988
8989 /* initialize stack of variables fixed to nonzero (memory may be already allocated in consTransSOS1()) */
8990 if ( conshdlrdata->fixnonzerovars == NULL )
8991 {
8992 conshdlrdata->maxnfixnonzerovars = conshdlrdata->nsos1vars;
8993 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &conshdlrdata->fixnonzerovars, conshdlrdata->maxnfixnonzerovars) );
8994 }
8995 }
8996
8997 return SCIP_OKAY;
8998 }
8999
9000
9001 /** solving process deinitialization method of constraint handler (called before branch and bound process data is freed) */
9002 static
SCIP_DECL_CONSEXITSOL(consExitsolSOS1)9003 SCIP_DECL_CONSEXITSOL(consExitsolSOS1)
9004 { /*lint --e{715}*/
9005 SCIP_CONSHDLRDATA* conshdlrdata;
9006 int c;
9007
9008 assert( scip != NULL );
9009 assert( conshdlr != NULL );
9010 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
9011 conshdlrdata = SCIPconshdlrGetData(conshdlr);
9012 assert( conshdlrdata != NULL );
9013
9014 /* check each constraint */
9015 for (c = 0; c < nconss; ++c)
9016 {
9017 SCIP_CONSDATA* consdata;
9018
9019 assert( conss != NULL );
9020 assert( conss[c] != NULL );
9021 consdata = SCIPconsGetData(conss[c]);
9022 assert( consdata != NULL );
9023
9024 SCIPdebugMsg(scip, "Exiting SOS1 constraint <%s>.\n", SCIPconsGetName(conss[c]) );
9025
9026 /* free rows */
9027 if ( consdata->rowub != NULL )
9028 {
9029 SCIP_CALL( SCIPreleaseRow(scip, &consdata->rowub) );
9030 }
9031
9032 if ( consdata->rowlb != NULL )
9033 {
9034 SCIP_CALL( SCIPreleaseRow(scip, &consdata->rowlb) );
9035 }
9036 }
9037
9038 /* free implication graph */
9039 if ( conshdlrdata->implgraph != NULL )
9040 {
9041 SCIP_CALL( freeImplGraphSOS1(scip, conshdlrdata) );
9042 }
9043 assert( conshdlrdata->implgraph == NULL );
9044
9045 /* free tclique graph and tclique data */
9046 if ( conshdlrdata->tcliquegraph != NULL )
9047 {
9048 assert( conshdlrdata->tcliquedata != NULL );
9049 SCIPfreeBlockMemory(scip, &conshdlrdata->tcliquedata);
9050 tcliqueFree(&conshdlrdata->tcliquegraph);
9051 }
9052 assert(conshdlrdata->tcliquegraph == NULL);
9053 assert(conshdlrdata->tcliquedata == NULL);
9054
9055 /* free stack of variables fixed to nonzero */
9056 SCIPfreeBlockMemoryArrayNull(scip, &conshdlrdata->fixnonzerovars, conshdlrdata->maxnfixnonzerovars); /*lint !e737*/
9057 conshdlrdata->nfixnonzerovars = 0;
9058 conshdlrdata->maxnfixnonzerovars = 0;
9059
9060 /* free graph for storing local conflicts */
9061 if ( conshdlrdata->localconflicts != NULL )
9062 SCIPdigraphFree(&conshdlrdata->localconflicts);
9063 assert( conshdlrdata->localconflicts == NULL );
9064
9065 /* free conflict graph */
9066 SCIP_CALL( freeConflictgraph(scip, conshdlrdata) );
9067 assert( conshdlrdata->conflictgraph == NULL );
9068
9069 return SCIP_OKAY;
9070 }
9071
9072
9073 /** frees specific constraint data */
9074 static
SCIP_DECL_CONSDELETE(consDeleteSOS1)9075 SCIP_DECL_CONSDELETE(consDeleteSOS1)
9076 {
9077 assert( scip != NULL );
9078 assert( conshdlr != NULL );
9079 assert( cons != NULL );
9080 assert( consdata != NULL );
9081 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
9082
9083 SCIPdebugMsg(scip, "Deleting SOS1 constraint <%s>.\n", SCIPconsGetName(cons) );
9084
9085 /* drop events on transformed variables */
9086 if ( SCIPconsIsTransformed(cons) )
9087 {
9088 SCIP_CONSHDLRDATA* conshdlrdata;
9089 int j;
9090
9091 /* get constraint handler data */
9092 conshdlrdata = SCIPconshdlrGetData(conshdlr);
9093 assert( conshdlrdata != NULL );
9094 assert( conshdlrdata->eventhdlr != NULL );
9095
9096 for (j = 0; j < (*consdata)->nvars; ++j)
9097 {
9098 SCIP_CALL( SCIPdropVarEvent(scip, (*consdata)->vars[j], EVENTHDLR_EVENT_TYPE, conshdlrdata->eventhdlr,
9099 (SCIP_EVENTDATA*)cons, -1) ); /*lint !e737 !e740*/
9100 }
9101 }
9102
9103 SCIPfreeBlockMemoryArray(scip, &(*consdata)->vars, (*consdata)->maxvars);
9104 if ( (*consdata)->weights != NULL )
9105 {
9106 SCIPfreeBlockMemoryArray(scip, &(*consdata)->weights, (*consdata)->maxvars);
9107 }
9108
9109 /* free rows */
9110 if ( (*consdata)->rowub != NULL )
9111 {
9112 SCIP_CALL( SCIPreleaseRow(scip, &(*consdata)->rowub) );
9113 }
9114 if ( (*consdata)->rowlb != NULL )
9115 {
9116 SCIP_CALL( SCIPreleaseRow(scip, &(*consdata)->rowlb) );
9117 }
9118 assert( (*consdata)->rowub == NULL );
9119 assert( (*consdata)->rowlb == NULL );
9120
9121 SCIPfreeBlockMemory(scip, consdata);
9122
9123 return SCIP_OKAY;
9124 }
9125
9126
9127 /** transforms constraint data into data belonging to the transformed problem */
9128 static
SCIP_DECL_CONSTRANS(consTransSOS1)9129 SCIP_DECL_CONSTRANS(consTransSOS1)
9130 {
9131 SCIP_CONSDATA* consdata;
9132 SCIP_CONSHDLRDATA* conshdlrdata;
9133 SCIP_CONSDATA* sourcedata;
9134 char s[SCIP_MAXSTRLEN];
9135 int j;
9136
9137 assert( scip != NULL );
9138 assert( conshdlr != NULL );
9139 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
9140 assert( sourcecons != NULL );
9141 assert( targetcons != NULL );
9142
9143 /* get constraint handler data */
9144 conshdlrdata = SCIPconshdlrGetData(conshdlr);
9145 assert( conshdlrdata != NULL );
9146 assert( conshdlrdata->eventhdlr != NULL );
9147
9148 SCIPdebugMsg(scip, "Transforming SOS1 constraint: <%s>.\n", SCIPconsGetName(sourcecons) );
9149
9150 /* get data of original constraint */
9151 sourcedata = SCIPconsGetData(sourcecons);
9152 assert( sourcedata != NULL );
9153 assert( sourcedata->nvars > 0 );
9154 assert( sourcedata->nvars <= sourcedata->maxvars );
9155
9156 /* initialize stack of variables fixed to nonzero */
9157 if ( conshdlrdata->fixnonzerovars == NULL )
9158 {
9159 conshdlrdata->maxnfixnonzerovars = SCIPgetNTotalVars(scip);
9160 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &conshdlrdata->fixnonzerovars, conshdlrdata->maxnfixnonzerovars) );
9161 }
9162
9163 /* create constraint data */
9164 SCIP_CALL( SCIPallocBlockMemory(scip, &consdata) );
9165
9166 consdata->nvars = sourcedata->nvars;
9167 consdata->maxvars = sourcedata->nvars;
9168 consdata->rowub = NULL;
9169 consdata->rowlb = NULL;
9170 consdata->nfixednonzeros = 0;
9171 consdata->local = sourcedata->local;
9172
9173 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->vars, consdata->nvars) );
9174
9175 /* if weights were used */
9176 if ( sourcedata->weights != NULL )
9177 {
9178 SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &consdata->weights, sourcedata->weights, consdata->nvars) );
9179 }
9180 else
9181 consdata->weights = NULL;
9182
9183 for (j = 0; j < sourcedata->nvars; ++j)
9184 {
9185 assert( sourcedata->vars[j] != 0 );
9186 SCIP_CALL( SCIPgetTransformedVar(scip, sourcedata->vars[j], &(consdata->vars[j])) );
9187
9188 /* if variable is fixed to be nonzero */
9189 if ( SCIPisFeasPositive(scip, SCIPvarGetLbLocal(consdata->vars[j])) || SCIPisFeasNegative(scip, SCIPvarGetUbLocal(consdata->vars[j])) )
9190 ++(consdata->nfixednonzeros);
9191 }
9192
9193 /* create transformed constraint with the same flags */
9194 (void) SCIPsnprintf(s, SCIP_MAXSTRLEN, "t_%s", SCIPconsGetName(sourcecons));
9195 SCIP_CALL( SCIPcreateCons(scip, targetcons, s, conshdlr, consdata,
9196 SCIPconsIsInitial(sourcecons), SCIPconsIsSeparated(sourcecons),
9197 SCIPconsIsEnforced(sourcecons), SCIPconsIsChecked(sourcecons),
9198 SCIPconsIsPropagated(sourcecons), SCIPconsIsLocal(sourcecons),
9199 SCIPconsIsModifiable(sourcecons), SCIPconsIsDynamic(sourcecons),
9200 SCIPconsIsRemovable(sourcecons), SCIPconsIsStickingAtNode(sourcecons)) );
9201
9202 /* catch bound change events on variable */
9203 for (j = 0; j < consdata->nvars; ++j)
9204 {
9205 SCIP_CALL( SCIPcatchVarEvent(scip, consdata->vars[j], EVENTHDLR_EVENT_TYPE, conshdlrdata->eventhdlr,
9206 (SCIP_EVENTDATA*)*targetcons, NULL) ); /*lint !e740*/
9207 }
9208
9209 #ifdef SCIP_DEBUG
9210 if ( consdata->nfixednonzeros > 0 )
9211 {
9212 SCIPdebugMsg(scip, "constraint <%s> has %d variables fixed to be nonzero.\n", SCIPconsGetName(*targetcons),
9213 consdata->nfixednonzeros );
9214 }
9215 #endif
9216
9217 return SCIP_OKAY;
9218 }
9219
9220
9221 /** presolving method of constraint handler */
9222 static
SCIP_DECL_CONSPRESOL(consPresolSOS1)9223 SCIP_DECL_CONSPRESOL(consPresolSOS1)
9224 { /*lint --e{715}*/
9225 SCIP_CONSHDLRDATA* conshdlrdata;
9226 SCIPdebug( int oldnfixedvars = *nfixedvars; )
9227 SCIPdebug( int oldnchgbds = *nchgbds; )
9228 SCIPdebug( int oldndelconss = *ndelconss; )
9229 SCIPdebug( int oldnupgdconss = *nupgdconss; )
9230 int nremovedvars;
9231
9232 assert( scip != NULL );
9233 assert( conshdlr != NULL );
9234 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
9235 assert( result != NULL );
9236
9237 conshdlrdata = SCIPconshdlrGetData(conshdlr);
9238 assert( conshdlrdata != NULL );
9239
9240 SCIPdebugMsg(scip, "Presolving SOS1 constraints.\n");
9241
9242 *result = SCIP_DIDNOTRUN;
9243
9244 nremovedvars = 0;
9245
9246 /* only run if success if possible */
9247 if( nconss > 0 && ( nrounds == 0 || nnewfixedvars > 0 || nnewaggrvars > 0 || nnewchgbds > 0 ) )
9248 {
9249 SCIP_Bool** adjacencymatrix = NULL;
9250 SCIP_DIGRAPH* conflictgraph;
9251 SCIP_EVENTHDLR* eventhdlr;
9252 int nsos1vars;
9253 int i;
9254 int j;
9255
9256 *result = SCIP_DIDNOTFIND;
9257
9258 /* get constraint handler data */
9259 assert( SCIPconshdlrGetData(conshdlr) != NULL );
9260 eventhdlr = SCIPconshdlrGetData(conshdlr)->eventhdlr;
9261 assert( eventhdlr != NULL );
9262
9263 /* initialize conflict graph */
9264 SCIP_CALL( initConflictgraph(scip, conshdlrdata, conss, nconss));
9265
9266 /* get conflict graph and number of SOS1 variables */
9267 conflictgraph = conshdlrdata->conflictgraph;
9268 nsos1vars = conshdlrdata->nsos1vars;
9269 if ( nsos1vars < 2 )
9270 {
9271 SCIP_CALL( freeConflictgraph(scip, conshdlrdata));
9272 return SCIP_OKAY;
9273 }
9274
9275 /* we do not create the adjacency matrix of the conflict graph if the number of SOS1 variables is larger than a predefined value */
9276 if ( conshdlrdata->maxsosadjacency == -1 || nsos1vars <= conshdlrdata->maxsosadjacency )
9277 {
9278 /* allocate buffer arrays for adjacency matrix */
9279 SCIP_CALL( SCIPallocBufferArray(scip, &adjacencymatrix, nsos1vars) );
9280 for (i = 0; i < nsos1vars; ++i)
9281 {
9282 SCIP_CALL( SCIPallocBufferArray(scip, &adjacencymatrix[i], i+1) );/*lint !e866*/
9283 }
9284
9285 /* create adjacency matrix */
9286 for (i = 0; i < nsos1vars; ++i)
9287 {
9288 for (j = 0; j < i+1; ++j)
9289 adjacencymatrix[i][j] = 0;
9290 }
9291 for (i = 0; i < nsos1vars; ++i)
9292 {
9293 int* succ;
9294 int nsucc;
9295
9296 succ = SCIPdigraphGetSuccessors(conflictgraph, i);
9297 nsucc = SCIPdigraphGetNSuccessors(conflictgraph, i);
9298
9299 for (j = 0; j < nsucc; ++j)
9300 {
9301 if ( i > succ[j] )
9302 adjacencymatrix[i][succ[j]] = 1;
9303 }
9304 }
9305 }
9306 else
9307 {
9308 SCIPdebugMsg(scip, "Adjacency matrix was not created since number of SOS1 variables (%d) is larger than %d.\n", nsos1vars, conshdlrdata->maxsosadjacency);
9309 }
9310
9311 /* perform one presolving round for SOS1 constraints */
9312 SCIP_CALL( presolRoundConssSOS1(scip, eventhdlr, conshdlrdata, conflictgraph, adjacencymatrix, conss, nconss, nsos1vars, naddconss, ndelconss, nupgdconss, nfixedvars, &nremovedvars, result) );
9313
9314 if ( adjacencymatrix != NULL )
9315 {
9316 /* perform one presolving round for SOS1 variables */
9317 if ( conshdlrdata->maxtightenbds != 0 && *result != SCIP_CUTOFF )
9318 {
9319 SCIP_CALL( presolRoundVarsSOS1(scip, conshdlrdata, conflictgraph, adjacencymatrix, nsos1vars, nfixedvars, nchgbds, naddconss, result) );
9320 }
9321
9322 /* free adjacency matrix */
9323 for (j = nsos1vars-1; j >= 0; --j)
9324 SCIPfreeBufferArrayNull(scip, &adjacencymatrix[j]);
9325 SCIPfreeBufferArrayNull(scip, &adjacencymatrix);
9326 }
9327
9328 /* free memory allocated in function initConflictgraph() */
9329 SCIP_CALL( freeConflictgraph(scip, conshdlrdata));
9330 }
9331 (*nchgcoefs) += nremovedvars;
9332
9333 SCIPdebug( SCIPdebugMsg(scip, "presolving fixed %d variables, changed %d bounds, removed %d variables, deleted %d constraints, and upgraded %d constraints.\n",
9334 *nfixedvars - oldnfixedvars, *nchgbds - oldnchgbds, nremovedvars, *ndelconss - oldndelconss, *nupgdconss - oldnupgdconss); )
9335
9336 return SCIP_OKAY;
9337 }
9338
9339
9340 /** LP initialization method of constraint handler (called before the initial LP relaxation at a node is solved) */
9341 static
SCIP_DECL_CONSINITLP(consInitlpSOS1)9342 SCIP_DECL_CONSINITLP(consInitlpSOS1)
9343 {
9344 SCIP_CONSHDLRDATA* conshdlrdata;
9345
9346 assert( scip != NULL );
9347 assert( conshdlr != NULL );
9348 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
9349
9350 conshdlrdata = SCIPconshdlrGetData(conshdlr);
9351 assert( conshdlrdata != NULL );
9352
9353 *infeasible = FALSE;
9354
9355 /* checking for initial rows for SOS1 constraints */
9356 if( conshdlrdata->boundcutsfromsos1 || conshdlrdata->switchcutsfromsos1 )
9357 {
9358 SCIP_CALL( initsepaBoundInequalityFromSOS1Cons(scip, conshdlr, conshdlrdata, conss, nconss, NULL, FALSE, -1, NULL, infeasible) );
9359 }
9360
9361 return SCIP_OKAY;
9362 }
9363
9364
9365 /** separation method of constraint handler for LP solutions */
9366 static
SCIP_DECL_CONSSEPALP(consSepalpSOS1)9367 SCIP_DECL_CONSSEPALP(consSepalpSOS1)
9368 { /*lint --e{715}*/
9369 assert( scip != NULL );
9370 assert( conshdlr != NULL );
9371 assert( conss != NULL );
9372 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
9373 assert( result != NULL );
9374
9375 SCIP_CALL( separateSOS1(scip, conshdlr, NULL, nconss, conss, result) );
9376
9377 return SCIP_OKAY;
9378 }
9379
9380
9381 /** separation method of constraint handler for arbitrary primal solutions */
9382 static
SCIP_DECL_CONSSEPASOL(consSepasolSOS1)9383 SCIP_DECL_CONSSEPASOL(consSepasolSOS1)
9384 { /*lint --e{715}*/
9385 assert( scip != NULL );
9386 assert( conshdlr != NULL );
9387 assert( conss != NULL );
9388 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
9389 assert( result != NULL );
9390
9391 SCIP_CALL( separateSOS1(scip, conshdlr, sol, nconss, conss, result) );
9392
9393 return SCIP_OKAY;
9394 }
9395
9396
9397 /** constraint enforcing method of constraint handler for LP solutions */
9398 static
SCIP_DECL_CONSENFOLP(consEnfolpSOS1)9399 SCIP_DECL_CONSENFOLP(consEnfolpSOS1)
9400 { /*lint --e{715}*/
9401 assert( scip != NULL );
9402 assert( conshdlr != NULL );
9403 assert( conss != NULL );
9404 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
9405 assert( result != NULL );
9406
9407 SCIP_CALL( enforceSOS1(scip, conshdlr, nconss, conss, NULL, result) );
9408
9409 return SCIP_OKAY;
9410 }
9411
9412
9413 /** constraint enforcing method of constraint handler for relaxation solutions */
9414 static
SCIP_DECL_CONSENFORELAX(consEnforelaxSOS1)9415 SCIP_DECL_CONSENFORELAX(consEnforelaxSOS1)
9416 { /*lint --e{715}*/
9417 assert( scip != NULL );
9418 assert( conshdlr != NULL );
9419 assert( conss != NULL );
9420 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
9421 assert( result != NULL );
9422
9423 SCIP_CALL( enforceSOS1(scip, conshdlr, nconss, conss, sol, result) );
9424
9425 return SCIP_OKAY;
9426 }
9427
9428
9429 /** constraint enforcing method of constraint handler for pseudo solutions */
9430 static
SCIP_DECL_CONSENFOPS(consEnfopsSOS1)9431 SCIP_DECL_CONSENFOPS(consEnfopsSOS1)
9432 { /*lint --e{715}*/
9433 assert( scip != NULL );
9434 assert( conshdlr != NULL );
9435 assert( conss != NULL );
9436 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
9437 assert( result != NULL );
9438
9439 SCIP_CALL( enforceSOS1(scip, conshdlr, nconss, conss, NULL, result) );
9440
9441 return SCIP_OKAY;
9442 }
9443
9444
9445 /** feasibility check method of constraint handler for integral solutions
9446 *
9447 * We simply check whether at most one variable is nonzero in the given solution.
9448 */
9449 static
SCIP_DECL_CONSCHECK(consCheckSOS1)9450 SCIP_DECL_CONSCHECK(consCheckSOS1)
9451 { /*lint --e{715}*/
9452 int c;
9453
9454 assert( scip != NULL );
9455 assert( conshdlr != NULL );
9456 assert( conss != NULL );
9457 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
9458 assert( result != NULL );
9459
9460 *result = SCIP_FEASIBLE;
9461
9462 /* check each constraint */
9463 for (c = 0; c < nconss && (*result == SCIP_FEASIBLE || completely); ++c)
9464 {
9465 SCIP_CONSDATA* consdata;
9466 int j;
9467 int cnt;
9468
9469 cnt = 0;
9470 assert( conss[c] != NULL );
9471 consdata = SCIPconsGetData(conss[c]);
9472 assert( consdata != NULL );
9473 SCIPdebugMsg(scip, "Checking SOS1 constraint <%s>.\n", SCIPconsGetName(conss[c]));
9474
9475 /* check all variables */
9476 for (j = 0; j < consdata->nvars; ++j)
9477 {
9478 /* if variable is nonzero */
9479 if ( ! SCIPisFeasZero(scip, SCIPgetSolVal(scip, sol, consdata->vars[j])) )
9480 {
9481 ++cnt;
9482
9483 /* if more than one variable is nonzero */
9484 if ( cnt > 1 )
9485 {
9486 SCIP_CALL( SCIPresetConsAge(scip, conss[c]) );
9487 *result = SCIP_INFEASIBLE;
9488
9489 /* update constraint violation in solution */
9490 if ( sol != NULL )
9491 SCIPupdateSolConsViolation(scip, sol, 1.0, 1.0);
9492
9493 if ( printreason )
9494 {
9495 int l;
9496
9497 SCIP_CALL( SCIPprintCons(scip, conss[c], NULL) );
9498 SCIPinfoMessage(scip, NULL, ";\nviolation: ");
9499
9500 for (l = 0; l < consdata->nvars; ++l)
9501 {
9502 /* if variable is nonzero */
9503 if ( ! SCIPisFeasZero(scip, SCIPgetSolVal(scip, sol, consdata->vars[l])) )
9504 {
9505 SCIPinfoMessage(scip, NULL, "<%s> = %.15g ",
9506 SCIPvarGetName(consdata->vars[l]), SCIPgetSolVal(scip, sol, consdata->vars[l]));
9507 }
9508 }
9509 SCIPinfoMessage(scip, NULL, "\n");
9510 }
9511 }
9512 }
9513 }
9514 }
9515
9516 return SCIP_OKAY;
9517 }
9518
9519
9520 /** domain propagation method of constraint handler */
9521 static
SCIP_DECL_CONSPROP(consPropSOS1)9522 SCIP_DECL_CONSPROP(consPropSOS1)
9523 { /*lint --e{715}*/
9524 SCIP_CONSHDLRDATA* conshdlrdata;
9525 SCIP_DIGRAPH* conflictgraph;
9526 SCIP_DIGRAPH* implgraph;
9527 int ngen = 0;
9528
9529 assert( scip != NULL );
9530 assert( conshdlr != NULL );
9531 assert( conss != NULL );
9532 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
9533 assert( result != NULL );
9534 assert( SCIPisTransformed(scip) );
9535
9536 /* return if number of SOS1 constraints is zero */
9537 if ( nconss < 1 )
9538 {
9539 *result = SCIP_DIDNOTRUN;
9540 return SCIP_OKAY;
9541 }
9542 *result = SCIP_DIDNOTFIND;
9543
9544 /* get constraint handler data */
9545 conshdlrdata = SCIPconshdlrGetData(conshdlr);
9546 assert( conshdlrdata != NULL );
9547
9548 /* get conflict graph */
9549 conflictgraph = conshdlrdata->conflictgraph;
9550
9551 /* get/initialize implication graph */
9552 implgraph = conshdlrdata->implgraph;
9553 if ( implgraph == NULL && conshdlrdata->implprop && conflictgraph != NULL )
9554 {
9555 if ( SCIPgetDepth(scip) == 0 )
9556 {
9557 SCIP_Bool success;
9558 SCIP_Bool cutoff;
9559 int nchbds;
9560
9561 SCIP_CALL( initImplGraphSOS1(scip, conshdlrdata, conflictgraph, conshdlrdata->nsos1vars, conshdlrdata->maxtightenbds, &nchbds, &cutoff, &success) );
9562 if ( ! success )
9563 conshdlrdata->implprop = FALSE;
9564
9565 if ( cutoff )
9566 {
9567 *result = SCIP_CUTOFF;
9568 return SCIP_OKAY;
9569 }
9570 else if ( nchbds > 0 )
9571 *result = SCIP_REDUCEDDOM;
9572 implgraph = conshdlrdata->implgraph;
9573 }
9574 else
9575 conshdlrdata->implprop = FALSE;
9576 }
9577
9578 /* if conflict graph propagation shall be used */
9579 if ( conshdlrdata->conflictprop && conflictgraph != NULL )
9580 {
9581 SCIP_VAR** fixnonzerovars;
9582 int nfixnonzerovars;
9583 int j;
9584
9585 assert( nconss > 0 );
9586
9587 /* stack of variables fixed to nonzero */
9588 nfixnonzerovars = conshdlrdata->nfixnonzerovars;
9589 fixnonzerovars = conshdlrdata->fixnonzerovars;
9590 assert( fixnonzerovars != NULL );
9591
9592 /* check each variable from stack */
9593 for (j = 0; j < nfixnonzerovars; ++j)
9594 {
9595 SCIP_VAR* var;
9596
9597 var = fixnonzerovars[j];
9598 if ( var != NULL )
9599 {
9600 int node;
9601 node = varGetNodeSOS1(conshdlrdata, var);
9602
9603 /* if variable is involved in an SOS1 constraint */
9604 if ( node >= 0 )
9605 {
9606 assert( varGetNodeSOS1(conshdlrdata, var) < conshdlrdata->nsos1vars );
9607 SCIPdebugMsg(scip, "Propagating SOS1 variable <%s>.\n", SCIPvarGetName(var) );
9608
9609 /* if zero is outside the domain of variable */
9610 if ( SCIPisFeasPositive(scip, SCIPvarGetLbLocal(var)) || SCIPisFeasNegative(scip, SCIPvarGetUbLocal(var)) )
9611 {
9612 SCIP_Bool cutoff;
9613
9614 SCIP_CALL( propVariableNonzero(scip, conflictgraph, implgraph, conss[0], node, conshdlrdata->implprop, &cutoff, &ngen) );
9615 if ( cutoff )
9616 {
9617 *result = SCIP_CUTOFF;
9618 return SCIP_OKAY;
9619 }
9620 }
9621 }
9622 }
9623 }
9624 }
9625 conshdlrdata->nfixnonzerovars = 0;
9626
9627 /* if SOS1 constraint propagation shall be used */
9628 if ( conshdlrdata->sosconsprop || conflictgraph == NULL )
9629 {
9630 int c;
9631
9632 /* check each constraint */
9633 for (c = 0; c < nconss; ++c)
9634 {
9635 SCIP_CONS* cons;
9636 SCIP_CONSDATA* consdata;
9637 SCIP_Bool cutoff;
9638
9639 assert( conss[c] != NULL );
9640 cons = conss[c];
9641 consdata = SCIPconsGetData(cons);
9642 assert( consdata != NULL );
9643 SCIPdebugMsg(scip, "Propagating SOS1 constraint <%s>.\n", SCIPconsGetName(cons) );
9644
9645 SCIP_CALL( propConsSOS1(scip, cons, consdata, &cutoff, &ngen) );
9646 if ( cutoff )
9647 {
9648 *result = SCIP_CUTOFF;
9649 return SCIP_OKAY;
9650 }
9651 }
9652 }
9653
9654 SCIPdebugMsg(scip, "Propagated %d domains.\n", ngen);
9655 if ( ngen > 0 )
9656 *result = SCIP_REDUCEDDOM;
9657
9658 return SCIP_OKAY;
9659 }
9660
9661
9662 /** propagation conflict resolving method of constraint handler
9663 *
9664 * We check which bound changes were the reason for infeasibility. We
9665 * use that @a inferinfo stores the index of the variable that has
9666 * bounds that fix it to be nonzero (these bounds are the reason). */
9667 static
SCIP_DECL_CONSRESPROP(consRespropSOS1)9668 SCIP_DECL_CONSRESPROP(consRespropSOS1)
9669 { /*lint --e{715}*/
9670 SCIP_VAR* var;
9671
9672 assert( scip != NULL );
9673 assert( cons != NULL );
9674 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
9675 assert( infervar != NULL );
9676 assert( bdchgidx != NULL );
9677 assert( result != NULL );
9678
9679 *result = SCIP_DIDNOTFIND;
9680 SCIPdebugMsg(scip, "Propagation resolution method of SOS1 constraint <%s>.\n", SCIPconsGetName(cons));
9681
9682 /* check whether conflict was detected in variable propagation or constraint propagation */
9683 if ( inferinfo < 0 )
9684 {
9685 SCIP_CONSHDLRDATA* conshdlrdata;
9686
9687 assert( conshdlr != NULL );
9688
9689 /* get constraint handler data */
9690 conshdlrdata = SCIPconshdlrGetData(conshdlr);
9691 assert( conshdlrdata != NULL );
9692 assert( conshdlrdata->conflictgraph != NULL );
9693 assert( inferinfo >= -conshdlrdata->maxnfixnonzerovars );
9694 assert( inferinfo >= -conshdlrdata->nsos1vars );
9695 assert( inferinfo <= -1 );
9696
9697 var = SCIPnodeGetVarSOS1(conshdlrdata->conflictgraph, -inferinfo - 1); /*lint !e2704*/
9698 }
9699 else
9700 {
9701 SCIP_CONSDATA* consdata;
9702
9703 /* get constraint data */
9704 consdata = SCIPconsGetData(cons);
9705 assert( consdata != NULL );
9706 assert( inferinfo < consdata->nvars );
9707
9708 var = consdata->vars[inferinfo];
9709 }
9710 assert( var != NULL );
9711 assert( var != infervar );
9712
9713 /* check if lower bound of var was the reason */
9714 if ( SCIPisFeasPositive(scip, SCIPgetVarLbAtIndex(scip, var, bdchgidx, FALSE)) )
9715 {
9716 SCIP_CALL( SCIPaddConflictLb(scip, var, bdchgidx) );
9717 *result = SCIP_SUCCESS;
9718 }
9719
9720 /* check if upper bound of var was the reason */
9721 if ( SCIPisFeasNegative(scip, SCIPgetVarUbAtIndex(scip, var, bdchgidx, FALSE)) )
9722 {
9723 SCIP_CALL( SCIPaddConflictUb(scip, var, bdchgidx) );
9724 *result = SCIP_SUCCESS;
9725 }
9726
9727 return SCIP_OKAY;
9728 }
9729
9730
9731 /** variable rounding lock method of constraint handler
9732 *
9733 * Let lb and ub be the lower and upper bounds of a
9734 * variable. Preprocessing usually makes sure that lb <= 0 <= ub.
9735 *
9736 * - If lb < 0 then rounding down may violate the constraint.
9737 * - If ub > 0 then rounding up may violated the constraint.
9738 * - If lb > 0 or ub < 0 then the constraint is infeasible and we do
9739 * not have to deal with it here.
9740 * - If lb == 0 then rounding down does not violate the constraint.
9741 * - If ub == 0 then rounding up does not violate the constraint.
9742 */
9743 static
SCIP_DECL_CONSLOCK(consLockSOS1)9744 SCIP_DECL_CONSLOCK(consLockSOS1)
9745 {
9746 SCIP_CONSDATA* consdata;
9747 SCIP_VAR** vars;
9748 int nvars;
9749 int j;
9750
9751 assert( scip != NULL );
9752 assert( conshdlr != NULL );
9753 assert( cons != NULL );
9754 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
9755 assert(locktype == SCIP_LOCKTYPE_MODEL);
9756
9757 consdata = SCIPconsGetData(cons);
9758 assert( consdata != NULL );
9759
9760 SCIPdebugMsg(scip, "Locking constraint <%s>.\n", SCIPconsGetName(cons));
9761
9762 vars = consdata->vars;
9763 nvars = consdata->nvars;
9764 assert( vars != NULL );
9765
9766 for (j = 0; j < nvars; ++j)
9767 {
9768 SCIP_VAR* var;
9769 var = vars[j];
9770
9771 /* if lower bound is negative, rounding down may violate constraint */
9772 if ( SCIPisFeasNegative(scip, SCIPvarGetLbGlobal(var)) )
9773 {
9774 SCIP_CALL( SCIPaddVarLocksType(scip, var, locktype, nlockspos, nlocksneg) );
9775 }
9776
9777 /* additionally: if upper bound is positive, rounding up may violate constraint */
9778 if ( SCIPisFeasPositive(scip, SCIPvarGetUbGlobal(var)) )
9779 {
9780 SCIP_CALL( SCIPaddVarLocksType(scip, var, locktype, nlocksneg, nlockspos) );
9781 }
9782 }
9783
9784 return SCIP_OKAY;
9785 }
9786
9787
9788 /** constraint display method of constraint handler */
9789 static
SCIP_DECL_CONSPRINT(consPrintSOS1)9790 SCIP_DECL_CONSPRINT(consPrintSOS1)
9791 { /*lint --e{715}*/
9792 SCIP_CONSDATA* consdata;
9793 int j;
9794
9795 assert( scip != NULL );
9796 assert( conshdlr != NULL );
9797 assert( cons != NULL );
9798 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
9799
9800 consdata = SCIPconsGetData(cons);
9801 assert( consdata != NULL );
9802
9803 for (j = 0; j < consdata->nvars; ++j)
9804 {
9805 if ( j > 0 )
9806 SCIPinfoMessage(scip, file, ", ");
9807 SCIP_CALL( SCIPwriteVarName(scip, file, consdata->vars[j], FALSE) );
9808 if ( consdata->weights == NULL )
9809 SCIPinfoMessage(scip, file, " (%d)", j+1);
9810 else
9811 SCIPinfoMessage(scip, file, " (%3.2f)", consdata->weights[j]);
9812 }
9813
9814 return SCIP_OKAY;
9815 }
9816
9817
9818 /** constraint copying method of constraint handler */
9819 static
SCIP_DECL_CONSCOPY(consCopySOS1)9820 SCIP_DECL_CONSCOPY(consCopySOS1)
9821 { /*lint --e{715}*/
9822 SCIP_CONSDATA* sourceconsdata;
9823 SCIP_VAR** sourcevars;
9824 SCIP_VAR** targetvars;
9825 SCIP_Real* targetweights = NULL;
9826 const char* consname;
9827 int nvars;
9828 int v;
9829
9830 assert( scip != NULL );
9831 assert( sourcescip != NULL );
9832 assert( sourcecons != NULL );
9833 assert( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(sourcecons)), CONSHDLR_NAME) == 0 );
9834 assert( valid != NULL );
9835
9836 *valid = TRUE;
9837
9838 if ( name != NULL )
9839 consname = name;
9840 else
9841 consname = SCIPconsGetName(sourcecons);
9842
9843 SCIPdebugMsg(scip, "Copying SOS1 constraint <%s> ...\n", consname);
9844
9845 sourceconsdata = SCIPconsGetData(sourcecons);
9846 assert( sourceconsdata != NULL );
9847
9848 /* get variables and weights of the source constraint */
9849 nvars = sourceconsdata->nvars;
9850 assert( nvars >= 0 );
9851
9852 /* duplicate weights array */
9853 if ( sourceconsdata->weights != NULL )
9854 {
9855 SCIP_CALL( SCIPduplicateBufferArray(sourcescip, &targetweights, sourceconsdata->weights, nvars) );
9856 }
9857
9858 /* get copied variables in target SCIP */
9859 sourcevars = sourceconsdata->vars;
9860 SCIP_CALL( SCIPallocBufferArray(sourcescip, &targetvars, nvars) );
9861 for (v = 0; v < nvars && *valid; ++v)
9862 {
9863 assert( sourcevars != NULL );
9864 SCIP_CALL( SCIPgetVarCopy(sourcescip, scip, sourcevars[v], &(targetvars[v]), varmap, consmap, global, valid) );
9865 }
9866
9867 /* only create the target constraint, if all variables were be copied */
9868 if ( *valid )
9869 {
9870 SCIP_CALL( SCIPcreateConsSOS1(scip, cons, consname, nvars, targetvars, targetweights,
9871 initial, separate, enforce, check, propagate, local, dynamic, removable, stickingatnode) );
9872 }
9873
9874 /* free buffer array */
9875 SCIPfreeBufferArray(sourcescip, &targetvars);
9876 SCIPfreeBufferArrayNull(sourcescip, &targetweights);
9877
9878 return SCIP_OKAY;
9879 }
9880
9881
9882 /** constraint parsing method of constraint handler */
9883 static
SCIP_DECL_CONSPARSE(consParseSOS1)9884 SCIP_DECL_CONSPARSE(consParseSOS1)
9885 { /*lint --e{715}*/
9886 SCIP_VAR* var;
9887 SCIP_Real weight;
9888 const char* s;
9889 char* t;
9890
9891 assert(scip != NULL);
9892 assert(conshdlr != NULL);
9893 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
9894 assert(cons != NULL);
9895 assert(success != NULL);
9896
9897 *success = TRUE;
9898 s = str;
9899
9900 /* create empty SOS1 constraint */
9901 SCIP_CALL( SCIPcreateConsSOS1(scip, cons, name, 0, NULL, NULL, initial, separate, enforce, check, propagate, local, dynamic, removable, stickingatnode) );
9902
9903 /* loop through string */
9904 do
9905 {
9906 /* parse variable name */
9907 SCIP_CALL( SCIPparseVarName(scip, s, &var, &t) );
9908 s = t;
9909
9910 /* skip until beginning of weight */
9911 while ( *s != '\0' && *s != '(' )
9912 ++s;
9913
9914 if ( *s == '\0' )
9915 {
9916 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "Syntax error: expected weight at input: %s\n", s);
9917 *success = FALSE;
9918 return SCIP_OKAY;
9919 }
9920 /* skip '(' */
9921 ++s;
9922
9923 /* find weight */
9924 weight = strtod(s, &t);
9925 if ( t == NULL )
9926 {
9927 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "Syntax error during parsing of the weight: %s\n", s);
9928 *success = FALSE;
9929 return SCIP_OKAY;
9930 }
9931 s = t;
9932
9933 /* skip white space, ',', and ')' */
9934 while ( *s != '\0' && ( isspace((unsigned char)*s) || *s == ',' || *s == ')' ) )
9935 ++s;
9936
9937 /* add variable */
9938 SCIP_CALL( SCIPaddVarSOS1(scip, *cons, var, weight) );
9939 }
9940 while ( *s != '\0' );
9941
9942 return SCIP_OKAY;
9943 }
9944
9945
9946 /** constraint method of constraint handler which returns the variables (if possible) */
9947 static
SCIP_DECL_CONSGETVARS(consGetVarsSOS1)9948 SCIP_DECL_CONSGETVARS(consGetVarsSOS1)
9949 { /*lint --e{715}*/
9950 SCIP_CONSDATA* consdata;
9951
9952 consdata = SCIPconsGetData(cons);
9953 assert(consdata != NULL);
9954
9955 if( varssize < consdata->nvars )
9956 (*success) = FALSE;
9957 else
9958 {
9959 assert(vars != NULL);
9960
9961 BMScopyMemoryArray(vars, consdata->vars, consdata->nvars);
9962 (*success) = TRUE;
9963 }
9964
9965 return SCIP_OKAY;
9966 }
9967
9968
9969 /** constraint method of constraint handler which returns the number of variables (if possible) */
9970 static
SCIP_DECL_CONSGETNVARS(consGetNVarsSOS1)9971 SCIP_DECL_CONSGETNVARS(consGetNVarsSOS1)
9972 { /*lint --e{715}*/
9973 SCIP_CONSDATA* consdata;
9974
9975 consdata = SCIPconsGetData(cons);
9976 assert(consdata != NULL);
9977
9978 (*nvars) = consdata->nvars;
9979 (*success) = TRUE;
9980
9981 return SCIP_OKAY;
9982 }
9983
9984
9985 /* ---------------- Callback methods of event handler ---------------- */
9986
9987 /** exec the event handler
9988 *
9989 * We update the number of variables fixed to be nonzero
9990 */
9991 static
SCIP_DECL_EVENTEXEC(eventExecSOS1)9992 SCIP_DECL_EVENTEXEC(eventExecSOS1)
9993 {
9994 SCIP_CONSHDLRDATA* conshdlrdata;
9995 SCIP_EVENTTYPE eventtype;
9996 SCIP_CONSHDLR* conshdlr;
9997 SCIP_CONSDATA* consdata;
9998 SCIP_CONS* cons;
9999 SCIP_VAR* var;
10000 SCIP_Real oldbound;
10001 SCIP_Real newbound;
10002
10003 assert( eventhdlr != NULL );
10004 assert( eventdata != NULL );
10005 assert( strcmp(SCIPeventhdlrGetName(eventhdlr), EVENTHDLR_NAME) == 0 );
10006 assert( event != NULL );
10007
10008 cons = (SCIP_CONS*)eventdata;
10009 assert( cons != NULL );
10010 consdata = SCIPconsGetData(cons);
10011 assert( 0 <= consdata->nfixednonzeros && consdata->nfixednonzeros <= consdata->nvars );
10012
10013 oldbound = SCIPeventGetOldbound(event);
10014 newbound = SCIPeventGetNewbound(event);
10015
10016 eventtype = SCIPeventGetType(event);
10017 switch ( eventtype )
10018 {
10019 case SCIP_EVENTTYPE_LBTIGHTENED:
10020 /* if variable is now fixed to be nonzero */
10021 if ( ! SCIPisFeasPositive(scip, oldbound) && SCIPisFeasPositive(scip, newbound) )
10022 {
10023 conshdlr = SCIPconsGetHdlr(cons);
10024 assert( conshdlr != NULL );
10025 conshdlrdata = SCIPconshdlrGetData(conshdlr);
10026 assert( conshdlrdata != NULL );
10027
10028 /* store variable fixed to be nonzero on stack */
10029 assert( 0 <= conshdlrdata->nfixnonzerovars && conshdlrdata->nfixnonzerovars <= SCIPgetNTotalVars(scip) );
10030 if ( conshdlrdata->nfixnonzerovars < conshdlrdata->maxnfixnonzerovars )
10031 {
10032 assert( conshdlrdata->fixnonzerovars != NULL );
10033 assert( SCIPeventGetVar(event) != NULL );
10034 conshdlrdata->fixnonzerovars[conshdlrdata->nfixnonzerovars++] = SCIPeventGetVar(event);
10035 }
10036
10037 ++(consdata->nfixednonzeros);
10038 }
10039 break;
10040
10041 case SCIP_EVENTTYPE_UBTIGHTENED:
10042 /* if variable is now fixed to be nonzero */
10043 if ( ! SCIPisFeasNegative(scip, oldbound) && SCIPisFeasNegative(scip, newbound) )
10044 {
10045 conshdlr = SCIPconsGetHdlr(cons);
10046 assert( conshdlr != NULL );
10047 conshdlrdata = SCIPconshdlrGetData(conshdlr);
10048 assert( conshdlrdata != NULL );
10049
10050 /* store variable fixed to be nonzero on stack */
10051 assert( 0 <= conshdlrdata->nfixnonzerovars && conshdlrdata->nfixnonzerovars <= SCIPgetNTotalVars(scip) );
10052 if ( conshdlrdata->nfixnonzerovars < conshdlrdata->maxnfixnonzerovars )
10053 {
10054 assert( conshdlrdata->fixnonzerovars != NULL );
10055 assert( SCIPeventGetVar(event) != NULL );
10056 conshdlrdata->fixnonzerovars[conshdlrdata->nfixnonzerovars++] = SCIPeventGetVar(event);
10057 }
10058
10059 ++(consdata->nfixednonzeros);
10060 }
10061 break;
10062
10063 case SCIP_EVENTTYPE_LBRELAXED:
10064 /* if variable is not fixed to be nonzero anymore */
10065 if ( SCIPisFeasPositive(scip, oldbound) && ! SCIPisFeasPositive(scip, newbound) )
10066 --(consdata->nfixednonzeros);
10067 break;
10068
10069 case SCIP_EVENTTYPE_UBRELAXED:
10070 /* if variable is not fixed to be nonzero anymore */
10071 if ( SCIPisFeasNegative(scip, oldbound) && ! SCIPisFeasNegative(scip, newbound) )
10072 --(consdata->nfixednonzeros);
10073 break;
10074
10075 case SCIP_EVENTTYPE_GLBCHANGED:
10076 var = SCIPeventGetVar(event);
10077 assert(var != NULL);
10078
10079 /* global lower bound is not negative anymore -> remove down lock */
10080 if ( SCIPisFeasNegative(scip, oldbound) && ! SCIPisFeasNegative(scip, newbound) )
10081 SCIP_CALL( SCIPunlockVarCons(scip, var, cons, TRUE, FALSE) );
10082 /* global lower bound turned negative -> add down lock */
10083 else if ( ! SCIPisFeasNegative(scip, oldbound) && SCIPisFeasNegative(scip, newbound) )
10084 SCIP_CALL( SCIPlockVarCons(scip, var, cons, TRUE, FALSE) );
10085 break;
10086
10087 case SCIP_EVENTTYPE_GUBCHANGED:
10088 var = SCIPeventGetVar(event);
10089 assert(var != NULL);
10090
10091 /* global upper bound is not positive anymore -> remove up lock */
10092 if ( SCIPisFeasPositive(scip, oldbound) && ! SCIPisFeasPositive(scip, newbound) )
10093 SCIP_CALL( SCIPunlockVarCons(scip, var, cons, FALSE, TRUE) );
10094 /* global upper bound turned positive -> add up lock */
10095 else if ( ! SCIPisFeasPositive(scip, oldbound) && SCIPisFeasPositive(scip, newbound) )
10096 SCIP_CALL( SCIPlockVarCons(scip, var, cons, FALSE, TRUE) );
10097 break;
10098
10099 default:
10100 SCIPerrorMessage("invalid event type.\n");
10101 return SCIP_INVALIDDATA;
10102 }
10103 assert( 0 <= consdata->nfixednonzeros && consdata->nfixednonzeros <= consdata->nvars );
10104
10105 SCIPdebugMsg(scip, "changed bound of variable <%s> from %f to %f (nfixednonzeros: %d).\n", SCIPvarGetName(SCIPeventGetVar(event)),
10106 oldbound, newbound, consdata->nfixednonzeros);
10107
10108 return SCIP_OKAY;
10109 }
10110
10111
10112 /** constraint handler method to determine a diving variable by assigning a variable and two values for diving */
10113 static
SCIP_DECL_CONSGETDIVEBDCHGS(consGetDiveBdChgsSOS1)10114 SCIP_DECL_CONSGETDIVEBDCHGS(consGetDiveBdChgsSOS1)
10115 {
10116 SCIP_CONSHDLRDATA* conshdlrdata;
10117
10118 assert( scip != NULL );
10119 assert( conshdlr != NULL );
10120 assert( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0 );
10121 assert( diveset != NULL );
10122 assert( success != NULL );
10123 assert( infeasible != NULL );
10124
10125 *infeasible = FALSE;
10126 *success = FALSE;
10127
10128 if ( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) != 0 )
10129 {
10130 SCIPerrorMessage("not an SOS1 constraint handler.\n");
10131 return SCIP_INVALIDDATA;
10132 }
10133 conshdlrdata = SCIPconshdlrGetData(conshdlr);
10134 assert( conshdlrdata != NULL );
10135
10136 /* if the SOS1 constraints do not overlap, we apply a faster method getDiveBdChgsSOS1constraints() that does not make use of the conflict graph;
10137 * for overlapping SOS1 constraints we apply the method getDiveBdChgsSOS1conflictgraph(), which then may produce better results (e.g. due to more
10138 * diving candidates) */
10139 if ( conshdlrdata->switchsos1branch )
10140 {
10141 SCIP_CALL( getDiveBdChgsSOS1constraints(scip, conshdlr, diveset, sol, success) );
10142 }
10143 else
10144 {
10145 SCIP_CALL( getDiveBdChgsSOS1conflictgraph(scip, conshdlr, diveset, sol, success) );
10146 }
10147
10148 return SCIP_OKAY;
10149 }
10150
10151
10152 /* ---------------- Constraint specific interface methods ---------------- */
10153
10154 /** creates the handler for SOS1 constraints and includes it in SCIP */
SCIPincludeConshdlrSOS1(SCIP * scip)10155 SCIP_RETCODE SCIPincludeConshdlrSOS1(
10156 SCIP* scip /**< SCIP data structure */
10157 )
10158 {
10159 SCIP_CONSHDLRDATA* conshdlrdata;
10160 SCIP_CONSHDLR* conshdlr;
10161
10162 /* create constraint handler data */
10163 SCIP_CALL( SCIPallocBlockMemory(scip, &conshdlrdata) );
10164 conshdlrdata->branchsos = TRUE;
10165 conshdlrdata->switchsos1branch = FALSE;
10166 conshdlrdata->switchcutsfromsos1 = FALSE;
10167 conshdlrdata->eventhdlr = NULL;
10168 conshdlrdata->fixnonzerovars = NULL;
10169 conshdlrdata->maxnfixnonzerovars = 0;
10170 conshdlrdata->nfixnonzerovars = 0;
10171 conshdlrdata->conflictgraph = NULL;
10172 conshdlrdata->localconflicts = NULL;
10173 conshdlrdata->isconflocal = FALSE;
10174 conshdlrdata->implgraph = NULL;
10175 conshdlrdata->nimplnodes = 0;
10176 conshdlrdata->nboundcuts = 0;
10177 conshdlrdata->tcliquegraph = NULL;
10178 conshdlrdata->tcliquedata = NULL;
10179 conshdlrdata->cntextsos1 = -1;
10180 conshdlrdata->varhash = NULL;
10181
10182 /* create event handler for bound change events */
10183 SCIP_CALL( SCIPincludeEventhdlrBasic(scip, &conshdlrdata->eventhdlr, EVENTHDLR_NAME, EVENTHDLR_DESC, eventExecSOS1, NULL) );
10184 if ( conshdlrdata->eventhdlr == NULL )
10185 {
10186 SCIPerrorMessage("event handler for SOS1 constraints not found.\n");
10187 return SCIP_PLUGINNOTFOUND;
10188 }
10189
10190 /* include constraint handler */
10191 SCIP_CALL( SCIPincludeConshdlrBasic(scip, &conshdlr, CONSHDLR_NAME, CONSHDLR_DESC,
10192 CONSHDLR_ENFOPRIORITY, CONSHDLR_CHECKPRIORITY, CONSHDLR_EAGERFREQ, CONSHDLR_NEEDSCONS,
10193 consEnfolpSOS1, consEnfopsSOS1, consCheckSOS1, consLockSOS1, conshdlrdata) );
10194 assert(conshdlr != NULL);
10195
10196 /* set non-fundamental callbacks via specific setter functions */
10197 SCIP_CALL( SCIPsetConshdlrCopy(scip, conshdlr, conshdlrCopySOS1, consCopySOS1) );
10198 SCIP_CALL( SCIPsetConshdlrDelete(scip, conshdlr, consDeleteSOS1) );
10199 SCIP_CALL( SCIPsetConshdlrGetDiveBdChgs(scip, conshdlr, consGetDiveBdChgsSOS1) );
10200 SCIP_CALL( SCIPsetConshdlrExitsol(scip, conshdlr, consExitsolSOS1) );
10201 SCIP_CALL( SCIPsetConshdlrInitsol(scip, conshdlr, consInitsolSOS1) );
10202 SCIP_CALL( SCIPsetConshdlrFree(scip, conshdlr, consFreeSOS1) );
10203 SCIP_CALL( SCIPsetConshdlrGetVars(scip, conshdlr, consGetVarsSOS1) );
10204 SCIP_CALL( SCIPsetConshdlrGetNVars(scip, conshdlr, consGetNVarsSOS1) );
10205 SCIP_CALL( SCIPsetConshdlrInitlp(scip, conshdlr, consInitlpSOS1) );
10206 SCIP_CALL( SCIPsetConshdlrParse(scip, conshdlr, consParseSOS1) );
10207 SCIP_CALL( SCIPsetConshdlrPresol(scip, conshdlr, consPresolSOS1, CONSHDLR_MAXPREROUNDS, CONSHDLR_PRESOLTIMING) );
10208 SCIP_CALL( SCIPsetConshdlrPrint(scip, conshdlr, consPrintSOS1) );
10209 SCIP_CALL( SCIPsetConshdlrProp(scip, conshdlr, consPropSOS1, CONSHDLR_PROPFREQ, CONSHDLR_DELAYPROP, CONSHDLR_PROP_TIMING) );
10210 SCIP_CALL( SCIPsetConshdlrResprop(scip, conshdlr, consRespropSOS1) );
10211 SCIP_CALL( SCIPsetConshdlrSepa(scip, conshdlr, consSepalpSOS1, consSepasolSOS1, CONSHDLR_SEPAFREQ, CONSHDLR_SEPAPRIORITY, CONSHDLR_DELAYSEPA) );
10212 SCIP_CALL( SCIPsetConshdlrTrans(scip, conshdlr, consTransSOS1) );
10213 SCIP_CALL( SCIPsetConshdlrEnforelax(scip, conshdlr, consEnforelaxSOS1) );
10214
10215 /* add SOS1 constraint handler parameters */
10216
10217 /* adjacency matrix parameters */
10218 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/maxsosadjacency",
10219 "do not create an adjacency matrix if number of SOS1 variables is larger than predefined value (-1: no limit)",
10220 &conshdlrdata->maxsosadjacency, TRUE, DEFAULT_MAXSOSADJACENCY, -1, INT_MAX, NULL, NULL) );
10221
10222 /* presolving parameters */
10223 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/maxextensions",
10224 "maximal number of extensions that will be computed for each SOS1 constraint (-1: no limit)",
10225 &conshdlrdata->maxextensions, TRUE, DEFAULT_MAXEXTENSIONS, -1, INT_MAX, NULL, NULL) );
10226
10227 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/maxtightenbds",
10228 "maximal number of bound tightening rounds per presolving round (-1: no limit)",
10229 &conshdlrdata->maxtightenbds, TRUE, DEFAULT_MAXTIGHTENBDS, -1, INT_MAX, NULL, NULL) );
10230
10231 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/perfimplanalysis",
10232 "if TRUE then perform implication graph analysis (might add additional SOS1 constraints)",
10233 &conshdlrdata->perfimplanalysis, TRUE, DEFAULT_PERFIMPLANALYSIS, NULL, NULL) );
10234
10235 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/depthimplanalysis",
10236 "number of recursive calls of implication graph analysis (-1: no limit)",
10237 &conshdlrdata->depthimplanalysis, TRUE, DEFAULT_DEPTHIMPLANALYSIS, -1, INT_MAX, NULL, NULL) );
10238
10239 /* propagation parameters */
10240 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/conflictprop",
10241 "whether to use conflict graph propagation",
10242 &conshdlrdata->conflictprop, TRUE, DEFAULT_CONFLICTPROP, NULL, NULL) );
10243
10244 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/implprop",
10245 "whether to use implication graph propagation",
10246 &conshdlrdata->implprop, TRUE, DEFAULT_IMPLPROP, NULL, NULL) );
10247
10248 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/sosconsprop",
10249 "whether to use SOS1 constraint propagation",
10250 &conshdlrdata->sosconsprop, TRUE, DEFAULT_SOSCONSPROP, NULL, NULL) );
10251
10252 /* branching parameters */
10253 SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/branchingrule",
10254 "which branching rule should be applied ? ('n': neighborhood, 'b': bipartite, 's': SOS1/clique) (note: in some cases an automatic switching to SOS1 branching is possible)",
10255 &conshdlrdata->branchingrule, TRUE, DEFAULT_BRANCHINGRULE, DEFAULT_BRANCHSTRATEGIES, NULL, NULL) );
10256
10257 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/autosos1branch",
10258 "if TRUE then automatically switch to SOS1 branching if the SOS1 constraints do not overlap",
10259 &conshdlrdata->autosos1branch, TRUE, DEFAULT_AUTOSOS1BRANCH, NULL, NULL) );
10260
10261 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/fixnonzero",
10262 "if neighborhood branching is used, then fix the branching variable (if positive in sign) to the value of the feasibility tolerance",
10263 &conshdlrdata->fixnonzero, TRUE, DEFAULT_FIXNONZERO, NULL, NULL) );
10264
10265 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/addcomps",
10266 "if TRUE then add complementarity constraints to the branching nodes (can be used in combination with neighborhood or bipartite branching)",
10267 &conshdlrdata->addcomps, TRUE, DEFAULT_ADDCOMPS, NULL, NULL) );
10268
10269 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/maxaddcomps",
10270 "maximal number of complementarity constraints added per branching node (-1: no limit)",
10271 &conshdlrdata->maxaddcomps, TRUE, DEFAULT_MAXADDCOMPS, -1, INT_MAX, NULL, NULL) );
10272
10273 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/addcompsfeas",
10274 "minimal feasibility value for complementarity constraints in order to be added to the branching node",
10275 &conshdlrdata->addcompsfeas, TRUE, DEFAULT_ADDCOMPSFEAS, -SCIP_REAL_MAX, SCIP_REAL_MAX, NULL, NULL) );
10276
10277 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/addbdsfeas",
10278 "minimal feasibility value for bound inequalities in order to be added to the branching node",
10279 &conshdlrdata->addbdsfeas, TRUE, DEFAULT_ADDBDSFEAS, -SCIP_REAL_MAX, SCIP_REAL_MAX, NULL, NULL) );
10280
10281 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/addextendedbds",
10282 "should added complementarity constraints be extended to SOS1 constraints to get tighter bound inequalities",
10283 &conshdlrdata->addextendedbds, TRUE, DEFAULT_ADDEXTENDEDBDS, NULL, NULL) );
10284
10285 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/branchsos",
10286 "Use SOS1 branching in enforcing (otherwise leave decision to branching rules)? This value can only be set to false if all SOS1 variables are binary",
10287 &conshdlrdata->branchsos, FALSE, TRUE, NULL, NULL) );
10288
10289 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/branchnonzeros",
10290 "Branch on SOS constraint with most number of nonzeros?",
10291 &conshdlrdata->branchnonzeros, FALSE, FALSE, NULL, NULL) );
10292
10293 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/branchweight",
10294 "Branch on SOS cons. with highest nonzero-variable weight for branching (needs branchnonzeros = false)?",
10295 &conshdlrdata->branchweight, FALSE, FALSE, NULL, NULL) );
10296
10297 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/addcompsdepth",
10298 "only add complementarity constraints to branching nodes for predefined depth (-1: no limit)",
10299 &conshdlrdata->addcompsdepth, TRUE, DEFAULT_ADDCOMPSDEPTH, -1, INT_MAX, NULL, NULL) );
10300
10301 /* selection rule parameters */
10302 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/nstrongrounds",
10303 "maximal number of strong branching rounds to perform for each node (-1: auto); only available for neighborhood and bipartite branching",
10304 &conshdlrdata->nstrongrounds, TRUE, DEFAULT_NSTRONGROUNDS, -1, INT_MAX, NULL, NULL) );
10305
10306 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/nstrongiter",
10307 "maximal number LP iterations to perform for each strong branching round (-2: auto, -1: no limit)",
10308 &conshdlrdata->nstrongiter, TRUE, DEFAULT_NSTRONGITER, -2, INT_MAX, NULL, NULL) );
10309
10310 /* separation parameters */
10311 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/boundcutsfromsos1",
10312 "if TRUE separate bound inequalities from initial SOS1 constraints",
10313 &conshdlrdata->boundcutsfromsos1, TRUE, DEFAULT_BOUNDCUTSFROMSOS1, NULL, NULL) );
10314
10315 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/boundcutsfromgraph",
10316 "if TRUE separate bound inequalities from the conflict graph",
10317 &conshdlrdata->boundcutsfromgraph, TRUE, DEFAULT_BOUNDCUTSFROMGRAPH, NULL, NULL) );
10318
10319 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/autocutsfromsos1",
10320 "if TRUE then automatically switch to separating initial SOS1 constraints if the SOS1 constraints do not overlap",
10321 &conshdlrdata->autocutsfromsos1, TRUE, DEFAULT_AUTOCUTSFROMSOS1, NULL, NULL) );
10322
10323 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/boundcutsfreq",
10324 "frequency for separating bound cuts; zero means to separate only in the root node",
10325 &conshdlrdata->boundcutsfreq, TRUE, DEFAULT_BOUNDCUTSFREQ, -1, SCIP_MAXTREEDEPTH, NULL, NULL) );
10326
10327 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/boundcutsdepth",
10328 "node depth of separating bound cuts (-1: no limit)",
10329 &conshdlrdata->boundcutsdepth, TRUE, DEFAULT_BOUNDCUTSDEPTH, -1, INT_MAX, NULL, NULL) );
10330
10331 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/maxboundcuts",
10332 "maximal number of bound cuts separated per branching node",
10333 &conshdlrdata->maxboundcuts, TRUE, DEFAULT_MAXBOUNDCUTS, 0, INT_MAX, NULL, NULL) );
10334
10335 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/maxboundcutsroot",
10336 "maximal number of bound cuts separated per iteration in the root node",
10337 &conshdlrdata->maxboundcutsroot, TRUE, DEFAULT_MAXBOUNDCUTSROOT, 0, INT_MAX, NULL, NULL) );
10338
10339 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/strthenboundcuts",
10340 "if TRUE then bound cuts are strengthened in case bound variables are available",
10341 &conshdlrdata->strthenboundcuts, TRUE, DEFAULT_STRTHENBOUNDCUTS, NULL, NULL) );
10342
10343 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/implcutsfreq",
10344 "frequency for separating implied bound cuts; zero means to separate only in the root node",
10345 &conshdlrdata->implcutsfreq, TRUE, DEFAULT_IMPLCUTSFREQ, -1, SCIP_MAXTREEDEPTH, NULL, NULL) );
10346
10347 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/implcutsdepth",
10348 "node depth of separating implied bound cuts (-1: no limit)",
10349 &conshdlrdata->implcutsdepth, TRUE, DEFAULT_IMPLCUTSDEPTH, -1, INT_MAX, NULL, NULL) );
10350
10351 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/maximplcuts",
10352 "maximal number of implied bound cuts separated per branching node",
10353 &conshdlrdata->maximplcuts, TRUE, DEFAULT_MAXIMPLCUTS, 0, INT_MAX, NULL, NULL) );
10354
10355 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/maximplcutsroot",
10356 "maximal number of implied bound cuts separated per iteration in the root node",
10357 &conshdlrdata->maximplcutsroot, TRUE, DEFAULT_MAXIMPLCUTSROOT, 0, INT_MAX, NULL, NULL) );
10358
10359 return SCIP_OKAY;
10360 }
10361
10362
10363 /** creates and captures a SOS1 constraint
10364 *
10365 * We set the constraint to not be modifable. If the weights are non NULL, the variables are ordered according to these
10366 * weights (in ascending order).
10367 *
10368 * @note The constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons().
10369 */
SCIPcreateConsSOS1(SCIP * scip,SCIP_CONS ** cons,const char * name,int nvars,SCIP_VAR ** vars,SCIP_Real * weights,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)10370 SCIP_RETCODE SCIPcreateConsSOS1(
10371 SCIP* scip, /**< SCIP data structure */
10372 SCIP_CONS** cons, /**< pointer to hold the created constraint */
10373 const char* name, /**< name of constraint */
10374 int nvars, /**< number of variables in the constraint */
10375 SCIP_VAR** vars, /**< array with variables of constraint entries */
10376 SCIP_Real* weights, /**< weights determining the variable order, or NULL if natural order should be used */
10377 SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP?
10378 * Usually set to TRUE. Set to FALSE for 'lazy constraints'. */
10379 SCIP_Bool separate, /**< should the constraint be separated during LP processing?
10380 * Usually set to TRUE. */
10381 SCIP_Bool enforce, /**< should the constraint be enforced during node processing?
10382 * TRUE for model constraints, FALSE for additional, redundant constraints. */
10383 SCIP_Bool check, /**< should the constraint be checked for feasibility?
10384 * TRUE for model constraints, FALSE for additional, redundant constraints. */
10385 SCIP_Bool propagate, /**< should the constraint be propagated during node processing?
10386 * Usually set to TRUE. */
10387 SCIP_Bool local, /**< is constraint only valid locally?
10388 * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */
10389 SCIP_Bool dynamic, /**< is constraint subject to aging?
10390 * Usually set to FALSE. Set to TRUE for own cuts which
10391 * are separated as constraints. */
10392 SCIP_Bool removable, /**< should the relaxation be removed from the LP due to aging or cleanup?
10393 * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */
10394 SCIP_Bool stickingatnode /**< should the constraint always be kept at the node where it was added, even
10395 * if it may be moved to a more global node?
10396 * Usually set to FALSE. Set to TRUE to for constraints that represent node data. */
10397 )
10398 {
10399 SCIP_CONSHDLR* conshdlr;
10400 SCIP_CONSDATA* consdata;
10401 SCIP_Bool modifiable;
10402 SCIP_Bool transformed;
10403 int v;
10404
10405 modifiable = FALSE;
10406
10407 /* find the SOS1 constraint handler */
10408 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
10409 if ( conshdlr == NULL )
10410 {
10411 SCIPerrorMessage("<%s> constraint handler not found\n", CONSHDLR_NAME);
10412 return SCIP_PLUGINNOTFOUND;
10413 }
10414
10415 /* are we in the transformed problem? */
10416 transformed = SCIPgetStage(scip) >= SCIP_STAGE_TRANSFORMED;
10417
10418 /* create constraint data */
10419 SCIP_CALL( SCIPallocBlockMemory(scip, &consdata) );
10420 consdata->vars = NULL;
10421 consdata->nvars = nvars;
10422 consdata->maxvars = nvars;
10423 consdata->rowub = NULL;
10424 consdata->rowlb = NULL;
10425 consdata->nfixednonzeros = transformed ? 0 : -1;
10426 consdata->weights = NULL;
10427 consdata->local = local;
10428
10429 if ( nvars > 0 )
10430 {
10431 SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &consdata->vars, vars, nvars) );
10432
10433 /* check weights */
10434 if ( weights != NULL )
10435 {
10436 /* store weights */
10437 SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &consdata->weights, weights, nvars) );
10438
10439 /* sort variables - ascending order */
10440 SCIPsortRealPtr(consdata->weights, (void**)consdata->vars, nvars);
10441 }
10442 }
10443 else
10444 {
10445 assert( weights == NULL );
10446 }
10447
10448 /* branching on multiaggregated variables does not seem to work well, so avoid it */
10449 for (v = 0; v < nvars; ++v)
10450 {
10451 SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, consdata->vars[v]) );
10452 }
10453
10454 /* create constraint */
10455 SCIP_CALL( SCIPcreateCons(scip, cons, name, conshdlr, consdata, initial, separate, enforce, check, propagate,
10456 local, modifiable, dynamic, removable, stickingatnode) );
10457 assert( transformed == SCIPconsIsTransformed(*cons) );
10458
10459 /* replace original variables by transformed variables in transformed constraint, add locks, and catch events */
10460 for (v = nvars - 1; v >= 0; --v)
10461 {
10462 SCIP_CONSHDLRDATA* conshdlrdata;
10463
10464 /* always use transformed variables in transformed constraints */
10465 if ( transformed )
10466 {
10467 SCIP_CALL( SCIPgetTransformedVar(scip, consdata->vars[v], &(consdata->vars[v])) );
10468 }
10469 assert( consdata->vars[v] != NULL );
10470 assert( transformed == SCIPvarIsTransformed(consdata->vars[v]) );
10471
10472 /* get constraint handler data */
10473 conshdlrdata = SCIPconshdlrGetData(conshdlr);
10474 assert( conshdlrdata != NULL );
10475
10476 /* handle the new variable */
10477 SCIP_CALL( handleNewVariableSOS1(scip, *cons, consdata, conshdlrdata, consdata->vars[v], transformed) );
10478 }
10479
10480 return SCIP_OKAY;
10481 }
10482
10483
10484 /** creates and captures a SOS1 constraint with all constraint flags set to their default values.
10485 *
10486 * @warning Do NOT set the constraint to be modifiable manually, because this might lead
10487 * to wrong results as the variable array will not be resorted
10488 *
10489 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
10490 */
SCIPcreateConsBasicSOS1(SCIP * scip,SCIP_CONS ** cons,const char * name,int nvars,SCIP_VAR ** vars,SCIP_Real * weights)10491 SCIP_RETCODE SCIPcreateConsBasicSOS1(
10492 SCIP* scip, /**< SCIP data structure */
10493 SCIP_CONS** cons, /**< pointer to hold the created constraint */
10494 const char* name, /**< name of constraint */
10495 int nvars, /**< number of variables in the constraint */
10496 SCIP_VAR** vars, /**< array with variables of constraint entries */
10497 SCIP_Real* weights /**< weights determining the variable order, or NULL if natural order should be used */
10498 )
10499 {
10500 SCIP_CALL( SCIPcreateConsSOS1( scip, cons, name, nvars, vars, weights, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE) );
10501
10502 return SCIP_OKAY;
10503 }
10504
10505
10506 /** adds variable to SOS1 constraint, the position is determined by the given weight */
SCIPaddVarSOS1(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var,SCIP_Real weight)10507 SCIP_RETCODE SCIPaddVarSOS1(
10508 SCIP* scip, /**< SCIP data structure */
10509 SCIP_CONS* cons, /**< constraint */
10510 SCIP_VAR* var, /**< variable to add to the constraint */
10511 SCIP_Real weight /**< weight determining position of variable */
10512 )
10513 {
10514 SCIP_CONSHDLRDATA* conshdlrdata;
10515 SCIP_CONSHDLR* conshdlr;
10516
10517 assert( scip != NULL );
10518 assert( var != NULL );
10519 assert( cons != NULL );
10520
10521 SCIPdebugMsg(scip, "adding variable <%s> to constraint <%s> with weight %g\n", SCIPvarGetName(var), SCIPconsGetName(cons), weight);
10522
10523 conshdlr = SCIPconsGetHdlr(cons);
10524 assert( conshdlr != NULL );
10525 if ( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) != 0 )
10526 {
10527 SCIPerrorMessage("constraint is not an SOS1 constraint.\n");
10528 return SCIP_INVALIDDATA;
10529 }
10530
10531 conshdlrdata = SCIPconshdlrGetData(conshdlr);
10532 assert( conshdlrdata != NULL );
10533
10534 SCIP_CALL( addVarSOS1(scip, cons, conshdlrdata, var, weight) );
10535
10536 return SCIP_OKAY;
10537 }
10538
10539
10540 /** appends variable to SOS1 constraint */
SCIPappendVarSOS1(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var)10541 SCIP_RETCODE SCIPappendVarSOS1(
10542 SCIP* scip, /**< SCIP data structure */
10543 SCIP_CONS* cons, /**< constraint */
10544 SCIP_VAR* var /**< variable to add to the constraint */
10545 )
10546 {
10547 SCIP_CONSHDLRDATA* conshdlrdata;
10548 SCIP_CONSHDLR* conshdlr;
10549
10550 assert( scip != NULL );
10551 assert( var != NULL );
10552 assert( cons != NULL );
10553
10554 SCIPdebugMsg(scip, "appending variable <%s> to constraint <%s>\n", SCIPvarGetName(var), SCIPconsGetName(cons));
10555
10556 conshdlr = SCIPconsGetHdlr(cons);
10557 assert( conshdlr != NULL );
10558 if ( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) != 0 )
10559 {
10560 SCIPerrorMessage("constraint is not an SOS1 constraint.\n");
10561 return SCIP_INVALIDDATA;
10562 }
10563
10564 conshdlrdata = SCIPconshdlrGetData(conshdlr);
10565 assert( conshdlrdata != NULL );
10566
10567 SCIP_CALL( appendVarSOS1(scip, cons, conshdlrdata, var) );
10568
10569 return SCIP_OKAY;
10570 }
10571
10572
10573 /** gets number of variables in SOS1 constraint */
SCIPgetNVarsSOS1(SCIP * scip,SCIP_CONS * cons)10574 int SCIPgetNVarsSOS1(
10575 SCIP* scip, /**< SCIP data structure */
10576 SCIP_CONS* cons /**< constraint */
10577 )
10578 {
10579 SCIP_CONSDATA* consdata;
10580
10581 assert( scip != NULL );
10582 assert( cons != NULL );
10583
10584 if ( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
10585 {
10586 SCIPerrorMessage("constraint is not an SOS1 constraint.\n");
10587 SCIPABORT();
10588 return -1; /*lint !e527*/
10589 }
10590
10591 consdata = SCIPconsGetData(cons);
10592 assert( consdata != NULL );
10593
10594 return consdata->nvars;
10595 }
10596
10597
10598 /** gets array of variables in SOS1 constraint */
SCIPgetVarsSOS1(SCIP * scip,SCIP_CONS * cons)10599 SCIP_VAR** SCIPgetVarsSOS1(
10600 SCIP* scip, /**< SCIP data structure */
10601 SCIP_CONS* cons /**< constraint data */
10602 )
10603 {
10604 SCIP_CONSDATA* consdata;
10605
10606 assert( scip != NULL );
10607 assert( cons != NULL );
10608
10609 if ( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
10610 {
10611 SCIPerrorMessage("constraint is not an SOS1 constraint.\n");
10612 SCIPABORT();
10613 return NULL; /*lint !e527*/
10614 }
10615
10616 consdata = SCIPconsGetData(cons);
10617 assert( consdata != NULL );
10618
10619 return consdata->vars;
10620 }
10621
10622
10623 /** gets array of weights in SOS1 constraint (or NULL if not existent) */
SCIPgetWeightsSOS1(SCIP * scip,SCIP_CONS * cons)10624 SCIP_Real* SCIPgetWeightsSOS1(
10625 SCIP* scip, /**< SCIP data structure */
10626 SCIP_CONS* cons /**< constraint data */
10627 )
10628 {
10629 SCIP_CONSDATA* consdata;
10630
10631 assert( scip != NULL );
10632 assert( cons != NULL );
10633
10634 if ( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
10635 {
10636 SCIPerrorMessage("constraint is not an SOS1 constraint.\n");
10637 SCIPABORT();
10638 return NULL; /*lint !e527*/
10639 }
10640
10641 consdata = SCIPconsGetData(cons);
10642 assert( consdata != NULL );
10643
10644 return consdata->weights;
10645 }
10646
10647
10648 /** gets conflict graph of SOS1 constraints (or NULL if not existent)
10649 *
10650 * @note The conflict graph is globally valid; local changes are not taken into account.
10651 */
SCIPgetConflictgraphSOS1(SCIP_CONSHDLR * conshdlr)10652 SCIP_DIGRAPH* SCIPgetConflictgraphSOS1(
10653 SCIP_CONSHDLR* conshdlr /**< SOS1 constraint handler */
10654 )
10655 {
10656 SCIP_CONSHDLRDATA* conshdlrdata;
10657
10658 assert( conshdlr != NULL );
10659
10660 if ( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) != 0 )
10661 {
10662 SCIPerrorMessage("not an SOS1 constraint handler.\n");
10663 SCIPABORT();
10664 return NULL; /*lint !e527*/
10665 }
10666 conshdlrdata = SCIPconshdlrGetData(conshdlr);
10667 assert( conshdlrdata != NULL );
10668
10669 return conshdlrdata->conflictgraph;
10670 }
10671
10672
10673 /** gets number of problem variables that are part of the SOS1 conflict graph */
SCIPgetNSOS1Vars(SCIP_CONSHDLR * conshdlr)10674 int SCIPgetNSOS1Vars(
10675 SCIP_CONSHDLR* conshdlr /**< SOS1 constraint handler */
10676 )
10677 {
10678 SCIP_CONSHDLRDATA* conshdlrdata;
10679
10680 assert( conshdlr != NULL );
10681
10682 if ( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) != 0 )
10683 {
10684 SCIPerrorMessage("not an SOS1 constraint handler.\n");
10685 SCIPABORT();
10686 return -1; /*lint !e527*/
10687 }
10688 conshdlrdata = SCIPconshdlrGetData(conshdlr);
10689 assert( conshdlrdata != NULL );
10690
10691 return conshdlrdata->nsos1vars;
10692 }
10693
10694
10695 /** returns whether variable is part of the SOS1 conflict graph */
SCIPvarIsSOS1(SCIP_CONSHDLR * conshdlr,SCIP_VAR * var)10696 SCIP_Bool SCIPvarIsSOS1(
10697 SCIP_CONSHDLR* conshdlr, /**< SOS1 constraint handler */
10698 SCIP_VAR* var /**< variable */
10699 )
10700 {
10701 SCIP_CONSHDLRDATA* conshdlrdata;
10702
10703 assert( var != NULL );
10704 assert( conshdlr != NULL );
10705
10706 if ( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) != 0 )
10707 {
10708 SCIPerrorMessage("not an SOS1 constraint handler.\n");
10709 SCIPABORT();
10710 return FALSE; /*lint !e527*/
10711 }
10712 conshdlrdata = SCIPconshdlrGetData(conshdlr);
10713 assert( conshdlrdata != NULL );
10714
10715 return varIsSOS1(conshdlrdata, var);
10716 }
10717
10718
10719 /** returns SOS1 index of variable or -1 if variable is not part of the SOS1 conflict graph */
SCIPvarGetNodeSOS1(SCIP_CONSHDLR * conshdlr,SCIP_VAR * var)10720 int SCIPvarGetNodeSOS1(
10721 SCIP_CONSHDLR* conshdlr, /**< SOS1 constraint handler */
10722 SCIP_VAR* var /**< variable */
10723 )
10724 {
10725 SCIP_CONSHDLRDATA* conshdlrdata;
10726
10727 assert( conshdlr != NULL );
10728 assert( var != NULL );
10729
10730 if ( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) != 0 )
10731 {
10732 SCIPerrorMessage("Not an SOS1 constraint handler.\n");
10733 SCIPABORT();
10734 return -1; /*lint !e527*/
10735 }
10736 conshdlrdata = SCIPconshdlrGetData(conshdlr);
10737 assert( conshdlrdata != NULL );
10738
10739 if ( conshdlrdata->varhash == NULL )
10740 {
10741 SCIPerrorMessage("Hashmap not yet initialized.\n");
10742 SCIPABORT();
10743 return -1; /*lint !e527*/
10744 }
10745
10746 return varGetNodeSOS1(conshdlrdata, var);
10747 }
10748
10749
10750 /** returns variable that belongs to a given node from the conflict graph */
SCIPnodeGetVarSOS1(SCIP_DIGRAPH * conflictgraph,int node)10751 SCIP_VAR* SCIPnodeGetVarSOS1(
10752 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
10753 int node /**< node from the conflict graph */
10754 )
10755 {
10756 SCIP_NODEDATA* nodedata;
10757
10758 assert( conflictgraph != NULL );
10759 assert( node >= 0 && node < SCIPdigraphGetNNodes(conflictgraph) );
10760
10761 /* get node data */
10762 nodedata = (SCIP_NODEDATA*)SCIPdigraphGetNodeData(conflictgraph, node);
10763
10764 if ( nodedata == NULL )
10765 {
10766 SCIPerrorMessage("variable is not assigned to an index.\n");
10767 SCIPABORT();
10768 return NULL; /*lint !e527*/
10769 }
10770
10771 return nodedata->var;
10772 }
10773
10774
10775 /** based on solution values of the variables, fixes variables to zero to turn all SOS1 constraints feasible */
SCIPmakeSOS1sFeasible(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_SOL * sol,SCIP_Bool * changed,SCIP_Bool * success)10776 SCIP_RETCODE SCIPmakeSOS1sFeasible(
10777 SCIP* scip, /**< SCIP pointer */
10778 SCIP_CONSHDLR* conshdlr, /**< SOS1 constraint handler */
10779 SCIP_SOL* sol, /**< solution */
10780 SCIP_Bool* changed, /**< pointer to store whether the solution has been changed */
10781 SCIP_Bool* success /**< pointer to store whether SOS1 constraints have been turned feasible and
10782 * solution was good enough */
10783 )
10784 {
10785 SCIP_CONSHDLRDATA* conshdlrdata;
10786 SCIP_Real roundobjval;
10787 SCIP_Bool allroundable;
10788
10789 assert( scip != NULL );
10790 assert( conshdlr != NULL );
10791 assert( sol != NULL );
10792 assert( changed != NULL );
10793 assert( success != NULL );
10794
10795 if ( strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) != 0 )
10796 {
10797 SCIPerrorMessage("Not an SOS1 constraint handler.\n");
10798 return SCIP_PARAMETERWRONGVAL;
10799 }
10800 conshdlrdata = SCIPconshdlrGetData(conshdlr);
10801 assert( conshdlrdata != NULL );
10802
10803 *changed = FALSE;
10804 *success = FALSE;
10805 allroundable = FALSE;
10806
10807 /* check number of SOS1 constraints */
10808 if ( SCIPconshdlrGetNConss(conshdlr) < 1 )
10809 {
10810 *success = TRUE;
10811 return SCIP_OKAY;
10812 }
10813
10814 /* if the SOS1 constraints do not overlap, we apply a faster method makeSOS1constraintsFeasible() that does not make use of the conflict graph;
10815 * for overlapping SOS1 constraints we apply the method makeSOS1conflictgraphFeasible(), which then may produce better feasible solutions */
10816 if ( conshdlrdata->switchsos1branch )
10817 {
10818 SCIP_CALL( makeSOS1constraintsFeasible(scip, conshdlr, sol, changed, &allroundable) );
10819 }
10820 else
10821 {
10822 SCIP_CALL( makeSOS1conflictgraphFeasible(scip, conshdlr, sol, changed, &allroundable) );
10823 }
10824
10825 if ( ! allroundable ) /*lint !e774*/
10826 return SCIP_OKAY;
10827
10828 /* check whether objective value of rounded solution is good enough */
10829 roundobjval = SCIPgetSolOrigObj(scip, sol);
10830 if ( SCIPgetObjsense(scip) == SCIP_OBJSENSE_MAXIMIZE )
10831 roundobjval *= -1;
10832
10833 if ( SCIPisLT(scip, roundobjval, SCIPgetUpperbound(scip) ) )
10834 *success = TRUE;
10835
10836 return SCIP_OKAY;
10837 }
10838