1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program and library */
4 /* SCIP --- Solving Constraint Integer Programs */
5 /* */
6 /* Copyright (C) 2002-2022 Konrad-Zuse-Zentrum */
7 /* fuer Informationstechnik Berlin */
8 /* */
9 /* SCIP is distributed under the terms of the ZIB Academic License. */
10 /* */
11 /* You should have received a copy of the ZIB Academic License */
12 /* along with SCIP; see the file COPYING. If not visit scipopt.org. */
13 /* */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15
16 /**@file cons_logicor.c
17 * @ingroup DEFPLUGINS_CONS
18 * @brief Constraint handler for logic or constraints \f$1^T x \ge 1\f$
19 * (equivalent to set covering, but algorithms are suited for depth first search).
20 * @author Tobias Achterberg
21 * @author Michael Winkler
22 */
23
24 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
25
26 #include "blockmemshell/memory.h"
27 #include "scip/cons_linear.h"
28 #include "scip/cons_logicor.h"
29 #include "scip/cons_setppc.h"
30 #include "scip/presolve.h"
31 #include "scip/pub_conflict.h"
32 #include "scip/pub_cons.h"
33 #include "scip/pub_event.h"
34 #include "scip/pub_lp.h"
35 #include "scip/pub_message.h"
36 #include "scip/pub_misc.h"
37 #include "scip/pub_misc_sort.h"
38 #include "scip/pub_var.h"
39 #include "scip/scip_conflict.h"
40 #include "scip/scip_cons.h"
41 #include "scip/scip_cut.h"
42 #include "scip/scip_event.h"
43 #include "scip/scip_general.h"
44 #include "scip/scip_lp.h"
45 #include "scip/scip_mem.h"
46 #include "scip/scip_message.h"
47 #include "scip/scip_nlp.h"
48 #include "scip/scip_numerics.h"
49 #include "scip/scip_param.h"
50 #include "scip/scip_prob.h"
51 #include "scip/scip_probing.h"
52 #include "scip/scip_sol.h"
53 #include "scip/scip_solvingstats.h"
54 #include "scip/scip_tree.h"
55 #include "scip/scip_var.h"
56 #include <string.h>
57
58
59 #define CONSHDLR_NAME "logicor"
60 #define CONSHDLR_DESC "logic or constraints"
61 #define CONSHDLR_SEPAPRIORITY +10000 /**< priority of the constraint handler for separation */
62 #define CONSHDLR_ENFOPRIORITY -2000000 /**< priority of the constraint handler for constraint enforcing */
63 #define CONSHDLR_CHECKPRIORITY -2000000 /**< priority of the constraint handler for checking feasibility */
64 #define CONSHDLR_SEPAFREQ 0 /**< frequency for separating cuts; zero means to separate only in the root node */
65 #define CONSHDLR_PROPFREQ 1 /**< frequency for propagating domains; zero means only preprocessing propagation */
66 #define CONSHDLR_EAGERFREQ 100 /**< frequency for using all instead of only the useful constraints in separation,
67 * propagation and enforcement, -1 for no eager evaluations, 0 for first only */
68 #define CONSHDLR_MAXPREROUNDS -1 /**< maximal number of presolving rounds the constraint handler participates in (-1: no limit) */
69 #define CONSHDLR_DELAYSEPA FALSE /**< should separation method be delayed, if other separators found cuts? */
70 #define CONSHDLR_DELAYPROP FALSE /**< should propagation method be delayed, if other propagators found reductions? */
71 #define CONSHDLR_NEEDSCONS TRUE /**< should the constraint handler be skipped, if no constraints are available? */
72
73 #define CONSHDLR_PRESOLTIMING SCIP_PRESOLTIMING_ALWAYS
74 #define CONSHDLR_PROP_TIMING SCIP_PROPTIMING_BEFORELP
75
76 #define LINCONSUPGD_PRIORITY +800000 /**< priority of the constraint handler for upgrading of linear constraints */
77
78 #define EVENTHDLR_NAME "logicor"
79 #define EVENTHDLR_DESC "event handler for logic or constraints"
80
81 #define CONFLICTHDLR_NAME "logicor"
82 #define CONFLICTHDLR_DESC "conflict handler creating logic or constraints"
83 #define CONFLICTHDLR_PRIORITY LINCONSUPGD_PRIORITY
84
85 #define DEFAULT_PRESOLPAIRWISE TRUE /**< should pairwise constraint comparison be performed in presolving? */
86 #define DEFAULT_STRENGTHEN TRUE /**< should pairwise constraint comparison try to strengthen constraints by removing superflous non-zeros? */
87
88 #define HASHSIZE_LOGICORCONS 500 /**< minimal size of hash table in logicor constraint tables */
89 #define DEFAULT_PRESOLUSEHASHING TRUE /**< should hash table be used for detecting redundant constraints in advance */
90 #define DEFAULT_DUALPRESOLVING TRUE /**< should dual presolving steps be performed? */
91 #define DEFAULT_NEGATEDCLIQUE TRUE /**< should negated clique information be used in presolving */
92 #define DEFAULT_IMPLICATIONS TRUE /**< should we try to shrink the variables and derive global boundchanges by
93 * using cliques and implications */
94
95 /* @todo make this a parameter setting */
96 #if 1 /* @todo test which AGEINCREASE formula is better! */
97 #define AGEINCREASE(n) (1.0 + 0.2 * (n))
98 #else
99 #define AGEINCREASE(n) (0.1 * (n))
100 #endif
101
102
103 /* @todo maybe use event SCIP_EVENTTYPE_VARUNLOCKED to decide for another dual-presolving run on a constraint */
104
105 /*
106 * Data structures
107 */
108
109 /** constraint handler data */
110 struct SCIP_ConshdlrData
111 {
112 SCIP_EVENTHDLR* eventhdlr; /**< event handler for events on watched variables */
113 SCIP_CONSHDLR* conshdlrlinear; /**< pointer to linear constraint handler or NULL if not included */
114 SCIP_CONSHDLR* conshdlrsetppc; /**< pointer to setppc constraint handler or NULL if not included */
115 SCIP_Bool presolpairwise; /**< should pairwise constraint comparison be performed in presolving? */
116 SCIP_Bool presolusehashing; /**< should hash table be used for detecting redundant constraints in
117 * advance */
118 SCIP_Bool dualpresolving; /**< should dual presolving steps be performed? */
119 SCIP_Bool usenegatedclique; /**< should negated clique information be used in presolving */
120 SCIP_Bool useimplications; /**< should we try to shrink the variables and derive global boundchanges
121 * by using clique and implications */
122 SCIP_Bool usestrengthening; /**< should pairwise constraint comparison try to strengthen constraints by
123 * removing superflous non-zeros? */
124 int nlastcliquesneg; /**< number of cliques after last negated clique presolving round */
125 int nlastimplsneg; /**< number of implications after last negated clique presolving round */
126 int nlastcliquesshorten;/**< number of cliques after last shortening of constraints */
127 int nlastimplsshorten; /**< number of implications after last shortening of constraints */
128 };
129
130 /* @todo it might speed up exit-presolve to remember all positions for variables when catching the varfixed event, or we
131 * change catching and dropping the events like it is done in cons_setppc, which probably makes the code more
132 * clear
133 */
134
135 /** logic or constraint data */
136 struct SCIP_ConsData
137 {
138 SCIP_ROW* row; /**< LP row, if constraint is already stored in LP row format */
139 SCIP_NLROW* nlrow; /**< NLP row, if constraint has been added to NLP relaxation */
140 SCIP_VAR** vars; /**< variables of the constraint */
141 int varssize; /**< size of vars array */
142 int nvars; /**< number of variables in the constraint */
143 int watchedvar1; /**< position of the first watched variable */
144 int watchedvar2; /**< position of the second watched variable */
145 int filterpos1; /**< event filter position of first watched variable */
146 int filterpos2; /**< event filter position of second watched variable */
147 unsigned int signature; /**< constraint signature which is need for pairwise comparison */
148 unsigned int presolved:1; /**< flag indicates if we have some fixed, aggregated or multi-aggregated
149 * variables
150 */
151 unsigned int impladded:1; /**< was the 2-variable logic or constraint already added as implication? */
152 unsigned int sorted:1; /**< are the constraint's variables sorted? */
153 unsigned int changed:1; /**< was constraint changed since last redundancy round in preprocessing? */
154 unsigned int merged:1; /**< are the constraint's equal/negated variables already merged? */
155 unsigned int existmultaggr:1; /**< does this constraint contain aggregations */
156 unsigned int validsignature:1; /**< is the signature valid */
157 };
158
159
160 /*
161 * Local methods
162 */
163
164 /** installs rounding locks for the given variable in the given logic or constraint */
165 static
166 SCIP_RETCODE lockRounding(
167 SCIP* scip, /**< SCIP data structure */
168 SCIP_CONS* cons, /**< logic or constraint */
169 SCIP_VAR* var /**< variable of constraint entry */
170 )
171 {
172 SCIP_CALL( SCIPlockVarCons(scip, var, cons, TRUE, FALSE) );
173
174 return SCIP_OKAY;
175 }
176
177 /** removes rounding locks for the given variable in the given logic or constraint */
178 static
179 SCIP_RETCODE unlockRounding(
180 SCIP* scip, /**< SCIP data structure */
181 SCIP_CONS* cons, /**< logic or constraint */
182 SCIP_VAR* var /**< variable of constraint entry */
183 )
184 {
185 SCIP_CALL( SCIPunlockVarCons(scip, var, cons, TRUE, FALSE) );
186
187 return SCIP_OKAY;
188 }
189
190 /** creates constraint handler data for logic or constraint handler */
191 static
192 SCIP_RETCODE conshdlrdataCreate(
193 SCIP* scip, /**< SCIP data structure */
194 SCIP_CONSHDLRDATA** conshdlrdata, /**< pointer to store the constraint handler data */
195 SCIP_EVENTHDLR* eventhdlr /**< event handler */
196 )
197 {
198 assert(scip != NULL);
199 assert(conshdlrdata != NULL);
200 assert(eventhdlr != NULL);
201
202 SCIP_CALL( SCIPallocBlockMemory(scip, conshdlrdata) );
203
204 (*conshdlrdata)->nlastcliquesneg = 0;
205 (*conshdlrdata)->nlastimplsneg = 0;
206 (*conshdlrdata)->nlastcliquesshorten = 0;
207 (*conshdlrdata)->nlastimplsshorten = 0;
208
209 /* set event handler for catching events on watched variables */
210 (*conshdlrdata)->eventhdlr = eventhdlr;
211
212 return SCIP_OKAY;
213 }
214
215 /** frees constraint handler data for logic or constraint handler */
216 static
217 void conshdlrdataFree(
218 SCIP* scip, /**< SCIP data structure */
219 SCIP_CONSHDLRDATA** conshdlrdata /**< pointer to the constraint handler data */
220 )
221 {
222 assert(conshdlrdata != NULL);
223 assert(*conshdlrdata != NULL);
224
225 SCIPfreeBlockMemory(scip, conshdlrdata);
226 }
227
228 /** ensures, that the vars array can store at least num entries */
229 static
230 SCIP_RETCODE consdataEnsureVarsSize(
231 SCIP* scip, /**< SCIP data structure */
232 SCIP_CONSDATA* consdata, /**< logicor constraint data */
233 int num /**< minimum number of entries to store */
234 )
235 {
236 assert(consdata != NULL);
237 assert(consdata->nvars <= consdata->varssize);
238
239 if( num > consdata->varssize )
240 {
241 int newsize;
242
243 newsize = SCIPcalcMemGrowSize(scip, num);
244 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->vars, consdata->varssize, newsize) );
245 consdata->varssize = newsize;
246 }
247 assert(num <= consdata->varssize);
248
249 return SCIP_OKAY;
250 }
251
252 /** creates a logic or constraint data object */
253 static
254 SCIP_RETCODE consdataCreate(
255 SCIP* scip, /**< SCIP data structure */
256 SCIP_CONSDATA** consdata, /**< pointer to store the logic or constraint data */
257 int nvars, /**< number of variables in the constraint */
258 SCIP_VAR** vars /**< variables of the constraint */
259 )
260 {
261 int v;
262
263 assert(consdata != NULL);
264 assert(nvars == 0 || vars != NULL);
265
266 SCIP_CALL( SCIPallocBlockMemory(scip, consdata) );
267
268 (*consdata)->row = NULL;
269 (*consdata)->nlrow = NULL;
270 if( nvars > 0 )
271 {
272 SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*consdata)->vars, vars, nvars) );
273 (*consdata)->varssize = nvars;
274 (*consdata)->nvars = nvars;
275 }
276 else
277 {
278 (*consdata)->vars = NULL;
279 (*consdata)->varssize = 0;
280 (*consdata)->nvars = 0;
281 }
282 (*consdata)->watchedvar1 = -1;
283 (*consdata)->watchedvar2 = -1;
284 (*consdata)->filterpos1 = -1;
285 (*consdata)->filterpos2 = -1;
286 (*consdata)->presolved = FALSE;
287 (*consdata)->impladded = FALSE;
288 (*consdata)->changed = TRUE;
289 (*consdata)->sorted = (nvars <= 1);
290 (*consdata)->merged = (nvars <= 1);
291 (*consdata)->existmultaggr = FALSE;
292 (*consdata)->validsignature = FALSE;
293
294 /* get transformed variables, if we are in the transformed problem */
295 if( SCIPisTransformed(scip) )
296 {
297 SCIP_CALL( SCIPgetTransformedVars(scip, (*consdata)->nvars, (*consdata)->vars, (*consdata)->vars) );
298
299 /* check for multi-aggregations and capture variables */
300 for( v = 0; v < (*consdata)->nvars; v++ )
301 {
302 SCIP_VAR* var = SCIPvarGetProbvar((*consdata)->vars[v]);
303 assert(var != NULL);
304 (*consdata)->existmultaggr = (*consdata)->existmultaggr || (SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);
305 SCIP_CALL( SCIPcaptureVar(scip, (*consdata)->vars[v]) );
306 }
307 }
308 else
309 {
310 /* capture variables */
311 for( v = 0; v < (*consdata)->nvars; v++ )
312 {
313 assert((*consdata)->vars[v] != NULL);
314 SCIP_CALL( SCIPcaptureVar(scip, (*consdata)->vars[v]) );
315 }
316 }
317
318 return SCIP_OKAY;
319 }
320
321 /** frees a logic or constraint data */
322 static
323 SCIP_RETCODE consdataFree(
324 SCIP* scip, /**< SCIP data structure */
325 SCIP_CONSDATA** consdata /**< pointer to the logic or constraint */
326 )
327 {
328 int v;
329
330 assert(consdata != NULL);
331 assert(*consdata != NULL);
332
333 /* release the row */
334 if( (*consdata)->row != NULL )
335 {
336 SCIP_CALL( SCIPreleaseRow(scip, &(*consdata)->row) );
337 }
338
339 /* release the nlrow */
340 if( (*consdata)->nlrow != NULL )
341 {
342 SCIP_CALL( SCIPreleaseNlRow(scip, &(*consdata)->nlrow) );
343 }
344
345 /* release variables */
346 for( v = 0; v < (*consdata)->nvars; v++ )
347 {
348 assert((*consdata)->vars[v] != NULL);
349 SCIP_CALL( SCIPreleaseVar(scip, &((*consdata)->vars[v])) );
350 }
351
352 SCIPfreeBlockMemoryArrayNull(scip, &(*consdata)->vars, (*consdata)->varssize);
353 SCIPfreeBlockMemory(scip, consdata);
354
355 return SCIP_OKAY;
356 }
357
358 /** prints logic or constraint to file stream */
359 static
360 SCIP_RETCODE consdataPrint(
361 SCIP* scip, /**< SCIP data structure */
362 SCIP_CONSDATA* consdata, /**< logic or constraint data */
363 FILE* file, /**< output file (or NULL for standard output) */
364 SCIP_Bool endline /**< should an endline be set? */
365 )
366 {
367 assert(consdata != NULL);
368
369 /* print constraint type */
370 SCIPinfoMessage(scip, file, "logicor(");
371
372 /* print variable list */
373 SCIP_CALL( SCIPwriteVarsList(scip, file, consdata->vars, consdata->nvars, TRUE, ',') );
374
375 /* close bracket */
376 SCIPinfoMessage(scip, file, ")");
377
378 if( endline )
379 SCIPinfoMessage(scip, file, "\n");
380
381 return SCIP_OKAY;
382 }
383
384 /** stores the given variable numbers as watched variables, and updates the event processing */
385 static
386 SCIP_RETCODE switchWatchedvars(
387 SCIP* scip, /**< SCIP data structure */
388 SCIP_CONS* cons, /**< logic or constraint */
389 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */
390 int watchedvar1, /**< new first watched variable */
391 int watchedvar2 /**< new second watched variable */
392 )
393 {
394 SCIP_CONSDATA* consdata;
395
396 consdata = SCIPconsGetData(cons);
397 assert(consdata != NULL);
398 assert(watchedvar1 == -1 || watchedvar1 != watchedvar2);
399 assert(watchedvar1 != -1 || watchedvar2 == -1);
400 assert(watchedvar1 == -1 || (0 <= watchedvar1 && watchedvar1 < consdata->nvars));
401 assert(watchedvar2 == -1 || (0 <= watchedvar2 && watchedvar2 < consdata->nvars));
402
403 /* if one watched variable is equal to the old other watched variable, just switch positions */
404 if( watchedvar1 == consdata->watchedvar2 || watchedvar2 == consdata->watchedvar1 )
405 {
406 int tmp;
407
408 tmp = consdata->watchedvar1;
409 consdata->watchedvar1 = consdata->watchedvar2;
410 consdata->watchedvar2 = tmp;
411 tmp = consdata->filterpos1;
412 consdata->filterpos1 = consdata->filterpos2;
413 consdata->filterpos2 = tmp;
414 }
415 assert(watchedvar1 == -1 || watchedvar1 != consdata->watchedvar2);
416 assert(watchedvar2 == -1 || watchedvar2 != consdata->watchedvar1);
417
418 /* drop events on old watched variables */
419 if( consdata->watchedvar1 != -1 && consdata->watchedvar1 != watchedvar1 )
420 {
421 assert(consdata->filterpos1 != -1);
422 SCIP_CALL( SCIPdropVarEvent(scip, consdata->vars[consdata->watchedvar1],
423 SCIP_EVENTTYPE_UBTIGHTENED | SCIP_EVENTTYPE_LBRELAXED, eventhdlr, (SCIP_EVENTDATA*)cons,
424 consdata->filterpos1) );
425 }
426 if( consdata->watchedvar2 != -1 && consdata->watchedvar2 != watchedvar2 )
427 {
428 assert(consdata->filterpos2 != -1);
429 SCIP_CALL( SCIPdropVarEvent(scip, consdata->vars[consdata->watchedvar2],
430 SCIP_EVENTTYPE_UBTIGHTENED | SCIP_EVENTTYPE_LBRELAXED, eventhdlr, (SCIP_EVENTDATA*)cons,
431 consdata->filterpos2) );
432 }
433
434 /* catch events on new watched variables */
435 if( watchedvar1 != -1 && watchedvar1 != consdata->watchedvar1 )
436 {
437 SCIP_CALL( SCIPcatchVarEvent(scip, consdata->vars[watchedvar1],
438 SCIP_EVENTTYPE_UBTIGHTENED | SCIP_EVENTTYPE_LBRELAXED, eventhdlr, (SCIP_EVENTDATA*)cons,
439 &consdata->filterpos1) );
440 }
441 if( watchedvar2 != -1 && watchedvar2 != consdata->watchedvar2 )
442 {
443 SCIP_CALL( SCIPcatchVarEvent(scip, consdata->vars[watchedvar2],
444 SCIP_EVENTTYPE_UBTIGHTENED | SCIP_EVENTTYPE_LBRELAXED, eventhdlr, (SCIP_EVENTDATA*)cons,
445 &consdata->filterpos2) );
446 }
447
448 /* set the new watched variables */
449 consdata->watchedvar1 = watchedvar1;
450 consdata->watchedvar2 = watchedvar2;
451
452 return SCIP_OKAY;
453 }
454
455 /** adds coefficient in logicor constraint */
456 static
457 SCIP_RETCODE addCoef(
458 SCIP* scip, /**< SCIP data structure */
459 SCIP_CONS* cons, /**< logicor constraint */
460 SCIP_VAR* var /**< variable to add to the constraint */
461 )
462 {
463 SCIP_CONSDATA* consdata;
464 SCIP_Bool transformed;
465
466 assert(var != NULL);
467
468 consdata = SCIPconsGetData(cons);
469 assert(consdata != NULL);
470
471 /* are we in the transformed problem? */
472 transformed = SCIPconsIsTransformed(cons);
473
474 /* always use transformed variables in transformed constraints */
475 if( transformed )
476 {
477 SCIP_CALL( SCIPgetTransformedVar(scip, var, &var) );
478
479 if( !consdata->existmultaggr && SCIPvarGetStatus(SCIPvarGetProbvar(var)) == SCIP_VARSTATUS_MULTAGGR )
480 consdata->existmultaggr = TRUE;
481
482 consdata->presolved = FALSE;
483 }
484 assert(var != NULL);
485 assert(transformed == SCIPvarIsTransformed(var));
486
487 SCIP_CALL( consdataEnsureVarsSize(scip, consdata, consdata->nvars + 1) );
488 consdata->vars[consdata->nvars] = var;
489 SCIP_CALL( SCIPcaptureVar(scip, consdata->vars[consdata->nvars]) );
490 consdata->nvars++;
491
492 /* we only catch this event in presolving stage */
493 if( SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING || SCIPgetStage(scip) == SCIP_STAGE_INITPRESOLVE )
494 {
495 SCIP_CONSHDLRDATA* conshdlrdata;
496 SCIP_CONSHDLR* conshdlr;
497
498 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
499 assert(conshdlr != NULL);
500 conshdlrdata = SCIPconshdlrGetData(conshdlr);
501 assert(conshdlrdata != NULL);
502
503 SCIP_CALL( SCIPcatchVarEvent(scip, var, SCIP_EVENTTYPE_VARFIXED, conshdlrdata->eventhdlr,
504 (SCIP_EVENTDATA*)cons, NULL) );
505 }
506
507 consdata->sorted = (consdata->nvars == 1);
508 consdata->changed = TRUE;
509 consdata->validsignature = FALSE;
510
511 /* install the rounding locks for the new variable */
512 SCIP_CALL( lockRounding(scip, cons, var) );
513
514 /* add the new coefficient to the LP row */
515 if( consdata->row != NULL )
516 {
517 SCIP_CALL( SCIPaddVarToRow(scip, consdata->row, var, 1.0) );
518 }
519
520 consdata->merged = FALSE;
521
522 return SCIP_OKAY;
523 }
524
525 /** deletes coefficient at given position from logic or constraint data */
526 static
527 SCIP_RETCODE delCoefPos(
528 SCIP* scip, /**< SCIP data structure */
529 SCIP_CONS* cons, /**< logic or constraint */
530 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */
531 int pos /**< position of coefficient to delete */
532 )
533 {
534 SCIP_CONSDATA* consdata;
535
536 assert(eventhdlr != NULL);
537
538 consdata = SCIPconsGetData(cons);
539 assert(consdata != NULL);
540 assert(0 <= pos && pos < consdata->nvars);
541 assert(SCIPconsIsTransformed(cons) == SCIPvarIsTransformed(consdata->vars[pos]));
542
543 /* remove the rounding locks of variable */
544 SCIP_CALL( unlockRounding(scip, cons, consdata->vars[pos]) );
545
546 /* we only catch this event in presolving stage, so we need to only drop it there */
547 if( SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING || SCIPgetStage(scip) == SCIP_STAGE_INITPRESOLVE )
548 {
549 SCIP_CALL( SCIPdropVarEvent(scip, consdata->vars[pos], SCIP_EVENTTYPE_VARFIXED, eventhdlr,
550 (SCIP_EVENTDATA*)cons, -1) );
551 }
552
553 if( SCIPconsIsTransformed(cons) )
554 {
555 /* if the position is watched, stop watching the position */
556 if( consdata->watchedvar1 == pos )
557 {
558 SCIP_CALL( switchWatchedvars(scip, cons, eventhdlr, consdata->watchedvar2, -1) );
559 }
560 if( consdata->watchedvar2 == pos )
561 {
562 SCIP_CALL( switchWatchedvars(scip, cons, eventhdlr, consdata->watchedvar1, -1) );
563 }
564 }
565 assert(pos != consdata->watchedvar1);
566 assert(pos != consdata->watchedvar2);
567
568 /* release variable */
569 SCIP_CALL( SCIPreleaseVar(scip, &consdata->vars[pos]) );
570
571 /* move the last variable to the free slot */
572 if( pos != consdata->nvars - 1 )
573 {
574 consdata->vars[pos] = consdata->vars[consdata->nvars-1];
575 consdata->sorted = FALSE;
576 }
577 consdata->nvars--;
578
579 /* if the last variable (that moved) was watched, update the watched position */
580 if( consdata->watchedvar1 == consdata->nvars )
581 consdata->watchedvar1 = pos;
582 if( consdata->watchedvar2 == consdata->nvars )
583 consdata->watchedvar2 = pos;
584
585 consdata->changed = TRUE;
586 consdata->validsignature = FALSE;
587
588 SCIP_CALL( SCIPenableConsPropagation(scip, cons) );
589
590 return SCIP_OKAY;
591 }
592
593 /** in case a part (more than one variable) in the logic or constraint is independent of every else, we can perform dual
594 * reductions;
595 * - fix the variable with the smallest object coefficient to one if the constraint is not modifiable and all
596 * variable are independant
597 * - fix all independant variables with negative object coefficient to one
598 * - fix all remaining independant variables to zero
599 *
600 * also added the special case were exactly one variable is locked by this constraint and another variable without any
601 * uplocks has a better objective value than this single variable
602 * - here we fix the variable to 0.0 (if the objective contribution is non-negative)
603 *
604 * Note: the following dual reduction for logic or constraints is already performed by the presolver "dualfix"
605 * - if a variable in a set covering constraint is only locked by that constraint and has negative or zero
606 * objective coefficient than it can be fixed to one
607 */
608 static
609 SCIP_RETCODE dualPresolving(
610 SCIP* scip, /**< SCIP data structure */
611 SCIP_CONS* cons, /**< setppc constraint */
612 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */
613 int* nfixedvars, /**< pointer to count number of fixings */
614 int* ndelconss, /**< pointer to count number of deleted constraints */
615 int* nchgcoefs, /**< pointer to count number of changed/deleted coefficients */
616 SCIP_RESULT* result /**< pointer to store the result SCIP_SUCCESS, if presolving was performed */
617 )
618 {
619 SCIP_CONSDATA* consdata;
620 SCIP_VAR** vars;
621 SCIP_VAR* var;
622 SCIP_VAR* activevar;
623 SCIP_Real bestobjval;
624 SCIP_Real bestobjvalnouplocks;
625 SCIP_Real objval;
626 SCIP_Real fixval;
627 SCIP_Bool infeasible;
628 SCIP_Bool fixed;
629 SCIP_Bool negated;
630 int nfixables;
631 int nvars;
632 int idx;
633 int idxnouplocks;
634 int v;
635
636 assert(scip != NULL);
637 assert(cons != NULL);
638 assert(eventhdlr != NULL);
639 assert(nfixedvars != NULL);
640 assert(ndelconss != NULL);
641 assert(nchgcoefs != NULL);
642 assert(result != NULL);
643
644 /* constraints for which the check flag is set to FALSE, did not contribute to the lock numbers; therefore, we cannot
645 * use the locks to decide for a dual reduction using this constraint; for example after a restart the cuts which are
646 * added to the problems have the check flag set to FALSE
647 */
648 if( !SCIPconsIsChecked(cons) )
649 return SCIP_OKAY;
650
651 assert(SCIPconsIsActive(cons));
652
653 consdata = SCIPconsGetData(cons);
654 assert(consdata != NULL);
655
656 nvars = consdata->nvars;
657
658 /* we don't want to consider small constraints (note that the constraints can be modifiable, so we can't delete this
659 * constraint)
660 */
661 if( nvars < 2 )
662 return SCIP_OKAY;
663
664 vars = consdata->vars;
665 idx = -1;
666 idxnouplocks = -1;
667 bestobjval = SCIP_INVALID;
668 bestobjvalnouplocks = SCIP_INVALID;
669
670 nfixables = 0;
671
672 /* check if we can apply the dual reduction; therefore count the number of variables where the logic or has the only
673 * locks on
674 */
675 for( v = nvars - 1; v >= 0; --v )
676 {
677 var = vars[v];
678 assert(var != NULL);
679
680 /* variables with varstatus not equal to SCIP_VARSTATUS_FIXED can also have fixed bounds, but were not removed yet */
681 if( SCIPvarGetUbGlobal(var) < 0.5 )
682 {
683 #ifndef NDEBUG
684 SCIP_VAR* bestvar = NULL;
685 #endif
686 if( idx == consdata->nvars - 1 )
687 {
688 #ifndef NDEBUG
689 bestvar = consdata->vars[idx];
690 #endif
691 idx = v;
692 }
693
694 SCIP_CALL( delCoefPos(scip, cons, eventhdlr, v) );
695 ++(*nchgcoefs);
696
697 assert(bestvar == NULL || bestvar == consdata->vars[v]);
698
699 continue;
700 }
701 if( SCIPvarGetLbGlobal(var) > 0.5 )
702 {
703 /* remove constraint since it is redundant */
704 SCIP_CALL( SCIPdelCons(scip, cons) );
705 ++(*ndelconss);
706
707 return SCIP_OKAY;
708 }
709
710 /* remember best variable with no uplocks, this variable dominates all other with exactly one downlock */
711 if( SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL) > 1
712 && SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL) == 0 )
713 {
714 SCIP_CALL( SCIPvarGetAggregatedObj(var, &objval) );
715
716 /* check if the current variable has a smaller objective coefficient then the best one */
717 if( SCIPisLT(scip, objval, bestobjval) )
718 {
719 idxnouplocks = v;
720 bestobjvalnouplocks = objval;
721 }
722 }
723
724 /* in case an other constraints has also locks on that variable we cannot perform a dual reduction on these
725 * variables
726 */
727 if( SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL) > 1
728 || SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL) > 0 )
729 continue;
730
731 ++nfixables;
732 negated = FALSE;
733
734 /* get the active variable */
735 SCIP_CALL( SCIPvarGetProbvarBinary(&var, &negated) );
736 assert(SCIPvarIsActive(var));
737
738 if( negated )
739 objval = -SCIPvarGetObj(var);
740 else
741 objval = SCIPvarGetObj(var);
742
743 /* check if the current variable has a smaller objective coefficient */
744 if( SCIPisLT(scip, objval, bestobjval) )
745 {
746 idx = v;
747 bestobjval = objval;
748 }
749 }
750
751 nvars = consdata->nvars;
752
753 /* check if we have a single variable dominated by another */
754 if( nfixables == 1 && idxnouplocks >= 0 )
755 {
756 assert(bestobjvalnouplocks != SCIP_INVALID); /*lint !e777*/
757
758 for( v = nvars - 1; v >= 0; --v )
759 {
760 var = vars[v];
761 assert(var != NULL);
762
763 /* check if a variable only appearing in this constraint is dominated by another */
764 if( SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL) == 1
765 && SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL) == 0 )
766 {
767 assert(idxnouplocks != v);
768
769 SCIP_CALL( SCIPvarGetAggregatedObj(var, &objval) );
770
771 if( SCIPisGE(scip, objval, bestobjvalnouplocks) && !SCIPisNegative(scip, objval) )
772 {
773 SCIP_CALL( SCIPfixVar(scip, var, 0.0, &infeasible, &fixed) );
774 assert(!infeasible);
775 assert(fixed);
776
777 SCIPdebugMsg(scip, " -> dual fixing <%s> == 0.0\n", SCIPvarGetName(var));
778 ++(*nfixedvars);
779 }
780
781 break;
782 }
783 }
784 }
785
786 if( nfixables < 2 )
787 return SCIP_OKAY;
788
789 nvars = consdata->nvars;
790
791 assert(idx >= 0 && idx < nvars);
792 assert(bestobjval < SCIPinfinity(scip));
793
794 *result = SCIP_SUCCESS;
795
796 /* fix all redundant variables to their best bound */
797
798 /* first part of all variables */
799 for( v = 0; v < nvars; ++v )
800 {
801 var = vars[v];
802 assert(var != NULL);
803
804 /* in case an other constraints has also locks on that variable we cannot perform a dual reduction on these
805 * variables
806 */
807 if( SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL) > 1
808 || SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL) > 0 )
809 continue;
810
811 if( v == idx )
812 continue;
813
814 activevar = var;
815 negated = FALSE;
816
817 /* get the active variable */
818 SCIP_CALL( SCIPvarGetProbvarBinary(&activevar, &negated) );
819 assert(SCIPvarIsActive(activevar));
820
821 if( negated )
822 objval = -SCIPvarGetObj(activevar);
823 else
824 objval = SCIPvarGetObj(activevar);
825
826 if( objval > 0.0 )
827 fixval = 0.0;
828 else
829 fixval = 1.0;
830
831 SCIP_CALL( SCIPfixVar(scip, var, fixval, &infeasible, &fixed) );
832 assert(!infeasible);
833 assert(fixed);
834
835 SCIPdebugMsg(scip, " -> dual fixing <%s> == %g\n", SCIPvarGetName(var), fixval);
836 ++(*nfixedvars);
837 }
838
839 /* if all variable have our appreciated number of locks and the constraint is not modifiable, or if the bestobjval is
840 * less than or equal to zero, we can fix the variable with the smallest objective coefficient to one and the
841 * constraint gets redundant
842 */
843 if( (nfixables == nvars && !SCIPconsIsModifiable(cons)) || bestobjval <= 0.0 )
844 {
845 SCIP_CALL( SCIPfixVar(scip, vars[idx], 1.0, &infeasible, &fixed) );
846 assert(!infeasible);
847 assert(fixed);
848
849 SCIPdebugMsg(scip, " -> fixed <%s> == 1.0\n", SCIPvarGetName(vars[idx]));
850 ++(*nfixedvars);
851
852 /* remove constraint since it is now redundant */
853 SCIP_CALL( SCIPdelCons(scip, cons) );
854 ++(*ndelconss);
855 }
856
857 return SCIP_OKAY;
858 }
859
860 /** deletes all zero-fixed variables, checks for variables fixed to one, replace all variables which are not active or
861 * not a negation of an active variable by there active or negation of an active counterpart
862 */
863 static
864 SCIP_RETCODE applyFixings(
865 SCIP* scip, /**< SCIP data structure */
866 SCIP_CONS* cons, /**< logic or constraint */
867 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */
868 SCIP_Bool* redundant, /**< returns whether a variable fixed to one exists in the constraint */
869 int* nchgcoefs, /**< pointer to count number of changed/deleted coefficients */
870 int* naddconss, /**< pointer to count number of added constraints, or NULL indicating we
871 * can not resolve multi-aggregations
872 */
873 int* ndelconss /**< pointer to count number of deleted constraints, or NULL indicating we
874 * can not resolve multi-aggregations
875 */
876 )
877 {
878 SCIP_CONSDATA* consdata;
879 SCIP_VAR* var;
880 int v;
881 SCIP_VAR** vars;
882 SCIP_Bool* negarray;
883 int nvars;
884
885 assert(eventhdlr != NULL);
886 assert(redundant != NULL);
887
888 consdata = SCIPconsGetData(cons);
889 assert(consdata != NULL);
890 assert(consdata->nvars == 0 || consdata->vars != NULL);
891
892 *redundant = FALSE;
893 v = 0;
894
895 /* all multi-aggregations should be resolved */
896 consdata->existmultaggr = FALSE;
897 consdata->presolved = TRUE;
898
899 /* remove zeros and mark constraint redundant when found one variable fixed to one */
900 while( v < consdata->nvars )
901 {
902 var = consdata->vars[v];
903 assert(SCIPvarIsBinary(var));
904
905 if( SCIPvarGetLbGlobal(var) > 0.5 )
906 {
907 assert(SCIPisFeasEQ(scip, SCIPvarGetUbGlobal(var), 1.0));
908 *redundant = TRUE;
909
910 return SCIP_OKAY;
911 }
912 else if( SCIPvarGetUbGlobal(var) < 0.5 )
913 {
914 assert(SCIPisFeasEQ(scip, SCIPvarGetLbGlobal(var), 0.0));
915 SCIP_CALL( delCoefPos(scip, cons, eventhdlr, v) );
916 ++(*nchgcoefs);
917 }
918 else
919 ++v;
920 }
921
922 if( consdata->nvars == 0 )
923 return SCIP_OKAY;
924
925 nvars = consdata->nvars;
926
927 /* allocate temporary memory */
928 SCIP_CALL( SCIPallocBufferArray(scip, &vars, nvars) );
929 SCIP_CALL( SCIPallocBufferArray(scip, &negarray, nvars) );
930
931 /* get active or negation of active variables */
932 SCIP_CALL( SCIPgetBinvarRepresentatives(scip, nvars, consdata->vars, vars, negarray) );
933
934 /* renew all variables, important that we do a backwards loop because deletion only affect rear items */
935 for( v = nvars - 1; v >= 0; --v )
936 {
937 var = vars[v];
938
939 /* resolve multi-aggregation */
940 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR || (SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED && SCIPvarGetStatus(SCIPvarGetNegatedVar(var)) == SCIP_VARSTATUS_MULTAGGR) )
941 {
942 SCIP_VAR** consvars;
943 SCIP_Real* consvals;
944 SCIP_Real constant = 0.0;
945 SCIP_Bool easycase;
946 int nconsvars;
947 int requiredsize;
948 int v2;
949
950 nconsvars = 1;
951 SCIP_CALL( SCIPallocBufferArray(scip, &consvars, 1) );
952 SCIP_CALL( SCIPallocBufferArray(scip, &consvals, 1) );
953 consvars[0] = var;
954 consvals[0] = 1.0;
955
956 /* get active variables for new constraint */
957 SCIP_CALL( SCIPgetProbvarLinearSum(scip, consvars, consvals, &nconsvars, nconsvars, &constant, &requiredsize, TRUE) );
958 /* if space was not enough we need to resize the buffers */
959 if( requiredsize > nconsvars )
960 {
961 SCIP_CALL( SCIPreallocBufferArray(scip, &consvars, requiredsize) );
962 SCIP_CALL( SCIPreallocBufferArray(scip, &consvals, requiredsize) );
963
964 SCIP_CALL( SCIPgetProbvarLinearSum(scip, consvars, consvals, &nconsvars, requiredsize, &constant, &requiredsize, TRUE) );
965 assert(requiredsize <= nconsvars);
966 }
967
968 easycase = FALSE;
969
970 if( SCIPisZero(scip, constant) )
971 {
972 /* add active representation */
973 for( v2 = nconsvars - 1; v2 >= 0; --v2 )
974 {
975 if( !SCIPvarIsBinary(consvars[v2]) )
976 break;
977
978 if( !SCIPisEQ(scip, consvals[v2], 1.0) )
979 break;
980 }
981
982 if( v2 < 0 )
983 easycase = TRUE;
984 }
985
986 /* we can easily add the coefficients and still have a logicor constraint */
987 if( easycase )
988 {
989 /* delete old (multi-aggregated) variable */
990 SCIP_CALL( delCoefPos(scip, cons, eventhdlr, v) );
991 ++(*nchgcoefs);
992
993 /* add active representation */
994 for( v2 = nconsvars - 1; v2 >= 0; --v2 )
995 {
996 assert(SCIPvarIsBinary(consvars[v2]));
997 assert(SCIPvarIsActive(consvars[v2]) || (SCIPvarGetStatus(consvars[v2]) == SCIP_VARSTATUS_NEGATED && SCIPvarIsActive(SCIPvarGetNegationVar(consvars[v2]))));
998
999 SCIP_CALL( addCoef(scip, cons, consvars[v2]) );
1000 ++(*nchgcoefs);
1001 }
1002 }
1003 /* we need to degrade this logicor constraint to a linear constraint*/
1004 else if( (ndelconss != NULL && naddconss != NULL) || SCIPconsIsAdded(cons) )
1005 {
1006 char name[SCIP_MAXSTRLEN];
1007 SCIP_CONS* newcons;
1008 SCIP_Real lhs;
1009 SCIP_Real rhs;
1010 int size;
1011 int k;
1012
1013 /* it might happen that there are more than one multi-aggregated variable, so we need to get the whole probvar sum over all variables */
1014
1015 size = MAX(nconsvars, 1) + nvars - 1;
1016
1017 /* memory needed is at least old number of variables - 1 + number of variables in first multi-aggregation */
1018 SCIP_CALL( SCIPreallocBufferArray(scip, &consvars, size) );
1019 SCIP_CALL( SCIPreallocBufferArray(scip, &consvals, size) );
1020
1021 nconsvars = nvars;
1022
1023 /* add constraint variables to new linear variables */
1024 for( k = nvars - 1; k >= 0; --k )
1025 {
1026 consvars[k] = vars[k];
1027 consvals[k] = 1.0;
1028 }
1029
1030 constant = 0.0;
1031
1032 /* get active variables for new constraint */
1033 SCIP_CALL( SCIPgetProbvarLinearSum(scip, consvars, consvals, &nconsvars, size, &constant, &requiredsize, TRUE) );
1034
1035 /* if space was not enough(we found another multi-aggregation), we need to resize the buffers */
1036 if( requiredsize > nconsvars )
1037 {
1038 SCIP_CALL( SCIPreallocBufferArray(scip, &consvars, requiredsize) );
1039 SCIP_CALL( SCIPreallocBufferArray(scip, &consvals, requiredsize) );
1040
1041 SCIP_CALL( SCIPgetProbvarLinearSum(scip, consvars, consvals, &nconsvars, requiredsize, &constant, &requiredsize, TRUE) );
1042 assert(requiredsize <= nconsvars);
1043 }
1044
1045 lhs = 1.0 - constant;
1046 rhs = SCIPinfinity(scip);
1047
1048 /* create linear constraint */
1049 (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s", SCIPconsGetName(cons));
1050 SCIP_CALL( SCIPcreateConsLinear(scip, &newcons, name, nconsvars, consvars, consvals, lhs, rhs,
1051 SCIPconsIsInitial(cons),
1052 SCIPconsIsSeparated(cons), SCIPconsIsEnforced(cons), SCIPconsIsChecked(cons),
1053 SCIPconsIsPropagated(cons), SCIPconsIsLocal(cons), SCIPconsIsModifiable(cons),
1054 SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons), SCIPconsIsStickingAtNode(cons)) );
1055 SCIP_CALL( SCIPaddCons(scip, newcons) );
1056
1057 SCIPdebugMsg(scip, "added linear constraint: ");
1058 SCIPdebugPrintCons(scip, newcons, NULL);
1059 SCIP_CALL( SCIPreleaseCons(scip, &newcons) );
1060
1061 SCIPfreeBufferArray(scip, &consvals);
1062 SCIPfreeBufferArray(scip, &consvars);
1063
1064 /* delete old constraint */
1065 SCIP_CALL( SCIPdelCons(scip, cons) );
1066 if( ndelconss != NULL && naddconss != NULL )
1067 {
1068 assert( naddconss != NULL ); /* for lint */
1069 ++(*ndelconss);
1070 ++(*naddconss);
1071 }
1072
1073 goto TERMINATE;
1074 }
1075 /* we need to degrade this logicor constraint to a linear constraint*/
1076 else
1077 {
1078 if( var != consdata->vars[v] )
1079 {
1080 SCIP_CALL( delCoefPos(scip, cons, eventhdlr, v) );
1081 SCIP_CALL( addCoef(scip, cons, var) );
1082 }
1083
1084 SCIPwarningMessage(scip, "logicor constraint <%s> has a multi-aggregated variable, which was not resolved and therefore could lead to aborts\n", SCIPconsGetName(cons));
1085 }
1086
1087 SCIPfreeBufferArray(scip, &consvals);
1088 SCIPfreeBufferArray(scip, &consvars);
1089 }
1090 else if( var != consdata->vars[v] )
1091 {
1092 assert(SCIPvarIsBinary(var));
1093
1094 SCIP_CALL( delCoefPos(scip, cons, eventhdlr, v) );
1095
1096 /* the binvar representative might be fixed:
1097 * - if fixed to 1, the constraint is redundant
1098 * - if fixed to 0, the representative does not need to be added to the constraint
1099 * - if not fixed, we add the representative to the constraint
1100 */
1101 if( SCIPvarGetLbGlobal(var) > 0.5 )
1102 {
1103 assert(SCIPisFeasEQ(scip, SCIPvarGetUbGlobal(var), 1.0));
1104 *redundant = TRUE;
1105
1106 goto TERMINATE;
1107 }
1108 else if( SCIPvarGetUbGlobal(var) < 0.5 )
1109 {
1110 assert(SCIPisFeasEQ(scip, SCIPvarGetLbGlobal(var), 0.0));
1111 ++(*nchgcoefs);
1112 }
1113 else
1114 {
1115 SCIP_CALL( addCoef(scip, cons, var) );
1116 }
1117 }
1118 }
1119
1120 SCIPdebugMsg(scip, "after fixings: ");
1121 SCIPdebug( SCIP_CALL(consdataPrint(scip, consdata, NULL, TRUE)) );
1122
1123 TERMINATE:
1124 /* free temporary memory */
1125 SCIPfreeBufferArray(scip, &negarray);
1126 SCIPfreeBufferArray(scip, &vars);
1127
1128 consdata->presolved = TRUE;
1129
1130 return SCIP_OKAY;
1131 }
1132
1133 /** analyzes conflicting assignment on given constraint, and adds conflict constraint to problem */
1134 static
1135 SCIP_RETCODE analyzeConflict(
1136 SCIP* scip, /**< SCIP data structure */
1137 SCIP_CONS* cons /**< logic or constraint that detected the conflict */
1138 )
1139 {
1140 SCIP_CONSDATA* consdata;
1141 int v;
1142
1143 /* conflict analysis can only be applied in solving stage and if it is applicable */
1144 if( (SCIPgetStage(scip) != SCIP_STAGE_SOLVING && !SCIPinProbing(scip)) || !SCIPisConflictAnalysisApplicable(scip) )
1145 return SCIP_OKAY;
1146
1147 consdata = SCIPconsGetData(cons);
1148 assert(consdata != NULL);
1149
1150 /* initialize conflict analysis, and add all variables of infeasible constraint to conflict candidate queue */
1151 SCIP_CALL( SCIPinitConflictAnalysis(scip, SCIP_CONFTYPE_PROPAGATION, FALSE) );
1152
1153 for( v = 0; v < consdata->nvars; ++v )
1154 {
1155 SCIP_CALL( SCIPaddConflictBinvar(scip, consdata->vars[v]) );
1156 }
1157
1158 /* analyze the conflict */
1159 SCIP_CALL( SCIPanalyzeConflictCons(scip, cons, NULL) );
1160
1161 return SCIP_OKAY;
1162 }
1163
1164 /** disables or deletes the given constraint, depending on the current depth */
1165 static
1166 SCIP_RETCODE disableCons(
1167 SCIP* scip, /**< SCIP data structure */
1168 SCIP_CONS* cons /**< bound disjunction constraint to be disabled */
1169 )
1170 {
1171 assert(SCIPconsGetValidDepth(cons) <= SCIPgetDepth(scip));
1172
1173 /* in case the logic or constraint is satisfied in the depth where it is also valid, we can delete it */
1174 if( SCIPgetDepth(scip) == SCIPconsGetValidDepth(cons) )
1175 {
1176 SCIP_CALL( SCIPdelCons(scip, cons) );
1177 }
1178 else
1179 {
1180 SCIPdebugMsg(scip, "disabling constraint cons <%s> at depth %d\n", SCIPconsGetName(cons), SCIPgetDepth(scip));
1181 SCIP_CALL( SCIPdisableCons(scip, cons) );
1182 }
1183
1184 return SCIP_OKAY;
1185 }
1186
1187 /** find pairs of negated variables in constraint: constraint is redundant */
1188 /** find sets of equal variables in constraint: multiple entries of variable can be replaced by single entry */
1189 static
1190 SCIP_RETCODE mergeMultiples(
1191 SCIP* scip, /**< SCIP data structure */
1192 SCIP_CONS* cons, /**< logic or constraint */
1193 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */
1194 unsigned char** entries, /**< array to store whether two positions in constraints represent the same variable */
1195 int* nentries, /**< pointer for array size, if array will be to small it's corrected */
1196 SCIP_Bool* redundant, /**< returns whether a variable fixed to one exists in the constraint */
1197 int* nchgcoefs /**< pointer to count number of changed/deleted coefficients */
1198 )
1199 {
1200 SCIP_CONSDATA* consdata;
1201 SCIP_VAR** vars;
1202 int nvars;
1203 SCIP_Bool* negarray;
1204 SCIP_VAR* var;
1205 int v;
1206 int pos;
1207 #ifndef NDEBUG
1208 int nbinvars;
1209 int nintvars;
1210 int nimplvars;
1211 #endif
1212
1213 assert(scip != NULL);
1214 assert(cons != NULL);
1215 assert(eventhdlr != NULL);
1216 assert(*entries != NULL);
1217 assert(nentries != NULL);
1218 assert(redundant != NULL);
1219 assert(nchgcoefs != NULL);
1220
1221 consdata = SCIPconsGetData(cons);
1222 assert(consdata != NULL);
1223
1224 nvars = consdata->nvars;
1225
1226 *redundant = FALSE;
1227
1228 if( consdata->merged )
1229 return SCIP_OKAY;
1230
1231 if( consdata->nvars <= 1 )
1232 {
1233 consdata->merged = TRUE;
1234 return SCIP_OKAY;
1235 }
1236
1237 assert(consdata->vars != NULL && nvars > 0);
1238
1239 #ifndef NDEBUG
1240 nbinvars = SCIPgetNBinVars(scip);
1241 nintvars = SCIPgetNIntVars(scip);
1242 nimplvars = SCIPgetNImplVars(scip);
1243 assert(*nentries >= nbinvars + nintvars + nimplvars);
1244
1245 /* all variables should be active or negative active variables, otherwise something went wrong with applyFixings()
1246 * called before mergeMultiples()
1247 */
1248 assert(consdata->presolved);
1249 #endif
1250
1251 /* allocate temporary memory */
1252 SCIP_CALL( SCIPallocBufferArray(scip, &negarray, nvars) );
1253
1254 vars = consdata->vars;
1255
1256 /* initialize entries array */
1257 for( v = nvars - 1; v >= 0; --v )
1258 {
1259 /* all variables should be active or negative active variables, otherwise something went wrong with applyFixings()
1260 * called before mergeMultiples()
1261 */
1262 assert(SCIPvarIsActive(vars[v]) ||
1263 (SCIPvarGetStatus(vars[v]) == SCIP_VARSTATUS_NEGATED && SCIPvarIsActive(SCIPvarGetNegationVar(vars[v]))));
1264 negarray[v] = SCIPvarIsNegated(vars[v]);
1265 var = negarray[v] ? SCIPvarGetNegationVar(vars[v]) : vars[v];
1266 assert(SCIPvarIsActive(var));
1267
1268 pos = SCIPvarGetProbindex(var);
1269
1270 /* check variable type, either pure binary or an integer/implicit integer variable with 0/1 bounds */
1271 assert((pos < nbinvars && SCIPvarGetType(var) == SCIP_VARTYPE_BINARY)
1272 || (SCIPvarIsBinary(var) &&
1273 ((pos >= nbinvars && pos < nbinvars + nintvars && SCIPvarGetType(var) == SCIP_VARTYPE_INTEGER) ||
1274 (pos >= nbinvars + nintvars && pos < nbinvars + nintvars + nimplvars &&
1275 SCIPvarGetType(var) == SCIP_VARTYPE_IMPLINT))));
1276
1277 /* var is not active yet */
1278 (*entries)[pos] = 0;
1279 }
1280
1281 /* check all vars for multiple entries, do necessary backwards loop because deletion only affect rear items */
1282 for( v = nvars - 1; v >= 0; --v )
1283 {
1284 var = negarray[v] ? SCIPvarGetNegationVar(vars[v]) : vars[v];
1285 assert(SCIPvarIsActive(var));
1286
1287 pos = SCIPvarGetProbindex(var);
1288
1289 /* if var occurs first time in constraint init entries array */
1290 if( (*entries)[pos] == 0 )
1291 (*entries)[pos] = negarray[v] ? 2 : 1;
1292 /* if var occurs second time in constraint, first time it was not negated */
1293 else if( (*entries)[pos] == 1 )
1294 {
1295 if( negarray[v] )
1296 {
1297 SCIPdebugMsg(scip, "logicor constraint <%s> redundant: variable <%s> and its negation are present\n",
1298 SCIPconsGetName(cons), SCIPvarGetName(var));
1299
1300 *redundant = TRUE;
1301 goto TERMINATE;
1302 }
1303 else
1304 {
1305 SCIP_CALL( delCoefPos(scip, cons, eventhdlr, v) );
1306 ++(*nchgcoefs);
1307 }
1308 }
1309 /* if var occurs second time in constraint, first time it was negated */
1310 else
1311 {
1312 if( !negarray[v] )
1313 {
1314 SCIPdebugMsg(scip, "logicor constraint <%s> redundant: variable <%s> and its negation are present\n",
1315 SCIPconsGetName(cons), SCIPvarGetName(var));
1316
1317 *redundant = TRUE;
1318 goto TERMINATE;
1319 }
1320 else
1321 {
1322 SCIP_CALL( delCoefPos(scip, cons, eventhdlr, v) );
1323 ++(*nchgcoefs);
1324 }
1325 }
1326 }
1327
1328 TERMINATE:
1329 /* free temporary memory */
1330 SCIPfreeBufferArray(scip, &negarray);
1331
1332 consdata->merged = TRUE;
1333
1334 return SCIP_OKAY;
1335 }
1336
1337 /** checks constraint for violation only looking at the watched variables, applies fixings if possible */
1338 static
1339 SCIP_RETCODE processWatchedVars(
1340 SCIP* scip, /**< SCIP data structure */
1341 SCIP_CONS* cons, /**< logic or constraint to be processed */
1342 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */
1343 SCIP_Bool* cutoff, /**< pointer to store TRUE, if the node can be cut off */
1344 SCIP_Bool* reduceddom, /**< pointer to store TRUE, if a domain reduction was found */
1345 SCIP_Bool* addcut, /**< pointer to store whether this constraint must be added as a cut */
1346 SCIP_Bool* mustcheck /**< pointer to store whether this constraint must be checked for feasibility */
1347 )
1348 {
1349 SCIP_CONSDATA* consdata;
1350 SCIP_VAR** vars;
1351 SCIP_Longint nbranchings1;
1352 SCIP_Longint nbranchings2;
1353 int nvars;
1354 int watchedvar1;
1355 int watchedvar2;
1356
1357 assert(cons != NULL);
1358 assert(SCIPconsGetHdlr(cons) != NULL);
1359 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
1360 assert(cutoff != NULL);
1361 assert(reduceddom != NULL);
1362 assert(addcut != NULL);
1363 assert(mustcheck != NULL);
1364
1365 consdata = SCIPconsGetData(cons);
1366 assert(consdata != NULL);
1367 assert(consdata->watchedvar1 == -1 || consdata->watchedvar1 != consdata->watchedvar2);
1368
1369 *addcut = FALSE;
1370 *mustcheck = FALSE;
1371
1372 SCIPdebugMsg(scip, "processing watched variables of constraint <%s>\n", SCIPconsGetName(cons));
1373
1374 vars = consdata->vars;
1375 nvars = consdata->nvars;
1376 assert(nvars == 0 || vars != NULL);
1377
1378 /* check watched variables if they are fixed to one */
1379 if( consdata->watchedvar1 >= 0 && SCIPvarGetLbLocal(vars[consdata->watchedvar1]) > 0.5 )
1380 {
1381 /* the variable is fixed to one, making the constraint redundant -> disable the constraint */
1382 SCIPdebugMsg(scip, " -> disabling constraint <%s> (watchedvar1 fixed to 1.0)\n", SCIPconsGetName(cons));
1383 SCIP_CALL( disableCons(scip, cons) );
1384 return SCIP_OKAY;
1385 }
1386 if( consdata->watchedvar2 >= 0 && SCIPvarGetLbLocal(vars[consdata->watchedvar2]) > 0.5 )
1387 {
1388 /* the variable is fixed to one, making the constraint redundant -> disable the constraint */
1389 SCIPdebugMsg(scip, " -> disabling constraint <%s> (watchedvar2 fixed to 1.0)\n", SCIPconsGetName(cons));
1390 SCIP_CALL( disableCons(scip, cons) );
1391 return SCIP_OKAY;
1392 }
1393
1394 /* check if watched variables are still unfixed */
1395 watchedvar1 = -1;
1396 watchedvar2 = -1;
1397 nbranchings1 = SCIP_LONGINT_MAX;
1398 nbranchings2 = SCIP_LONGINT_MAX;
1399 if( consdata->watchedvar1 >= 0 && SCIPvarGetUbLocal(vars[consdata->watchedvar1]) > 0.5 )
1400 {
1401 watchedvar1 = consdata->watchedvar1;
1402 nbranchings1 = -1; /* prefer keeping the watched variable */
1403 }
1404 if( consdata->watchedvar2 >= 0 && SCIPvarGetUbLocal(vars[consdata->watchedvar2]) > 0.5 )
1405 {
1406 if( watchedvar1 == -1 )
1407 {
1408 watchedvar1 = consdata->watchedvar2;
1409 nbranchings1 = -1; /* prefer keeping the watched variable */
1410 }
1411 else
1412 {
1413 watchedvar2 = consdata->watchedvar2;
1414 nbranchings2 = -1; /* prefer keeping the watched variable */
1415 }
1416 }
1417 assert(watchedvar1 >= 0 || watchedvar2 == -1);
1418 assert(nbranchings1 <= nbranchings2);
1419
1420 /* search for new watched variables */
1421 if( watchedvar2 == -1 )
1422 {
1423 int v;
1424
1425 for( v = 0; v < nvars; ++v )
1426 {
1427 SCIP_Longint nbranchings;
1428
1429 /* don't process the watched variables again */
1430 if( v == consdata->watchedvar1 || v == consdata->watchedvar2 )
1431 continue;
1432
1433 /* check, if the variable is fixed */
1434 if( SCIPvarGetUbLocal(vars[v]) < 0.5 )
1435 continue;
1436
1437 /* check, if the literal is satisfied */
1438 if( SCIPvarGetLbLocal(vars[v]) > 0.5 )
1439 {
1440 assert(v != consdata->watchedvar1);
1441 assert(v != consdata->watchedvar2);
1442
1443 /* the variable is fixed to one, making the constraint redundant;
1444 * make sure, the feasible variable is watched and disable the constraint
1445 */
1446 SCIPdebugMsg(scip, " -> disabling constraint <%s> (variable <%s> fixed to 1.0)\n",
1447 SCIPconsGetName(cons), SCIPvarGetName(vars[v]));
1448 if( consdata->watchedvar1 != -1 )
1449 {
1450 SCIP_CALL( switchWatchedvars(scip, cons, eventhdlr, consdata->watchedvar1, v) );
1451 }
1452 else
1453 {
1454 SCIP_CALL( switchWatchedvars(scip, cons, eventhdlr, v, consdata->watchedvar2) );
1455 }
1456 SCIP_CALL( disableCons(scip, cons) );
1457 return SCIP_OKAY;
1458 }
1459
1460 /* the variable is unfixed and can be used as watched variable */
1461 nbranchings = SCIPvarGetNBranchingsCurrentRun(vars[v], SCIP_BRANCHDIR_DOWNWARDS);
1462 assert(nbranchings >= 0);
1463 if( nbranchings < nbranchings2 )
1464 {
1465 if( nbranchings < nbranchings1 )
1466 {
1467 watchedvar2 = watchedvar1;
1468 nbranchings2 = nbranchings1;
1469 watchedvar1 = v;
1470 nbranchings1 = nbranchings;
1471 }
1472 else
1473 {
1474 watchedvar2 = v;
1475 nbranchings2 = nbranchings;
1476 }
1477 }
1478 }
1479 }
1480 assert(nbranchings1 <= nbranchings2);
1481 assert(watchedvar1 >= 0 || watchedvar2 == -1);
1482
1483 if( watchedvar1 == -1 )
1484 {
1485 /* there is no unfixed variable left -> the constraint is infeasible
1486 * - a modifiable constraint must be added as a cut and further pricing must be performed in the LP solving loop
1487 * - an unmodifiable constraint is infeasible and the node can be cut off
1488 */
1489 assert(watchedvar2 == -1);
1490
1491 SCIPdebugMsg(scip, " -> constraint <%s> is infeasible\n", SCIPconsGetName(cons));
1492
1493 SCIP_CALL( SCIPresetConsAge(scip, cons) );
1494 if( SCIPconsIsModifiable(cons) )
1495 *addcut = TRUE;
1496 else
1497 {
1498 /* use conflict analysis to get a conflict constraint out of the conflicting assignment */
1499 SCIP_CALL( analyzeConflict(scip, cons) );
1500
1501 /* mark the node to be cut off */
1502 *cutoff = TRUE;
1503 }
1504 }
1505 else if( watchedvar2 == -1 )
1506 {
1507 /* there is only one unfixed variable:
1508 * - a modifiable constraint must be checked manually
1509 * - an unmodifiable constraint is feasible and can be disabled after the remaining variable is fixed to one
1510 */
1511 assert(0 <= watchedvar1 && watchedvar1 < nvars);
1512 assert(SCIPisFeasEQ(scip, SCIPvarGetLbLocal(vars[watchedvar1]), 0.0));
1513 assert(SCIPisFeasEQ(scip, SCIPvarGetUbLocal(vars[watchedvar1]), 1.0));
1514 if( SCIPconsIsModifiable(cons) )
1515 *mustcheck = TRUE;
1516 else
1517 {
1518 SCIP_Bool infbdchg;
1519
1520 /* fixed remaining variable to one and disable constraint; make sure, the fixed-to-one variable is watched */
1521 SCIPdebugMsg(scip, " -> single-literal constraint <%s> (fix <%s> to 1.0) at depth %d\n",
1522 SCIPconsGetName(cons), SCIPvarGetName(vars[watchedvar1]), SCIPgetDepth(scip));
1523 SCIP_CALL( SCIPinferBinvarCons(scip, vars[watchedvar1], TRUE, cons, 0, &infbdchg, NULL) );
1524 assert(!infbdchg);
1525 SCIP_CALL( SCIPresetConsAge(scip, cons) );
1526 if( watchedvar1 != consdata->watchedvar1 ) /* keep one of the watched variables */
1527 {
1528 SCIP_CALL( switchWatchedvars(scip, cons, eventhdlr, watchedvar1, consdata->watchedvar1) );
1529 }
1530 SCIP_CALL( disableCons(scip, cons) );
1531 *reduceddom = TRUE;
1532 }
1533 }
1534 else
1535 {
1536 SCIPdebugMsg(scip, " -> new watched variables <%s> and <%s> of constraint <%s> are still unfixed\n",
1537 SCIPvarGetName(vars[watchedvar1]), SCIPvarGetName(vars[watchedvar2]), SCIPconsGetName(cons));
1538
1539 /* switch to the new watched variables */
1540 SCIP_CALL( switchWatchedvars(scip, cons, eventhdlr, watchedvar1, watchedvar2) );
1541
1542 /* there are at least two unfixed variables -> the constraint must be checked manually */
1543 *mustcheck = TRUE;
1544
1545 /* disable propagation of constraint until a watched variable gets fixed */
1546 SCIP_CALL( SCIPdisableConsPropagation(scip, cons) );
1547
1548 /* increase aging counter */
1549 SCIP_CALL( SCIPaddConsAge(scip, cons, AGEINCREASE(consdata->nvars)) );
1550 }
1551
1552 return SCIP_OKAY;
1553 }
1554
1555 /** checks constraint for violation, returns TRUE iff constraint is feasible */
1556 static
1557 SCIP_Bool isConsViolated(
1558 SCIP* scip, /**< SCIP data structure */
1559 SCIP_CONS* cons, /**< logic or constraint to be checked */
1560 SCIP_SOL* sol /**< primal CIP solution */
1561 )
1562 {
1563 SCIP_CONSDATA* consdata;
1564 SCIP_VAR** vars;
1565 SCIP_Real solval;
1566 SCIP_Real sum;
1567 int nvars;
1568 int v;
1569
1570 consdata = SCIPconsGetData(cons);
1571 assert(consdata != NULL);
1572
1573 vars = consdata->vars;
1574 nvars = consdata->nvars;
1575
1576 /* calculate the constraint's activity */
1577 sum = 0.0;
1578 for( v = 0; v < nvars && sum < 1.0; ++v )
1579 {
1580 assert(SCIPvarIsBinary(vars[v]));
1581
1582 solval = SCIPgetSolVal(scip, sol, vars[v]);
1583 assert(SCIPisFeasGE(scip, solval, 0.0) && SCIPisFeasLE(scip, solval, 1.0));
1584
1585 sum += solval;
1586 }
1587
1588 /* calculate constraint violation and update it in solution */
1589 if( sol != NULL ){
1590 SCIP_Real absviol = 1.0 - sum;
1591 SCIP_Real relviol = SCIPrelDiff(1.0, sum);
1592 SCIPupdateSolLPConsViolation(scip, sol, absviol, relviol);
1593 }
1594
1595 return SCIPisFeasLT(scip, sum, 1.0);
1596 }
1597
1598 /** creates an LP row in a logic or constraint data object */
1599 static
1600 SCIP_RETCODE createRow(
1601 SCIP* scip, /**< SCIP data structure */
1602 SCIP_CONS* cons /**< logic or constraint */
1603 )
1604 {
1605 SCIP_CONSDATA* consdata;
1606
1607 consdata = SCIPconsGetData(cons);
1608 assert(consdata != NULL);
1609 assert(consdata->row == NULL);
1610
1611 SCIP_CALL( SCIPcreateEmptyRowCons(scip, &consdata->row, cons, SCIPconsGetName(cons), 1.0, SCIPinfinity(scip),
1612 SCIPconsIsLocal(cons), SCIPconsIsModifiable(cons), SCIPconsIsRemovable(cons)) );
1613
1614 SCIP_CALL( SCIPaddVarsToRowSameCoef(scip, consdata->row, consdata->nvars, consdata->vars, 1.0) );
1615
1616 return SCIP_OKAY;
1617 }
1618
1619 /** adds logicor constraint as row to the NLP, if not added yet */
1620 static
1621 SCIP_RETCODE addNlrow(
1622 SCIP* scip, /**< SCIP data structure */
1623 SCIP_CONS* cons /**< logicor constraint */
1624 )
1625 {
1626 SCIP_CONSDATA* consdata;
1627
1628 assert(SCIPisNLPConstructed(scip));
1629
1630 /* skip deactivated, redundant, or local constraints (the NLP does not allow for local rows at the moment) */
1631 if( !SCIPconsIsActive(cons) || !SCIPconsIsChecked(cons) || SCIPconsIsLocal(cons) )
1632 return SCIP_OKAY;
1633
1634 consdata = SCIPconsGetData(cons);
1635 assert(consdata != NULL);
1636
1637 if( consdata->nlrow == NULL )
1638 {
1639 SCIP_Real* coefs;
1640 int i;
1641
1642 SCIP_CALL( SCIPallocBufferArray(scip, &coefs, consdata->nvars) );
1643 for( i = 0; i < consdata->nvars; ++i )
1644 coefs[i] = 1.0;
1645
1646 SCIP_CALL( SCIPcreateNlRow(scip, &consdata->nlrow, SCIPconsGetName(cons),
1647 0.0, consdata->nvars, consdata->vars, coefs, NULL, 1.0, SCIPinfinity(scip), SCIP_EXPRCURV_LINEAR) );
1648 assert(consdata->nlrow != NULL);
1649
1650 SCIPfreeBufferArray(scip, &coefs);
1651 }
1652
1653 if( !SCIPnlrowIsInNLP(consdata->nlrow) )
1654 {
1655 SCIP_CALL( SCIPaddNlRow(scip, consdata->nlrow) );
1656 }
1657
1658 return SCIP_OKAY;
1659 }
1660
1661 /** adds logic or constraint as cut to the LP */
1662 static
1663 SCIP_RETCODE addCut(
1664 SCIP* scip, /**< SCIP data structure */
1665 SCIP_CONS* cons, /**< logic or constraint */
1666 SCIP_Bool* cutoff /**< whether a cutoff has been detected */
1667 )
1668 {
1669 SCIP_CONSDATA* consdata;
1670
1671 assert( cutoff != NULL );
1672 *cutoff = FALSE;
1673
1674 consdata = SCIPconsGetData(cons);
1675 assert(consdata != NULL);
1676
1677 if( consdata->row == NULL )
1678 {
1679 /* convert logic or constraint data into LP row */
1680 SCIP_CALL( createRow(scip, cons) );
1681 }
1682 assert(consdata->row != NULL);
1683
1684 /* insert LP row as cut */
1685 if( !SCIProwIsInLP(consdata->row) )
1686 {
1687 SCIPdebugMsg(scip, "adding constraint <%s> as cut to the LP\n", SCIPconsGetName(cons));
1688 SCIP_CALL( SCIPaddRow(scip, consdata->row, FALSE, cutoff) );
1689 }
1690
1691 return SCIP_OKAY;
1692 }
1693
1694 /** checks constraint for violation, and adds it as a cut if possible */
1695 static
1696 SCIP_RETCODE separateCons(
1697 SCIP* scip, /**< SCIP data structure */
1698 SCIP_CONS* cons, /**< logic or constraint to be separated */
1699 SCIP_SOL* sol, /**< primal CIP solution, NULL for current LP solution */
1700 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */
1701 SCIP_Bool* cutoff, /**< pointer to store TRUE, if the node can be cut off */
1702 SCIP_Bool* separated, /**< pointer to store TRUE, if a cut was found */
1703 SCIP_Bool* reduceddom /**< pointer to store TRUE, if a domain reduction was found */
1704 )
1705 {
1706 SCIP_Bool addcut;
1707 SCIP_Bool mustcheck;
1708
1709 assert(cons != NULL);
1710 assert(SCIPconsGetHdlr(cons) != NULL);
1711 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
1712 assert(cutoff != NULL);
1713 assert(separated != NULL);
1714 assert(reduceddom != NULL);
1715
1716 *cutoff = FALSE;
1717 SCIPdebugMsg(scip, "separating constraint <%s>\n", SCIPconsGetName(cons));
1718
1719 /* update and check the watched variables, if they were changed since last processing */
1720 if( sol == NULL && SCIPconsIsPropagationEnabled(cons) )
1721 {
1722 SCIP_CALL( processWatchedVars(scip, cons, eventhdlr, cutoff, reduceddom, &addcut, &mustcheck) );
1723 }
1724 else
1725 {
1726 addcut = FALSE;
1727 mustcheck = TRUE;
1728 }
1729
1730 if( mustcheck )
1731 {
1732 SCIP_CONSDATA* consdata;
1733
1734 assert(!addcut);
1735
1736 consdata = SCIPconsGetData(cons);
1737 assert(consdata != NULL);
1738
1739 /* variable's fixings didn't give us any information -> we have to check the constraint */
1740 if( sol == NULL && consdata->row != NULL )
1741 {
1742 /* skip constraints already in the LP */
1743 if( SCIProwIsInLP(consdata->row) )
1744 return SCIP_OKAY;
1745 else
1746 {
1747 SCIP_Real feasibility;
1748
1749 assert(!SCIProwIsInLP(consdata->row));
1750 feasibility = SCIPgetRowLPFeasibility(scip, consdata->row);
1751 addcut = SCIPisFeasNegative(scip, feasibility);
1752 }
1753 }
1754 else
1755 {
1756 addcut = isConsViolated(scip, cons, sol);
1757 }
1758 }
1759
1760 if( addcut )
1761 {
1762 /* insert LP row as cut */
1763 SCIP_CALL( addCut(scip, cons, cutoff) );
1764 SCIP_CALL( SCIPresetConsAge(scip, cons) );
1765 *separated = TRUE;
1766 }
1767
1768 return SCIP_OKAY;
1769 }
1770
1771 /** enforces the pseudo solution on the given constraint */
1772 static
1773 SCIP_RETCODE enforcePseudo(
1774 SCIP* scip, /**< SCIP data structure */
1775 SCIP_CONS* cons, /**< logic or constraint to be separated */
1776 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */
1777 SCIP_Bool* cutoff, /**< pointer to store TRUE, if the node can be cut off */
1778 SCIP_Bool* infeasible, /**< pointer to store TRUE, if the constraint was infeasible */
1779 SCIP_Bool* reduceddom, /**< pointer to store TRUE, if a domain reduction was found */
1780 SCIP_Bool* solvelp /**< pointer to store TRUE, if the LP has to be solved */
1781 )
1782 {
1783 SCIP_Bool addcut;
1784 SCIP_Bool mustcheck;
1785
1786 assert(!SCIPhasCurrentNodeLP(scip));
1787 assert(cons != NULL);
1788 assert(SCIPconsGetHdlr(cons) != NULL);
1789 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
1790 assert(cutoff != NULL);
1791 assert(infeasible != NULL);
1792 assert(reduceddom != NULL);
1793 assert(solvelp != NULL);
1794
1795 /* update and check the watched variables, if they were changed since last processing */
1796 if( SCIPconsIsPropagationEnabled(cons) )
1797 {
1798 SCIP_CALL( processWatchedVars(scip, cons, eventhdlr, cutoff, reduceddom, &addcut, &mustcheck) );
1799 }
1800 else
1801 {
1802 addcut = FALSE;
1803 mustcheck = TRUE;
1804 }
1805
1806 if( mustcheck )
1807 {
1808 assert(!addcut);
1809
1810 if( isConsViolated(scip, cons, NULL) )
1811 {
1812 /* constraint was infeasible -> reset age */
1813 SCIP_CALL( SCIPresetConsAge(scip, cons) );
1814 *infeasible = TRUE;
1815 }
1816 }
1817 else if( addcut )
1818 {
1819 /* a cut must be added to the LP -> we have to solve the LP immediately */
1820 SCIP_CALL( SCIPresetConsAge(scip, cons) );
1821 *solvelp = TRUE;
1822 }
1823
1824 return SCIP_OKAY;
1825 }
1826
1827 /** sorts logicor constraint's variables by non-decreasing variable index */
1828 static
1829 void consdataSort(
1830 SCIP_CONSDATA* consdata /**< linear constraint data */
1831 )
1832 {
1833 assert(consdata != NULL);
1834
(1) Event cond_true: |
Condition "!consdata->sorted", taking true branch. |
1835 if( !consdata->sorted )
1836 {
(2) Event cond_false: |
Condition "consdata->nvars <= 1", taking false branch. |
1837 if( consdata->nvars <= 1 )
1838 consdata->sorted = TRUE;
1839 else
(3) Event else_branch: |
Reached else branch. |
1840 {
1841 SCIP_VAR* var1 = NULL;
1842 SCIP_VAR* var2 = NULL;
1843
1844 /* remember watch variables */
(4) Event cond_true: |
Condition "consdata->watchedvar1 != -1", taking true branch. |
1845 if( consdata->watchedvar1 != -1 )
1846 {
1847 var1 = consdata->vars[consdata->watchedvar1];
1848 assert(var1 != NULL);
1849 consdata->watchedvar1 = -1;
(5) Event cond_true: |
Condition "consdata->watchedvar2 != -1", taking true branch. |
1850 if( consdata->watchedvar2 != -1 )
1851 {
1852 var2 = consdata->vars[consdata->watchedvar2];
1853 assert(var2 != NULL);
1854 consdata->watchedvar2 = -1;
1855 }
1856 }
1857 assert(consdata->watchedvar1 == -1);
1858 assert(consdata->watchedvar2 == -1);
1859 assert(var1 != NULL || var2 == NULL);
1860
1861 /* sort variables after index */
1862 SCIPsortPtr((void**)consdata->vars, SCIPvarComp, consdata->nvars);
1863 consdata->sorted = TRUE;
1864
1865 /* correct watched variables */
(6) Event cond_true: |
Condition "var1 != NULL", taking true branch. |
1866 if( var1 != NULL )
1867 {
1868 int pos;
1869 #ifndef NDEBUG
1870 SCIP_Bool found;
1871
1872 found = SCIPsortedvecFindPtr((void**)consdata->vars, SCIPvarComp, (void*)var1, consdata->nvars, &pos);
1873 assert(found);
1874 #else
1875 SCIPsortedvecFindPtr((void**)consdata->vars, SCIPvarComp, (void*)var1, consdata->nvars, &pos);
1876 #endif
1877 assert(pos >= 0 && pos < consdata->nvars);
1878 consdata->watchedvar1 = pos;
1879
(7) Event cond_true: |
Condition "var2 != NULL", taking true branch. |
1880 if( var2 != NULL )
1881 {
1882 #ifndef NDEBUG
1883 found = SCIPsortedvecFindPtr((void**)consdata->vars, SCIPvarComp, (void*)var2, consdata->nvars, &pos);
1884 assert(found);
1885 #else
1886 SCIPsortedvecFindPtr((void**)consdata->vars, SCIPvarComp, (void*)var2, consdata->nvars, &pos);
1887 #endif
1888 assert(pos >= 0 && pos < consdata->nvars);
1889 consdata->watchedvar2 = pos;
1890 }
1891 }
1892 }
1893 }
1894
1895 #ifdef SCIP_DEBUG
1896 /* check sorting */
1897 {
1898 int v;
1899
1900 for( v = consdata->nvars - 1; v > 0; --v )
1901 {
1902 assert(SCIPvarCompare(consdata->vars[v], consdata->vars[v - 1]) >= 0);
1903 }
1904 }
1905 #endif
1906 }
1907
1908 /** gets the key of the given element */
1909 static
1910 SCIP_DECL_HASHGETKEY(hashGetKeyLogicorcons)
1911 { /*lint --e{715}*/
1912 /* the key is the element itself */
1913 return elem;
1914 }
1915
1916 /** returns TRUE iff both keys are equal; two constraints are equal if they have the same variables */
1917 static
1918 SCIP_DECL_HASHKEYEQ(hashKeyEqLogicorcons)
1919 {
1920 SCIP_CONSDATA* consdata1;
1921 SCIP_CONSDATA* consdata2;
1922 SCIP_Bool coefsequal;
1923 int i;
1924 #ifndef NDEBUG
1925 SCIP* scip;
1926
1927 scip = (SCIP*)userptr;
1928 assert(scip != NULL);
1929 #endif
1930
1931 consdata1 = SCIPconsGetData((SCIP_CONS*)key1);
1932 consdata2 = SCIPconsGetData((SCIP_CONS*)key2);
1933
1934 /* checks trivial case */
1935 if( consdata1->nvars != consdata2->nvars )
1936 return FALSE;
1937
1938 /* sorts the constraints */
1939 consdataSort(consdata1);
1940 consdataSort(consdata2);
1941 assert(consdata1->sorted);
1942 assert(consdata2->sorted);
1943
1944 coefsequal = TRUE;
1945
1946 for( i = 0; i < consdata1->nvars ; ++i )
1947 {
1948 /* tests if variables are equal */
1949 if( consdata1->vars[i] != consdata2->vars[i] )
1950 {
1951 assert(SCIPvarCompare(consdata1->vars[i], consdata2->vars[i]) == 1 ||
1952 SCIPvarCompare(consdata1->vars[i], consdata2->vars[i]) == -1);
1953 coefsequal = FALSE;
1954 break;
1955 }
1956 assert(SCIPvarCompare(consdata1->vars[i], consdata2->vars[i]) == 0);
1957 }
1958
1959 return coefsequal;
1960 }
1961
1962 /** returns the hash value of the key */
1963 static
1964 SCIP_DECL_HASHKEYVAL(hashKeyValLogicorcons)
1965 { /*lint --e{715}*/
1966 SCIP_CONSDATA* consdata;
1967 int minidx;
1968 int mididx;
1969 int maxidx;
1970
1971 consdata = SCIPconsGetData((SCIP_CONS*)key);
1972 assert(consdata != NULL);
1973 assert(consdata->sorted);
1974 assert(consdata->nvars > 0);
1975
1976 minidx = SCIPvarGetIndex(consdata->vars[0]);
1977 mididx = SCIPvarGetIndex(consdata->vars[consdata->nvars / 2]);
1978 maxidx = SCIPvarGetIndex(consdata->vars[consdata->nvars - 1]);
1979 assert(minidx >= 0 && minidx <= maxidx);
1980
1981 return SCIPhashFour(consdata->nvars, minidx, mididx, maxidx);
1982 }
1983
1984 /** compares each constraint with all other constraints for a possible duplication and removes duplicates using a hash
1985 * table; also @see removeRedundantConssAndNonzeros()
1986 */
1987 static
1988 SCIP_RETCODE detectRedundantConstraints(
1989 SCIP* scip, /**< SCIP data structure */
1990 BMS_BLKMEM* blkmem, /**< block memory */
1991 SCIP_CONS** conss, /**< constraint set */
1992 int nconss, /**< number of constraints in constraint set */
1993 int* firstchange, /**< pointer to store first changed constraint */
1994 int* ndelconss /**< pointer to count number of deleted constraints */
1995 )
1996 {
1997 SCIP_HASHTABLE* hashtable;
1998 int hashtablesize;
1999 int c;
2000
2001 assert(conss != NULL);
2002 assert(ndelconss != NULL);
2003
2004 /* create a hash table for the constraint set */
2005 hashtablesize = nconss;
2006 hashtablesize = MAX(hashtablesize, HASHSIZE_LOGICORCONS);
2007 SCIP_CALL( SCIPhashtableCreate(&hashtable, blkmem, hashtablesize,
2008 hashGetKeyLogicorcons, hashKeyEqLogicorcons, hashKeyValLogicorcons, (void*) scip) );
2009
2010 /* check all constraints in the given set for redundancy */
2011 for( c = 0; c < nconss; ++c )
2012 {
2013 SCIP_CONS* cons0;
2014 SCIP_CONS* cons1;
2015 SCIP_CONSDATA* consdata0;
2016
2017 cons0 = conss[c];
2018
2019 if( !SCIPconsIsActive(cons0) || SCIPconsIsModifiable(cons0) )
2020 continue;
2021
2022 consdata0 = SCIPconsGetData(cons0);
2023 /* sort the constraint */
2024 consdataSort(consdata0);
2025 assert(consdata0->sorted);
2026
2027 /* get constraint from current hash table with same variables as cons0 */
2028 cons1 = (SCIP_CONS*)(SCIPhashtableRetrieve(hashtable, (void*)cons0));
2029
2030 if( cons1 != NULL )
2031 {
2032 #ifndef NDEBUG
2033 SCIP_CONSDATA* consdata1;
2034 #endif
2035
2036 assert(SCIPconsIsActive(cons1));
2037 assert(!SCIPconsIsModifiable(cons1));
2038
2039 #ifndef NDEBUG
2040 consdata1 = SCIPconsGetData(cons1);
2041 #endif
2042 assert(consdata1 != NULL);
2043 assert(consdata0->nvars >= 1 && consdata0->nvars == consdata1->nvars);
2044
2045 assert(consdata0->sorted && consdata1->sorted);
2046 assert(consdata0->vars[0] == consdata1->vars[0]);
2047
2048 /* update flags of constraint which caused the redundancy s.t. nonredundant information doesn't get lost */
2049 /* coverity[swapped_arguments] */
2050 SCIP_CALL( SCIPupdateConsFlags(scip, cons1, cons0) );
2051
2052 /* delete consdel */
2053 SCIP_CALL( SCIPdelCons(scip, cons0) );
2054 (*ndelconss)++;
2055
2056 /* update the first changed constraint to begin the next aggregation round with */
2057 if( consdata0->changed && SCIPconsGetPos(cons1) < *firstchange )
2058 *firstchange = SCIPconsGetPos(cons1);
2059
2060 assert(SCIPconsIsActive(cons1));
2061 }
2062 else
2063 {
2064 /* no such constraint in current hash table: insert cons0 into hash table */
2065 SCIP_CALL( SCIPhashtableInsert(hashtable, (void*) cons0) );
2066 }
2067 }
2068
2069 /* free hash table */
2070 SCIPhashtableFree(&hashtable);
2071
2072 return SCIP_OKAY;
2073 }
2074
2075 /** removes the redundant second constraint and updates the flags of the first one */
2076 static
2077 SCIP_RETCODE removeRedundantCons(
2078 SCIP* scip, /**< SCIP data structure */
2079 SCIP_CONS* cons0, /**< constraint that should stay */
2080 SCIP_CONS* cons1, /**< constraint that should be deleted */
2081 int* ndelconss /**< pointer to count number of deleted constraints */
2082 )
2083 {
2084 assert(ndelconss != NULL);
2085
2086 SCIPdebugMsg(scip, " -> removing logicor constraint <%s> which is redundant to <%s>\n",
2087 SCIPconsGetName(cons1), SCIPconsGetName(cons0));
2088 SCIPdebugPrintCons(scip, cons0, NULL);
2089 SCIPdebugPrintCons(scip, cons1, NULL);
2090
2091 /* update flags of cons0 */
2092 SCIP_CALL( SCIPupdateConsFlags(scip, cons0, cons1) );
2093
2094 /* delete cons1 */
2095 SCIP_CALL( SCIPdelCons(scip, cons1) );
2096 (*ndelconss)++;
2097
2098 return SCIP_OKAY;
2099 }
2100
2101
2102 /** compute and return a signature for given variables */
2103 static
2104 unsigned int calcSignature(
2105 SCIP_VAR** vars, /**< variables to calculate the signature for */
2106 int nvars /**< number of variables to calculate the signature for */
2107 )
2108 {
2109 unsigned int signature = 0;
2110 int v;
2111
2112 assert(vars != NULL);
2113 assert(nvars >= 1);
2114
2115 for( v = nvars - 1; v >= 0; --v )
2116 {
2117 signature |= ((unsigned int)1 << ((unsigned int)SCIPvarGetIndex(vars[v]) % (sizeof(unsigned int) * 8)));
2118 }
2119
2120 return signature;
2121 }
2122
2123 /** compute the constraint signature which is used to detect constraints, that contain potentially the same set of
2124 * variables
2125 */
2126 static
2127 void consdataCalcSignature(
2128 SCIP_CONSDATA* consdata /**< logicor constraint data */
2129 )
2130 {
2131 if( consdata->validsignature )
2132 return;
2133
2134 consdata->signature = calcSignature(consdata->vars, consdata->nvars);
2135 consdata->validsignature = TRUE;
2136 }
2137
2138 /** remove a constraint from the column representation */
2139 static
2140 void removeConsFromOccurList(
2141 SCIP_CONS* cons, /**< logicor constraint */
2142 SCIP_HASHMAP* varstopos, /**< map for mapping variables to positions in the occurlist */
2143 SCIP_CONS*** occurlist, /**< column representation of logicor constraints */
2144 int* noccurlistentries, /**< arrray with number of constraints for each variable in the occurlist */
2145 int occurlistlength /**< number of columns in the occurlist */
2146 )
2147 {
2148 SCIP_VAR** vars;
2149 SCIP_VAR* var;
2150 SCIP_CONSDATA* consdata;
2151 int nvars;
2152 int pos;
2153 int v;
2154 int l;
2155
2156 assert(cons != NULL);
2157 assert(SCIPconsIsActive(cons));
2158 assert(varstopos != NULL);
2159 assert(occurlist != NULL);
2160 assert(noccurlistentries != NULL);
2161
2162 consdata = SCIPconsGetData(cons);
2163 assert(consdata != NULL);
2164
2165 nvars = consdata->nvars;
2166 assert(nvars >= 1);
2167 vars = consdata->vars;
2168 assert(vars != NULL);
2169
2170 /* remove constraint from list */
2171 for( v = nvars - 1; v >= 0; --v )
2172 {
2173 var = vars[v];
2174
2175 assert(SCIPhashmapExists(varstopos, (void*) var));
2176
2177 pos = SCIPhashmapGetImageInt(varstopos, (void*)var);
2178 assert(0 < pos && pos <= occurlistlength);
2179
2180 --pos;
2181
2182 /* remove for each variable one corresponding entry */
2183 for( l = noccurlistentries[pos] - 1; l >= 0; --l )
2184 {
2185 if( occurlist[pos][l] == cons )
2186 {
2187 --noccurlistentries[pos];
2188 assert(noccurlistentries[pos] >= 0);
2189
2190 occurlist[pos][l] = occurlist[pos][noccurlistentries[pos]];
2191 break;
2192 }
2193 }
2194 assert(l >= 0);
2195 }
2196 }
2197
2198 /** determine shortest constraint list in column representation */
2199 static
2200 void findShortestOccurlist(
2201 SCIP_VAR** vars, /**< variables to find the shortestlist for */
2202 int nvars, /**< number of variables */
2203 SCIP_HASHMAP* varstopos, /**< map for mapping variables to positions in the occurlist */
2204 SCIP_CONS*** occurlist, /**< column representation of logicor constraints */
2205 int* noccurlistentries, /**< arrray with number of constraints for each variable in the occurlist */
2206 int occurlistlength, /**< number of columns in the occurlist */
2207 int* nentries, /**< pointer to store the number of entries in the shortest list */
2208 SCIP_CONS*** shortestlist /**< pointer to store smallest array with constraints */
2209 )
2210 {
2211 SCIP_VAR* var;
2212 int pos;
2213 int v;
2214
2215 assert(vars != 0);
2216 assert(nvars >= 1);
2217 assert(varstopos != NULL);
2218 assert(occurlist != NULL);
2219 assert(noccurlistentries != NULL);
2220 assert(nentries != NULL);
2221 assert(shortestlist != NULL);
2222
2223 *nentries = INT_MAX;
2224 *shortestlist = NULL;
2225
2226 /* find the shortest list */
2227 for( v = nvars - 1; v >= 0; --v )
2228 {
2229 var = vars[v];
2230 assert(var != NULL);
2231
2232 /* it might be that a variable is not yet put into the occurlist, then this constraint cannot cover another */
2233 if( !SCIPhashmapExists(varstopos, (void*) var) )
2234 {
2235 *nentries = 0;
2236 return;
2237 }
2238
2239 pos = SCIPhashmapGetImageInt(varstopos, (void*)var);
2240 assert(0 < pos && pos <= occurlistlength);
2241
2242 --pos;
2243
2244 /* remember the shortest list */
2245 if( noccurlistentries[pos] < *nentries )
2246 {
2247 *nentries = noccurlistentries[pos];
2248 *shortestlist = occurlist[pos];
2249 }
2250 }
2251 }
2252
2253 /** run a pairwise comparison for detecting subset-constraints of other constraint while using a signature */
2254 static
2255 SCIP_RETCODE removeRedundantConss(
2256 SCIP* scip, /**< SCIP data structure */
2257 SCIP_CONS* cons, /**< logicor constraint to check if it covers another */
2258 SCIP_HASHMAP* varstopos, /**< map for mapping variables to positions in the occurlist */
2259 SCIP_CONS*** occurlist, /**< column representation of logicor constraints */
2260 int* noccurlistentries, /**< arrray with number of constraints for each variable in the occurlist */
2261 int occurlistlength, /**< number of columns in the occurlist */
2262 int* ndelconss /**< pointer to store the number of deleted constraints */
2263 )
2264 {
2265 SCIP_CONS** shortestlist;
2266 SCIP_VAR** vars;
2267 SCIP_CONS* cons1;
2268 SCIP_VAR* var;
2269 SCIP_CONSDATA* consdata;
2270 int nentries;
2271 int c;
2272 int v;
2273
2274 assert(scip != NULL);
2275 assert(cons != NULL);
2276 assert(SCIPconsIsActive(cons));
2277 assert(!SCIPconsIsModifiable(cons));
2278 assert(varstopos != NULL);
2279 assert(occurlist != NULL);
2280 assert(noccurlistentries != NULL);
2281 assert(ndelconss != NULL);
2282
2283 consdata = SCIPconsGetData(cons);
2284 assert(consdata != NULL);
2285 assert(consdata->nvars > 1);
2286 assert(consdata->validsignature);
2287 assert(consdata->sorted);
2288
2289 vars = consdata->vars;
2290 assert(vars != NULL);
2291
2292 /* determine shortest column */
2293 findShortestOccurlist(vars, consdata->nvars, varstopos, occurlist, noccurlistentries, occurlistlength, &nentries, &shortestlist);
2294
2295 /* one variable which does not appear in the column representation anymore */
2296 if( nentries == 0 )
2297 return SCIP_OKAY;
2298
2299 assert(shortestlist != NULL);
2300 assert(0 < nentries);
2301
2302 /* check all constraints in the shortest list for coverage */
2303 for( c = nentries - 1; c >= 0; --c )
2304 {
2305 cons1 = shortestlist[c];
2306 assert(cons1 != NULL);
2307 assert(!SCIPconsIsModifiable(cons1));
2308 assert(SCIPconsIsActive(cons1));
2309
2310 if( cons != cons1 )
2311 {
2312 SCIP_CONSDATA* consdata1 = SCIPconsGetData(cons1);
2313 assert(consdata1 != NULL);
2314 assert(consdata1->nvars >= consdata->nvars);
2315
2316 /* constraints with the same length cannot be covered and same constraints are removed in
2317 * detectRedundantConstraints()
2318 */
2319 if( consdata1->nvars == consdata->nvars )
2320 continue;
2321
2322 assert(consdata->validsignature);
2323 assert(consdata->sorted);
2324 assert(consdata1->validsignature);
2325 assert(consdata1->sorted);
2326
2327 if( (consdata->signature & (~consdata1->signature)) == 0 )
2328 {
2329 SCIP_VAR* var1;
2330 int v1;
2331
2332 v = 0;
2333 v1 = 0;
2334
2335 while( v < consdata->nvars && v1 < consdata1->nvars )
2336 {
2337 int comp;
2338
2339 var = vars[v];
2340 var1 = consdata1->vars[v1];
2341
2342 comp = SCIPvarCompare(var, var1);
2343
2344 if( comp == 0 )
2345 {
2346 ++v;
2347 ++v1;
2348 }
2349 else if( comp > 0 )
2350 ++v1;
2351 else
2352 break;
2353 }
2354
2355 /* cons1 is covered by cons */
2356 if( v == consdata->nvars )
2357 {
2358 /* remove cons1 from columns representation */
2359 removeConsFromOccurList(cons1, varstopos, occurlist, noccurlistentries, occurlistlength);
2360
2361 /* delete redundant constraint and update constraint flags if necessary */
2362 SCIP_CALL( removeRedundantCons(scip, cons, cons1, ndelconss) );
2363 }
2364 }
2365 }
2366 }
2367
2368 return SCIP_OKAY;
2369 }
2370
2371 /** compararer for sorting constraints after their number of variables */
2372 static
2373 SCIP_DECL_SORTPTRCOMP(conssLogicorComp)
2374 {
2375 SCIP_CONSDATA* consdata1;
2376 SCIP_CONSDATA* consdata2;
2377
2378 assert(elem1 != NULL);
2379 assert(elem2 != NULL);
2380
2381 consdata1 = SCIPconsGetData((SCIP_CONS*) elem1);
2382 consdata2 = SCIPconsGetData((SCIP_CONS*) elem2);
2383
2384 assert(consdata1 != NULL);
2385 assert(consdata2 != NULL);
2386
2387 return consdata1->nvars - consdata2->nvars;
2388 }
2389
2390 /** add a constraint to the column representation */
2391 static
2392 SCIP_RETCODE addConsToOccurList(
2393 SCIP* scip, /**< SCIP data structure */
2394 SCIP_CONS* cons, /**< logicor constraint */
2395 SCIP_HASHMAP* varstopos, /**< map for mapping variables to positions in the occurlist */
2396 SCIP_CONS*** occurlist, /**< column representation of logicor constraints */
2397 int* noccurlistentries, /**< arrray with number of constraints for each variable in the occurlist */
2398 int* occurlistsizes, /**< array of sizes for each variable in the occurlist */
2399 int* occurlistlength, /**< number of columns in the occurlist */
2400 int occurlistsize /**< size of occurlist */
2401 )
2402 {
2403 SCIP_VAR** vars;
2404 SCIP_VAR* var;
2405 SCIP_CONSDATA* consdata;
2406 int pos;
2407 int v;
2408
2409 assert(scip != NULL);
2410 assert(cons != NULL);
2411 assert(SCIPconsIsActive(cons));
2412 assert(varstopos != NULL);
2413 assert(occurlist != NULL);
2414 assert(noccurlistentries != NULL);
2415 assert(occurlistsizes != NULL);
2416 assert(occurlistlength != NULL);
2417 assert(*occurlistlength <= occurlistsize);
2418
2419 consdata = SCIPconsGetData(cons);
2420 assert(consdata != NULL);
2421 assert(consdata->nvars > 1);
2422
2423 vars = consdata->vars;
2424 assert(vars != NULL);
2425
2426 for( v = consdata->nvars - 1; v >= 0; --v )
2427 {
2428 var = vars[v];
2429 assert(var != NULL);
2430 assert(SCIPvarIsActive(var) || (SCIPvarGetNegatedVar(var) != NULL && SCIPvarIsActive(SCIPvarGetNegatedVar(var))));
2431
2432 /* check if the variable is not yet put into the occurlist */
2433 if( !SCIPhashmapExists(varstopos, (void*) var) )
2434 {
2435 pos = *occurlistlength;
2436 assert(pos <= occurlistsize);
2437
2438 /* occurlist values need to be clear */
2439 assert(occurlist[pos] == NULL);
2440 assert(noccurlistentries[pos] == 0);
2441 assert(occurlistsizes[pos] == 0);
2442
2443 /* allocate memory */
2444 assert(SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL) > 0 || !SCIPconsIsChecked(cons));
2445 occurlistsizes[pos] = SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL) + 1;
2446 SCIP_CALL( SCIPallocBufferArray(scip, &(occurlist[pos]), occurlistsizes[pos]) ); /*lint !e866*/
2447
2448 /* put constraint in list of current variable */
2449 occurlist[pos][noccurlistentries[pos]] = cons;
2450 ++(noccurlistentries[pos]);
2451
2452 /* add new variable to map */
2453 SCIP_CALL( SCIPhashmapInsertInt(varstopos, var, pos + 1) );
2454
2455 ++(*occurlistlength);
2456 }
2457 else
2458 {
2459 pos = SCIPhashmapGetImageInt(varstopos, (void*)var);
2460 assert(0 < pos && pos <= *occurlistlength);
2461
2462 --pos;
2463
2464 assert(occurlist[pos] != NULL);
2465 assert(occurlistsizes[pos] > 0);
2466
2467 /* do we need to resize the array */
2468 if( noccurlistentries[pos] == occurlistsizes[pos] )
2469 {
2470 occurlistsizes[pos] = SCIPcalcMemGrowSize(scip, occurlistsizes[pos] + 1);
2471 assert(occurlistsizes[pos] > noccurlistentries[pos] && occurlistsizes[pos] < INT_MAX);
2472
2473 /* resize occurlist for current variable */
2474 SCIP_CALL( SCIPreallocBufferArray(scip, &(occurlist[pos]), occurlistsizes[pos]) ); /*lint !e866*/
2475 }
2476 assert(noccurlistentries[pos] < occurlistsizes[pos]);
2477
2478 /* put constraint in list of current variable */
2479 occurlist[pos][noccurlistentries[pos]] = cons;
2480 ++(noccurlistentries[pos]);
2481 }
2482 }
2483
2484 return SCIP_OKAY;
2485 }
2486
2487 /** run a pairwise comparison for the given variables against all constraits to detect redundant non-zeros in these
2488 * constraints
2489 */
2490 static
2491 SCIP_RETCODE removeRedundantNonZeros(
2492 SCIP* scip, /**< SCIP data structure */
2493 SCIP_CONS* cons, /**< logicor constraint to check if it covers another */
2494 SCIP_VAR* artvar, /**< artificial negated variable of constraint */
2495 int artpos, /**< position to replace constraint variable with artvar */
2496 SCIP_HASHMAP* varstopos, /**< map for mapping variables to positions in the occurlist */
2497 SCIP_CONS*** occurlist, /**< column representation of logicor constraints */
2498 int* noccurlistentries, /**< number of constraints for each variable in the occurlist */
2499 int occurlistlength, /**< number of columns in the occurlist */
2500 SCIP_EVENTHDLR* eventhdlr, /**< event handler */
2501 int* nchgcoefs, /**< pointer to store the number of deleted non-zeros */
2502 SCIP_Bool* deleted /**< pointer to store if cons will be deleted */
2503 )
2504 {
2505 SCIP_CONS** shortestlist;
2506 SCIP_VAR** vars;
2507 SCIP_CONS* cons1;
2508 SCIP_VAR* oldvar;
2509 SCIP_VAR* var;
2510 SCIP_CONSDATA* consdata;
2511 unsigned int signature;
2512 int nentries;
2513 int nvars;
2514 int c;
2515 int v;
2516 int pos;
2517
2518 assert(scip != NULL);
2519 assert(cons != NULL);
2520 assert(artvar != NULL);
2521 assert(SCIPconsIsActive(cons));
2522 assert(!SCIPconsIsModifiable(cons));
2523 assert(varstopos != NULL);
2524 assert(SCIPhashmapExists(varstopos, (void*) artvar));
2525 assert(occurlist != NULL);
2526 assert(noccurlistentries != NULL);
2527 assert(nchgcoefs != NULL);
2528 assert(deleted != NULL);
2529
2530 consdata = SCIPconsGetData(cons);
2531 assert(consdata != NULL);
2532 assert(consdata->sorted);
2533
2534 nvars = consdata->nvars;
2535 assert(nvars > 1);
2536 assert(0 <= artpos && artpos < nvars);
2537
2538 vars = consdata->vars;
2539 assert(vars != NULL);
2540
2541 *deleted = FALSE;
2542
2543 /* temporary exchange the variable for finding the shortest list */
2544 oldvar = vars[artpos];
2545 assert(oldvar == SCIPvarGetNegatedVar(artvar));
2546 vars[artpos] = artvar;
2547
2548 /* determine shortest column */
2549 findShortestOccurlist(vars, nvars, varstopos, occurlist, noccurlistentries, occurlistlength, &nentries, &shortestlist);
2550
2551 /* correct exchanged variable with constraint variables */
2552 vars[artpos] = oldvar;
2553
2554 /* one variable which does not appear in the column representation anymore */
2555 if( nentries == 0 )
2556 return SCIP_OKAY;
2557
2558 assert(shortestlist != NULL);
2559 assert(0 < nentries);
2560
2561 /* temporary exchange the variable for calculating a valid signature */
2562 oldvar = vars[artpos];
2563 vars[artpos] = artvar;
2564 signature = calcSignature(vars, nvars);
2565
2566 /* correct exchanged variable with constraint variables */
2567 vars[artpos] = oldvar;
2568
2569 /* check all constraints in the shortest list for coverage */
2570 for( c = nentries - 1; c >= 0; --c )
2571 {
2572 cons1 = shortestlist[c];
2573 assert(cons1 != NULL);
2574 assert(!SCIPconsIsModifiable(cons1));
2575
2576 if( !SCIPconsIsActive(cons1) )
2577 continue;
2578
2579 if( cons != cons1 )
2580 {
2581 SCIP_CONSDATA* consdata1 = SCIPconsGetData(cons1);
2582 assert(consdata1 != NULL);
2583
2584 /* constraints with the less variables cannot be covered */
2585 if( consdata1->nvars < nvars )
2586 continue;
2587
2588 pos = -1;
2589
2590 assert(consdata->sorted);
2591 assert(consdata->merged);
2592 assert(consdata1->validsignature);
2593 assert(consdata1->sorted);
2594 assert(consdata1->merged);
2595
2596 if( (signature & (~consdata1->signature)) == 0 )
2597 {
2598 SCIP_VAR* var1;
2599 int v1;
2600
2601 v = 0;
2602 v1 = 0;
2603
2604 while( v < nvars && v1 < consdata1->nvars )
2605 {
2606 int comp;
2607
2608 /* skip position of artificial variable */
2609 if( artpos == v )
2610 {
2611 ++v;
2612 continue;
2613 }
2614
2615 var1 = consdata1->vars[v1];
2616
2617 /* did we find the artificial variable in cons1 */
2618 if( artvar == var1 )
2619 {
2620 /* remember of possible redundant variable */
2621 assert(pos == -1);
2622 pos = v1;
2623
2624 ++v1;
2625 continue;
2626 }
2627
2628 var = vars[v];
2629 comp = SCIPvarCompare(var, var1);
2630
2631 /* check if the cons1 can still be covered */
2632 if( comp == 0 )
2633 {
2634 ++v;
2635 ++v1;
2636 }
2637 else if( comp > 0 )
2638 ++v1;
2639 else
2640 break;
2641 }
2642
2643 /* cons1 is might be covered by the changed constraints cons, meaning that we might remove the artvar from
2644 * cons1
2645 */
2646 if( v == nvars )
2647 {
2648 int l;
2649
2650 /* if the artificial variable was not yet found, search over the rear variables in constraint cons1 */
2651 if( pos == -1 )
2652 {
2653 while( v1 < consdata1->nvars )
2654 {
2655 if( artvar == consdata1->vars[v1] )
2656 {
2657 /* remember of possible redundant variable */
2658 pos = v1;
2659 break;
2660 }
2661 ++v1;
2662 }
2663 }
2664
2665 if( pos >= 0 )
2666 {
2667 int conspos;
2668
2669 assert(pos < consdata1->nvars);
2670 assert(artvar == consdata1->vars[pos]);
2671
2672 /* remove redudant entry in cons1 */
2673 SCIPdebugMsg(scip, "variable %s in logicor constraint <%s> is redundant and will be removed (used constraint %s)\n",
2674 SCIPvarGetName(artvar), SCIPconsGetName(cons1), SCIPconsGetName(cons));
2675 SCIPdebugPrintCons(scip, cons1, NULL);
2676 conspos = pos;
2677
2678 if( consdata1->nvars > nvars )
2679 {
2680 pos = SCIPhashmapGetImageInt(varstopos, (void*)artvar);
2681 assert(0 < pos && pos <= occurlistlength);
2682
2683 --pos;
2684
2685 /* remove corresponding entry in column representation */
2686 for( l = noccurlistentries[pos] - 1; l >= 0; --l )
2687 {
2688 if( occurlist[pos][l] == cons1 )
2689 {
2690 --noccurlistentries[pos];
2691 assert(noccurlistentries[pos] >= 0);
2692
2693 occurlist[pos][l] = occurlist[pos][noccurlistentries[pos]];
2694 break;
2695 }
2696 }
2697 assert(l >= 0);
2698 }
2699 else
2700 {
2701 assert(consdata1->nvars == nvars);
2702
2703 /* delete cons */
2704 SCIPdebugMsg(scip, "logicor constraint <%s> is redundant due to constraint <%s> after removing variable <%s>\n",
2705 SCIPconsGetName(cons), SCIPconsGetName(cons1), SCIPvarGetName(artvar));
2706
2707 /* remove cons from columns representation */
2708 removeConsFromOccurList(cons, varstopos, occurlist, noccurlistentries, occurlistlength);
2709
2710 /* update flags of cons1 */
2711 SCIP_CALL( SCIPupdateConsFlags(scip, cons1, cons) );
2712
2713 SCIP_CALL( SCIPdelCons(scip, cons) );
2714 *deleted = TRUE;
2715 }
2716
2717 /* remove variable */
2718 SCIP_CALL( delCoefPos(scip, cons1, eventhdlr, conspos) );
2719 ++(*nchgcoefs);
2720 consdataSort(consdata1);
2721 consdataCalcSignature(consdata1);
2722
2723 if( *deleted )
2724 return SCIP_OKAY;
2725 }
2726 }
2727 }
2728 }
2729 }
2730
2731 return SCIP_OKAY;
2732 }
2733
2734 /** find and remove redundant non-zero entries */
2735 static
2736 SCIP_RETCODE strengthenConss(
2737 SCIP* scip, /**< SCIP data structure */
2738 SCIP_CONS** conss, /**< sorted array of logicor constraint */
2739 int nconss, /**< number of sorted constraints */
2740 SCIP_HASHMAP* varstopos, /**< map for mapping variables to positions in the occurlist */
2741 SCIP_CONS*** occurlist, /**< column representation of logicor constraints */
2742 int* noccurlistentries, /**< number of constraints for each variable in the occurlist */
2743 int occurlistlength, /**< number of columns in the occurlist */
2744 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */
2745 int* ndelconss, /**< pointer to store the number of deleted constraints */
2746 int* nchgcoefs /**< pointer to store the number of remove coefficients */
2747 )
2748 {
2749 SCIP_VAR** vars;
2750 SCIP_CONSDATA* consdata;
2751 SCIP_CONS* cons;
2752 SCIP_VAR* artvar;
2753 int nvars;
2754 int c;
2755 int v;
2756
2757 assert(scip != NULL);
2758 assert(conss != NULL || nconss == 0);
2759 assert(varstopos != NULL);
2760 assert(occurlist != NULL);
2761 assert(noccurlistentries != NULL);
2762 assert(eventhdlr != NULL);
2763 assert(ndelconss != NULL);
2764 assert(nchgcoefs != NULL);
2765
2766 if( nconss == 0 )
2767 return SCIP_OKAY;
2768
2769 assert(conss != NULL);
2770
2771 for( c = 0; c < nconss; ++c )
2772 {
2773 cons = conss[c];
2774 assert(cons != NULL);
2775 assert(!SCIPconsIsModifiable(cons));
2776
2777 if( !SCIPconsIsActive(cons) )
2778 continue;
2779
2780 consdata = SCIPconsGetData(cons);
2781 assert(consdata != NULL);
2782
2783 nvars = consdata->nvars;
2784 assert(nvars >= 1);
2785
2786 if( nvars == 1 )
2787 continue;
2788
2789 vars = consdata->vars;
2790 assert(vars != NULL);
2791
2792 for( v = nvars - 1; v >= 0; --v )
2793 {
2794 artvar = SCIPvarGetNegatedVar(vars[v]);
2795
2796 if( artvar != NULL && SCIPhashmapExists(varstopos, (void*) artvar) )
2797 {
2798 SCIP_Bool deleted;
2799
2800 /* detect and remove redundant non-zero entries */
2801 /* @todo: improve this algorithm by using the information that a constraint variables does not appaer in any
2802 * other constraint, which means that only this variable needs to be negated to check for redundant
2803 * non-zeros, therefor change also findShortestOccurlist() to return the corresponding
2804 * variable/position
2805 */
2806 SCIP_CALL( removeRedundantNonZeros(scip, cons, artvar, v, varstopos, occurlist, noccurlistentries,
2807 occurlistlength, eventhdlr, nchgcoefs, &deleted) );
2808
2809 if( deleted )
2810 {
2811 assert(SCIPconsIsDeleted(cons));
2812 ++(*ndelconss);
2813 break;
2814 }
2815 else
2816 assert(SCIPconsIsActive(cons));
2817 }
2818 }
2819 }
2820
2821 return SCIP_OKAY;
2822 }
2823
2824
2825 /** prepares a constraint by removing fixings and merge it */
2826 static
2827 SCIP_RETCODE prepareCons(
2828 SCIP* scip, /**< SCIP data structure */
2829 SCIP_CONS* cons, /**< logic or constraint */
2830 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */
2831 unsigned char** entries, /**< array to store whether two positions in constraints represent the same variable */
2832 int* nentries, /**< pointer for array size, if array will be to small it's corrected */
2833 SCIP_Bool* redundant, /**< returns whether a variable fixed to one exists in the constraint */
2834 int* nfixedvars, /**< pointer to count number of fixings */
2835 int* nchgcoefs, /**< pointer to count number of changed/deleted coefficients */
2836 int* ndelconss, /**< pointer to count number of deleted constraints */
2837 SCIP_Bool* cutoff /**< pointer to store, if cut off appeared */
2838 )
2839 {
2840 SCIP_CONSDATA* consdata;
2841
2842 assert(scip != NULL);
2843 assert(cons != NULL);
2844 assert(!SCIPconsIsDeleted(cons));
2845 assert(eventhdlr != NULL);
2846 assert(*entries != NULL);
2847 assert(nentries != NULL);
2848 assert(redundant != NULL);
2849 assert(nfixedvars != NULL);
2850 assert(nchgcoefs != NULL);
2851 assert(ndelconss != NULL);
2852 assert(redundant != NULL);
2853
2854 consdata = SCIPconsGetData(cons);
2855 assert(consdata != NULL);
2856 assert(consdata->nvars > 0);
2857
2858 *redundant = FALSE;
2859
2860 /* remove old fixings */
2861 if( !consdata->presolved )
2862 {
2863 /* remove all variables that are fixed to zero, check redundancy due to fixed-to-one variable */
2864 SCIP_CALL( applyFixings(scip, cons, eventhdlr, redundant, nchgcoefs, NULL, NULL) );
2865 }
2866
2867 if( !*redundant )
2868 {
2869 /* merge constraint */
2870 SCIP_CALL( mergeMultiples(scip, cons, eventhdlr, entries, nentries, redundant, nchgcoefs) );
2871 }
2872
2873 if( *redundant )
2874 {
2875 SCIP_CALL( SCIPdelCons(scip, cons) );
2876 ++(*ndelconss);
2877
2878 return SCIP_OKAY;
2879 }
2880
2881 if( consdata->nvars == 0 )
2882 {
2883 *cutoff = TRUE;
2884 }
2885 else if( consdata->nvars == 1 )
2886 {
2887 SCIP_Bool infeasible;
2888 SCIP_Bool fixed;
2889
2890 SCIPdebugMsg(scip, " -> fix last remaining variable and delete constraint\n");
2891
2892 SCIP_CALL( SCIPfixVar(scip, consdata->vars[0], 1.0, &infeasible, &fixed) );
2893 assert(!infeasible);
2894 assert(fixed);
2895 ++(*nfixedvars);
2896
2897 SCIP_CALL( SCIPdelCons(scip, cons) );
2898 ++(*ndelconss);
2899
2900 *redundant = TRUE;
2901 }
2902 consdata->presolved = TRUE;
2903
2904 return SCIP_OKAY;
2905 }
2906
2907
2908 /** find covered/subsumed constraints and redundant non-zero entries
2909 *
2910 * covered:
2911 * e.g.: c1: x1 + x2 + x3 >= 1
2912 * c2: x1 + x2 + x3 + x4 >= 1
2913 *
2914 * strengthen:
2915 * e.g.: c1: x1 + x2 + x3 >= 1
2916 * c2: x1 + x2 + ~x3 + x4 >= 1
2917 *
2918 * => c2: x1 + x2 + x4 >= 1
2919 *
2920 * @see "Effective Preprocessing in SAT through Variable and Clause Elimination" by Niklas En and Armin Biere
2921 */
2922 static
2923 SCIP_RETCODE removeRedundantConssAndNonzeros(
2924 SCIP* scip, /**< SCIP data structure */
2925 SCIP_CONS** conss, /**< array of logicor constraints */
2926 int nconss, /**< number of logicor constraints */
2927 unsigned char** entries, /**< array to store whether two positions in constraints represent the same
2928 * variable */
2929 int* nentries, /**< pointer for array size, if array will be to small it's corrected */
2930 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */
2931 SCIP_Bool usestrengthening, /**< should we try to strengthen constraints by removing superflous
2932 * non-zeros? */
2933 int* firstchange, /**< pointer to store first changed constraint */
2934 int* nfixedvars, /**< pointer to count number of fixings */
2935 int* ndelconss, /**< pointer to store the number of deleted constraints */
2936 int* nchgcoefs, /**< pointer to store the number of deleted coefficients */
2937 SCIP_Bool* cutoff /**< pointer to store, if cut off appeared */
2938 )
2939 {
2940 SCIP_CONS*** occurlist;
2941 SCIP_CONS** myconss;
2942 SCIP_HASHMAP* varstopos;
2943 SCIP_CONS* cons;
2944 SCIP_CONSDATA* consdata;
2945 int* noccurlistentries;
2946 int* occurlistsizes;
2947 SCIP_Bool redundant;
2948 SCIP_Bool conschanged;
2949 int lastnfixedvars;
2950 int nbinvars;
2951 int occurlistlength;
2952 int occurlistsize;
2953 int nmyconss;
2954 int nmaxvars;
2955 int c;
2956
2957 assert(scip != NULL);
2958 assert(conss != NULL || nconss == 0);
2959 assert(entries != NULL);
2960 assert(*entries != NULL);
2961 assert(nentries != NULL);
2962 assert(eventhdlr != NULL);
2963 assert(firstchange != NULL);
2964 assert(0 <= *firstchange);
2965 assert(nfixedvars != NULL);
2966 assert(ndelconss != NULL);
2967 assert(nchgcoefs != NULL);
2968
2969 if( *firstchange > nconss || nconss < 2 )
2970 return SCIP_OKAY;
2971
2972 SCIPdebugMsg(scip, "starting removeRedundantConssAndNonzeros(), pairwise comparison to detect covered logicor constraints\n");
2973
2974 /* copy constraints to re-order them */
2975 SCIP_CALL( SCIPduplicateBufferArray(scip, &myconss, conss, nconss) );
2976
2977 nmyconss = nconss;
2978 lastnfixedvars = -1;
2979 while( *nfixedvars != lastnfixedvars )
2980 {
2981 lastnfixedvars = *nfixedvars;
2982 for( c = nconss - 1; c >= 0; --c )
2983 {
2984 cons = myconss[c];
2985 assert(cons != NULL);
2986
2987 if( SCIPconsIsDeleted(cons) || SCIPconsIsModifiable(cons) )
2988 {
2989 myconss[c] = myconss[nmyconss - 1];
2990 --nmyconss;
2991
2992 continue;
2993 }
2994
2995 /* prepare constraint by removing fixings and merge it */
2996 SCIP_CALL( prepareCons(scip, cons, eventhdlr, entries, nentries, &redundant, nfixedvars, nchgcoefs, ndelconss, cutoff) );
2997
2998 if( redundant )
2999 {
3000 assert(SCIPconsIsDeleted(cons));
3001 assert(!(*cutoff));
3002
3003 myconss[c] = myconss[nmyconss - 1];
3004 --nmyconss;
3005
3006 continue;
3007 }
3008
3009 if( *cutoff )
3010 {
3011 SCIPfreeBufferArray(scip, &myconss);
3012
3013 return SCIP_OKAY;
3014 }
3015
3016 consdata = SCIPconsGetData(cons);
3017
3018 /* sort the constraint */
3019 consdataSort(consdata);
3020
3021 assert(consdata->nvars >= 2);
3022 }
3023 }
3024
3025 SCIPsortPtr((void**)myconss, conssLogicorComp, nmyconss);
3026 assert(myconss[0] != NULL && myconss[nmyconss - 1] != NULL);
3027 assert(SCIPconsGetData(myconss[0]) != NULL && SCIPconsGetData(myconss[nmyconss - 1]) != NULL);
3028 assert(SCIPconsGetData(myconss[0])->nvars <= SCIPconsGetData(myconss[nmyconss - 1])->nvars);
3029
3030 /* we can stop if strengthening is disabled and all constraints have the same amount of variables */
3031 if( !usestrengthening && SCIPconsGetData(myconss[0])->nvars == SCIPconsGetData(myconss[nmyconss - 1])->nvars )
3032 {
3033 SCIPfreeBufferArray(scip, &myconss);
3034
3035 return SCIP_OKAY;
3036 }
3037
3038 /* @note: in the following we have at least number of nonzeros in logicor constraints + three times two the number of
3039 * binary variables memory consumption + a map for variables to positions, we need this to get a column base
3040 * representation
3041 */
3042
3043 /* get number of all possible(incl. implcit) binary variables and their negation */
3044 nbinvars = SCIPgetNVars(scip) - SCIPgetNContVars(scip);
3045 occurlistsize = 2 * nbinvars;
3046
3047 /* allocate memory for the column representation for each variable */
3048 SCIP_CALL( SCIPallocBufferArray(scip, &occurlist, occurlistsize) );
3049 BMSclearMemoryArray(occurlist, occurlistsize);
3050 SCIP_CALL( SCIPallocBufferArray(scip, &noccurlistentries, occurlistsize) );
3051 BMSclearMemoryArray(noccurlistentries, occurlistsize);
3052 SCIP_CALL( SCIPallocBufferArray(scip, &occurlistsizes, occurlistsize) );
3053 BMSclearMemoryArray(occurlistsizes, occurlistsize);
3054
3055 /* create hashmap to map all occuring variables to a position in the list */
3056 SCIP_CALL( SCIPhashmapCreate(&varstopos, SCIPblkmem(scip), nmyconss) );
3057
3058 /* get maximal number of variables over all logicor constraints */
3059 c = nmyconss - 1;
3060 cons = myconss[c];
3061 assert(cons != NULL);
3062 assert(SCIPconsIsActive(cons));
3063 consdata = SCIPconsGetData(cons);
3064 assert(consdata != NULL);
3065 nmaxvars = consdata->nvars;
3066
3067 occurlistlength = 0;
3068 conschanged = FALSE;
3069
3070 /* determine all constraints with the maximal number of variables and add them to the column representation */
3071 do
3072 {
3073 /* calculate hash-signature */
3074 consdataCalcSignature(consdata);
3075 assert(consdata->validsignature);
3076 conschanged = conschanged || consdata->changed;
3077 consdata->changed = FALSE;
3078
3079 /* add constraint to column data structure */
3080 SCIP_CALL( addConsToOccurList(scip, cons, varstopos, occurlist, noccurlistentries, occurlistsizes, &occurlistlength, occurlistsize) );
3081
3082 --c;
3083 if( c < 0 )
3084 break;
3085
3086 cons = myconss[c];
3087 assert(cons != NULL);
3088 assert(SCIPconsIsActive(cons));
3089 consdata = SCIPconsGetData(cons);
3090 assert(consdata != NULL);
3091 }
3092 while( consdata->nvars == nmaxvars );
3093
3094 /* remove covered constraints and left over constraints to the column representation */
3095 while( c >= 0 )
3096 {
3097 cons = myconss[c];
3098 assert(cons != NULL);
3099 assert(SCIPconsIsActive(cons));
3100 consdata = SCIPconsGetData(cons);
3101 assert(consdata != NULL);
3102
3103 /* calculate hash-signature */
3104 consdataCalcSignature(consdata);
3105 assert(consdata->validsignature);
3106
3107 /* search for covered constraints */
3108 if( conschanged || consdata->changed )
3109 {
3110 /* detect covered constraints
3111 *
3112 * e.g.: c1: x1 + x2 + x3 >= 1
3113 * c2: x1 + x2 + x3 + x4 >= 1
3114 *
3115 * => delete c2
3116 */
3117 SCIP_CALL( removeRedundantConss(scip, cons, varstopos, occurlist, noccurlistentries, occurlistlength, ndelconss) );
3118 assert(SCIPconsIsActive(cons));
3119
3120 consdata->changed = FALSE;
3121 conschanged = TRUE;
3122 }
3123
3124 /* add constraint to column data structure */
3125 SCIP_CALL( addConsToOccurList(scip, cons, varstopos, occurlist, noccurlistentries, occurlistsizes, &occurlistlength, occurlistsize) );
3126
3127 --c;
3128 }
3129
3130 /* strengthen constraint while removing non-zeros
3131 *
3132 * e.g.: c1: x1 + x2 + x3 >= 1
3133 * c2: x1 + x2 + ~x3 + x4 >= 1
3134 *
3135 * => c2: x1 + x2 + x4 >= 1
3136 *
3137 * special case:
3138 *
3139 * e.g.: c1: x1 + x2 + x3 >= 1
3140 * c2: x1 + x2 + ~x3 >= 1
3141 *
3142 * => delete c1; c2: x1 + x2 >= 1
3143 *
3144 */
3145 SCIP_CALL( strengthenConss(scip, myconss, nmyconss, varstopos, occurlist, noccurlistentries, occurlistlength, eventhdlr, ndelconss, nchgcoefs) );
3146
3147 /* delete temporary memory in occurlist */
3148 for( --occurlistsize ; occurlistsize >= 0; --occurlistsize )
3149 {
3150 assert((occurlistsizes[occurlistsize] == 0) == (occurlist[occurlistsize] == NULL));
3151 SCIPfreeBufferArrayNull(scip, &(occurlist[occurlistsize]));
3152 }
3153
3154 /* delete temporary memory */
3155 SCIPhashmapFree(&varstopos);
3156 SCIPfreeBufferArray(scip, &occurlistsizes);
3157 SCIPfreeBufferArray(scip, &noccurlistentries);
3158 SCIPfreeBufferArray(scip, &occurlist);
3159 SCIPfreeBufferArray(scip, &myconss);
3160
3161 return SCIP_OKAY;
3162 }
3163
3164 #define MAX_CONSLENGTH 200
3165
3166 /** try to tighten constraints by reducing the number of variables in the constraints using implications and cliques,
3167 * also derive fixations through them, @see SCIPshrinkDisjunctiveVarSet()
3168 */
3169 static
3170 SCIP_RETCODE shortenConss(
3171 SCIP* scip, /**< SCIP data structure */
3172 SCIP_CONSHDLRDATA* conshdlrdata, /**< logic or constraint handler data */
3173 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */
3174 SCIP_CONS** conss, /**< all constraints */
3175 int nconss, /**< number of constraints */
3176 unsigned char** entries, /**< array to store whether two positions in constraints represent the same
3177 * variable */
3178 int* nentries, /**< pointer for array size, if array will be to small it's corrected */
3179 int* nfixedvars, /**< pointer to count number of fixings */
3180 int* ndelconss, /**< pointer to count number of deleted constraints */
3181 int* nchgcoefs, /**< pointer to count number of changed/deleted coefficients */
3182 SCIP_Bool* cutoff /**< pointer to store, if cut off appeared */
3183 )
3184 {
3185 SCIP_VAR** probvars;
3186 SCIP_VAR* var;
3187 SCIP_Real* bounds;
3188 SCIP_Bool* boundtypes;
3189 SCIP_Bool* redundants;
3190 int nbinprobvars;
3191 int nredvars;
3192 int c;
3193 int v;
3194
3195 assert(scip != NULL);
3196 assert(eventhdlr != NULL);
3197 assert(conss != NULL || nconss == 0);
3198 assert(entries != NULL);
3199 assert(*entries != NULL);
3200 assert(nentries != NULL);
3201 assert(nfixedvars != NULL);
3202 assert(ndelconss != NULL);
3203 assert(nchgcoefs != NULL);
3204
3205 if( nconss == 0 )
3206 return SCIP_OKAY;
3207
3208 assert(conss != NULL);
3209
3210 if( SCIPgetNCliques(scip) == conshdlrdata->nlastcliquesshorten
3211 && SCIPgetNImplications(scip) == conshdlrdata->nlastimplsshorten )
3212 return SCIP_OKAY;
3213
3214 conshdlrdata->nlastcliquesshorten = SCIPgetNCliques(scip);
3215 conshdlrdata->nlastimplsshorten = SCIPgetNImplications(scip);
3216
3217 nbinprobvars = SCIPgetNVars(scip) - SCIPgetNContVars(scip);
3218
3219 /* allocate temporary memory */
3220 SCIP_CALL( SCIPallocBufferArray(scip, &probvars, nbinprobvars) );
3221 SCIP_CALL( SCIPallocBufferArray(scip, &bounds, nbinprobvars) );
3222 SCIP_CALL( SCIPallocBufferArray(scip, &boundtypes, nbinprobvars) );
3223 SCIP_CALL( SCIPallocCleanBufferArray(scip, &redundants, nbinprobvars) );
3224
3225 for( c = nconss - 1; c >= 0; --c )
3226 {
3227 SCIP_Bool redundant = FALSE;
3228 SCIP_Bool glbinfeas = FALSE;
3229 SCIP_CONS* cons = conss[c];
3230 SCIP_CONSDATA* consdata;
3231
3232 assert(cons != NULL);
3233
3234 if( SCIPconsIsDeleted(cons) )
3235 continue;
3236
3237 consdata = SCIPconsGetData(cons);
3238 assert(consdata != NULL);
3239
3240 /* prepare constraint by removing fixings and merge it; we invalidate the presolved flag, because the calls to
3241 * SCIPcleanupCliques() in this loop may have lead to fixed variables that are not yet removed */
3242 consdata->presolved = FALSE;
3243 SCIP_CALL( prepareCons(scip, cons, eventhdlr, entries, nentries, &redundant, nfixedvars, nchgcoefs, ndelconss, cutoff) );
3244
3245 if( redundant )
3246 {
3247 assert(SCIPconsIsDeleted(cons));
3248 continue;
3249 }
3250
3251 if( *cutoff )
3252 goto TERMINATE;
3253
3254 assert(consdata->nvars >= 2);
3255
3256 /* do not try to shorten too long constraints */
3257 if( consdata->nvars > MAX_CONSLENGTH )
3258 continue;
3259
3260 /* form necessary data */
3261 for( v = consdata->nvars - 1; v >= 0; --v)
3262 {
3263 var = consdata->vars[v];
3264 assert(var != NULL);
3265 assert(SCIPvarIsActive(var) || (SCIPvarGetStatus(var) == SCIP_VARSTATUS_NEGATED && SCIPvarIsActive(SCIPvarGetNegationVar(var))));
3266
3267 if( SCIPvarIsActive(var) )
3268 {
3269 probvars[v] = var;
3270 bounds[v] = 1.0;
3271 boundtypes[v] = FALSE;
3272 }
3273 else
3274 {
3275 probvars[v] = SCIPvarGetNegationVar(var);
3276 bounds[v] = 0.0;
3277 boundtypes[v] = TRUE;
3278 }
3279 }
3280
3281 SCIP_CALL( SCIPcleanupCliques(scip, cutoff) );
3282
3283 if( *cutoff )
3284 goto TERMINATE;
3285
3286 /* use implications and cliques to derive global fixings and to shrink the number of variables in this constraints */
3287 SCIP_CALL( SCIPshrinkDisjunctiveVarSet(scip, probvars, bounds, boundtypes, redundants, consdata->nvars, &nredvars,
3288 nfixedvars, &redundant, &glbinfeas, TRUE) );
3289
3290 if( glbinfeas )
3291 {
3292 /* reset redundants array to FALSE */
3293 BMSclearMemoryArray(redundants, nbinprobvars);
3294 goto TERMINATE;
3295 }
3296
3297 /* remove redundant constraint */
3298 if( redundant )
3299 {
3300 SCIP_CALL( SCIPdelCons(scip, cons) );
3301 ++(*ndelconss);
3302
3303 /* reset redundants array to FALSE */
3304 BMSclearMemoryArray(redundants, consdata->nvars);
3305 continue;
3306 }
3307
3308 /* remove redundant variables */
3309 if( nredvars > 0 )
3310 {
3311 for( v = consdata->nvars - 1; v >= 0; --v )
3312 {
3313 if( redundants[v] )
3314 {
3315 SCIP_CALL( delCoefPos(scip, cons, eventhdlr, v) );
3316
3317 /* reset entry to FALSE */
3318 redundants[v] = FALSE;
3319 }
3320 }
3321 *nchgcoefs += nredvars;
3322
3323 /* if only one variable is left over fix it */
3324 if( consdata->nvars == 1 )
3325 {
3326 SCIP_Bool infeasible;
3327 SCIP_Bool fixed;
3328
3329 SCIPdebugMsg(scip, " -> fix last remaining variable and delete constraint\n");
3330
3331 SCIP_CALL( SCIPfixVar(scip, consdata->vars[0], 1.0, &infeasible, &fixed) );
3332 assert(!infeasible);
3333 assert(fixed);
3334 ++(*nfixedvars);
3335
3336 SCIP_CALL( SCIPdelCons(scip, cons) );
3337 ++(*ndelconss);
3338 }
3339 /* @todo might also upgrade a two variable constraint to a set-packing constraint */
3340 }
3341 }
3342
3343 /* invalidate the presolved flags, because the calls to SCIPcleanupCliques() may have lead to fixed variables that
3344 * are not yet removed */
3345 for( c = nconss - 1; c >= 0; --c )
3346 {
3347 SCIP_CONS* cons = conss[c];
3348 SCIP_CONSDATA* consdata;
3349
3350 assert(cons != NULL);
3351
3352 consdata = SCIPconsGetData(cons);
3353 assert(consdata != NULL);
3354
3355 consdata->presolved = FALSE;
3356 }
3357
3358 TERMINATE:
3359 /* free temporary memory */
3360 SCIPfreeCleanBufferArray(scip, &redundants);
3361 SCIPfreeBufferArray(scip, &boundtypes);
3362 SCIPfreeBufferArray(scip, &bounds);
3363 SCIPfreeBufferArray(scip, &probvars);
3364
3365 return SCIP_OKAY;
3366 }
3367
3368 #define MAXCOMPARISONS 1000000
3369
3370 /** try to find a negated clique in a constraint which makes this constraint redundant but we need to keep the negated
3371 * clique information alive, so we create a corresponding set-packing constraint
3372 */
3373 static
3374 SCIP_RETCODE removeConstraintsDueToNegCliques(
3375 SCIP* scip, /**< SCIP data structure */
3376 SCIP_CONSHDLR* conshdlr, /**< logicor constraint handler */
3377 SCIP_CONSHDLR* conshdlrsetppc, /**< setppc constraint handler, or NULL */
3378 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */
3379 SCIP_CONS** conss, /**< all constraints */
3380 int nconss, /**< number of constraints */
3381 unsigned char** entries, /**< array to store whether two positions in constraints represent the same
3382 * variable */
3383 int* nentries, /**< pointer for array size, if array will be to small it's corrected */
3384 int* nfixedvars, /**< pointer to count number of fixings */
3385 int* ndelconss, /**< pointer to count number of deleted constraints */
3386 int* nupgdconss, /**< pointer to count number of upgraded constraints */
3387 int* nchgcoefs, /**< pointer to count number of changed/deleted coefficients */
3388 SCIP_Bool* cutoff /**< pointer to store, if cut off appeared */
3389 )
3390 {
3391 SCIP_CONSHDLRDATA* conshdlrdata;
3392 SCIP_CONS* cons;
3393 SCIP_CONSDATA* consdata;
3394 SCIP_VAR** repvars;
3395 SCIP_Bool* negated;
3396 SCIP_VAR* var1;
3397 SCIP_Bool redundant;
3398 int c;
3399 int size;
3400 int maxcomppercons;
3401 int comppercons;
3402
3403 assert(scip != NULL);
3404 assert(conshdlr != NULL);
3405 assert(eventhdlr != NULL);
3406 assert(conss != NULL || nconss == 0);
3407 assert(entries != NULL);
3408 assert(*entries != NULL);
3409 assert(nentries != NULL);
3410 assert(nfixedvars != NULL);
3411 assert(ndelconss != NULL);
3412 assert(nupgdconss != NULL);
3413 assert(nchgcoefs != NULL);
3414 assert(cutoff != NULL);
3415
3416 conshdlrdata = SCIPconshdlrGetData(conshdlr);
3417 assert(conshdlrdata != NULL);
3418
3419 if( nconss == 0 )
3420 return SCIP_OKAY;
3421
3422 if( SCIPgetNCliques(scip) == conshdlrdata->nlastcliquesneg && SCIPgetNImplications(scip) == conshdlrdata->nlastimplsneg )
3423 return SCIP_OKAY;
3424
3425 conshdlrdata->nlastcliquesneg = SCIPgetNCliques(scip);
3426 conshdlrdata->nlastimplsneg = SCIPgetNImplications(scip);
3427
3428 /* estimate the maximal number of variables in a logicor constraint */
3429 size = SCIPgetNVars(scip) - SCIPgetNContVars(scip);
3430 if( size <= 0 )
3431 return SCIP_OKAY;
3432
3433 /* temporary memory for active/negation of active variables */
3434 SCIP_CALL( SCIPallocBufferArray(scip, &repvars, size) );
3435 SCIP_CALL( SCIPallocBufferArray(scip, &negated, size) );
3436
3437 /* iterate over all constraints and try to find negated cliques in logicors */
3438 for( c = nconss - 1; c >= 0; --c )
3439 {
3440 int v;
3441
3442 assert(conss != NULL); /* for flexelint */
3443
3444 cons = conss[c];
3445 assert(cons != NULL);
3446
3447 if( !SCIPconsIsActive(cons) )
3448 continue;
3449
3450 /* prepare constraint by removing fixings and merge it */
3451 SCIP_CALL( prepareCons(scip, cons, eventhdlr, entries, nentries, &redundant, nfixedvars, nchgcoefs, ndelconss, cutoff) );
3452
3453 if( redundant )
3454 {
3455 assert(SCIPconsIsDeleted(cons));
3456 continue;
3457 }
3458
3459 if( *cutoff )
3460 goto TERMINATE;
3461
3462 consdata = SCIPconsGetData(cons);
3463 assert(consdata != NULL);
3464 assert(consdata->nvars >= 2);
3465 assert(consdata->nvars <= size);
3466 assert(consdata->presolved);
3467
3468 if( SCIPconsIsModifiable(cons) && consdata->nvars == 2 )
3469 continue;
3470
3471 if( c % 100 == 0 && SCIPisStopped(scip) )
3472 break;
3473
3474 maxcomppercons = MAXCOMPARISONS / nconss;
3475 comppercons = 0;
3476
3477 BMScopyMemoryArray(repvars, consdata->vars, consdata->nvars);
3478
3479 /* all variables should be active or negative active variables, otherwise something went wrong with applyFixings()
3480 * called before mergeMultiples()
3481 */
3482 for( v = consdata->nvars - 1; v >= 0; --v )
3483 {
3484 assert(SCIPvarIsActive(repvars[v]) || (SCIPvarGetStatus(repvars[v]) == SCIP_VARSTATUS_NEGATED && SCIPvarIsActive(SCIPvarGetNegationVar(repvars[v]))));
3485 negated[v] = SCIPvarIsNegated(repvars[v]);
3486 }
3487
3488 for( v = consdata->nvars - 1; v > 0; --v )
3489 {
3490 SCIP_Bool breakloop;
3491 SCIP_Bool neg1;
3492 int w;
3493
3494 var1 = repvars[v];
3495
3496 /* if there is no negated variable, there can't be a negated clique */
3497 if( SCIPvarGetNegatedVar(var1) == NULL )
3498 continue;
3499
3500 /* get active counterpart to check for common cliques */
3501 if( SCIPvarGetStatus(var1) == SCIP_VARSTATUS_NEGATED )
3502 {
3503 var1 = SCIPvarGetNegatedVar(var1);
3504 neg1 = TRUE;
3505 }
3506 else
3507 neg1 = FALSE;
3508
3509 if( !SCIPvarIsActive(var1) )
3510 continue;
3511
3512 /* no cliques available */
3513 if( SCIPvarGetNCliques(var1, neg1) == 0 && SCIPvarGetNImpls(var1, neg1) == 0 )
3514 continue;
3515
3516 comppercons += (v - 1);
3517
3518 breakloop = FALSE;
3519
3520 for( w = v - 1; w >= 0; --w )
3521 {
3522 SCIP_VAR* var2;
3523 SCIP_Bool neg2;
3524
3525 var2 = repvars[w];
3526
3527 /* if there is no negated variable, there can't be a negated clique */
3528 if( SCIPvarGetNegatedVar(var2) == NULL )
3529 continue;
3530
3531 if( SCIPvarGetStatus(var2) == SCIP_VARSTATUS_NEGATED )
3532 {
3533 var2 = SCIPvarGetNegatedVar(var2);
3534 neg2 = TRUE;
3535 }
3536 else
3537 neg2 = FALSE;
3538
3539 if( !SCIPvarIsActive(var2) )
3540 continue;
3541
3542 /* no cliques available */
3543 if( SCIPvarGetNCliques(var2, neg2) == 0 && SCIPvarGetNImpls(var2, neg2) == 0 )
3544 continue;
3545
3546 /* check if both active variable are the same */
3547 if( var1 == var2 )
3548 {
3549 if( neg1 != neg2 )
3550 {
3551 SCIPdebugMsg(scip, "logicor constraint <%s> is redundant, because variable <%s> and its negation <%s> exist\n",
3552 SCIPconsGetName(cons), SCIPvarGetName(var1), SCIPvarGetName(var2));
3553
3554 SCIP_CALL( SCIPdelCons(scip, cons) );
3555
3556 breakloop = TRUE;
3557 }
3558 else
3559 {
3560 #ifndef NDEBUG
3561 SCIP_VAR* lastvar = consdata->vars[consdata->nvars - 1];
3562 #endif
3563 SCIPdebugMsg(scip, "in logicor constraint <%s>, active variable of <%s> and active variable of <%s> are the same, removing the first\n",
3564 SCIPconsGetName(cons), SCIPvarGetName(consdata->vars[v]), SCIPvarGetName(consdata->vars[w]));
3565
3566 SCIP_CALL( delCoefPos(scip, cons, eventhdlr, v) );
3567
3568 if( v < consdata->nvars )
3569 {
3570 /* delCoefPos replaces the variable on position v with the last one, so w also need to correct the
3571 * negated array the same way, and because of deletion the number of variables is already decreased
3572 */
3573 assert(consdata->vars[v] == lastvar);
3574 negated[v] = negated[consdata->nvars];
3575 }
3576 ++(*nchgcoefs);
3577 }
3578 break;
3579 }
3580
3581 if( SCIPvarsHaveCommonClique(var1, neg1, var2, neg2, TRUE) && conshdlrsetppc != NULL )
3582 {
3583 SCIP_CONS* newcons;
3584 SCIP_VAR* vars[2];
3585
3586 /* this negated clique information could be created out of this logicor constraint even if there are more
3587 * than two variables left (, for example by probing), we need to keep this information by creating a
3588 * setppc constraint instead
3589 */
3590
3591 /* get correct variables */
3592 if( !neg1 )
3593 vars[0] = SCIPvarGetNegatedVar(var1);
3594 else
3595 vars[0] = var1;
3596
3597 if( !neg2 )
3598 vars[1] = SCIPvarGetNegatedVar(var2);
3599 else
3600 vars[1] = var2;
3601
3602 SCIP_CALL( SCIPcreateConsSetpack(scip, &newcons, SCIPconsGetName(cons), 2, vars,
3603 SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), SCIPconsIsEnforced(cons),
3604 SCIPconsIsChecked(cons), SCIPconsIsPropagated(cons),
3605 SCIPconsIsLocal(cons), SCIPconsIsModifiable(cons),
3606 SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons), SCIPconsIsStickingAtNode(cons)) );
3607
3608 SCIP_CALL( SCIPaddCons(scip, newcons) );
3609 SCIPdebugPrintCons(scip, newcons, NULL);
3610
3611 SCIP_CALL( SCIPreleaseCons(scip, &newcons) );
3612
3613 SCIPdebugMsg(scip, "logicor constraint <%s> is redundant due to negated clique information and will be replaced by a setppc constraint \n",
3614 SCIPconsGetName(cons));
3615 SCIPdebugMsg(scip, "variable <%s> and variable <%s> are in a negated clique\n", SCIPvarGetName(consdata->vars[v]), SCIPvarGetName(consdata->vars[w]));
3616
3617 SCIP_CALL( SCIPdelCons(scip, cons) );
3618 ++(*nupgdconss);
3619
3620 breakloop = TRUE;
3621 break;
3622 }
3623 }
3624 if( breakloop )
3625 break;
3626
3627 /* do not do to many comparisons */
3628 if( comppercons > maxcomppercons )
3629 break;
3630 }
3631 }
3632
3633 TERMINATE:
3634 /* free temporary memory */
3635 SCIPfreeBufferArray(scip, &negated);
3636 SCIPfreeBufferArray(scip, &repvars);
3637
3638 return SCIP_OKAY;
3639 }
3640
3641 /** handle all cases with less than three variables in a logicor constraint
3642 *
3643 * in case a constraint has zero variables left, we detected infeasibility
3644 * in case a constraint has one variables left, we will fix it to one
3645 * in case a constraint has two variables left, we will add the implication and upgrade it to a set-packing constraint
3646 */
3647 static
3648 SCIP_RETCODE fixDeleteOrUpgradeCons(
3649 SCIP* scip, /**< SCIP data structure */
3650 SCIP_CONS* cons, /**< logic or constraint */
3651 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */
3652 SCIP_CONSHDLR* conshdlrlinear, /**< linear constraint handler, or NULL */
3653 SCIP_CONSHDLR* conshdlrsetppc, /**< setppc constraint handler, or NULL */
3654 int* nfixedvars, /**< pointer to count number of fixings */
3655 int* nchgbds, /**< pointer to count number of tightened bounds */
3656 int* nchgcoefs, /**< pointer to count number of changed/deleted coefficients */
3657 int* ndelconss, /**< pointer to count number of deleted constraints */
3658 int* naddconss, /**< pointer to count number of added constraints */
3659 int* nupgdconss, /**< pointer to count number of upgraded constraints */
3660 SCIP_Bool* cutoff /**< pointer to store TRUE, if the node can be cut off */
3661 )
3662 {
3663 SCIP_CONSDATA* consdata;
3664 SCIP_Bool infeasible;
3665 SCIP_Bool fixed;
3666
3667 assert(scip != NULL);
3668 assert(cons != NULL);
3669 assert(eventhdlr != NULL);
3670 assert(nfixedvars != NULL);
3671 assert(nchgbds != NULL);
3672 assert(nchgcoefs != NULL);
3673 assert(ndelconss != NULL);
3674 assert(naddconss != NULL);
3675 assert(nupgdconss != NULL);
3676 assert(cutoff != NULL);
3677
3678 *cutoff = FALSE;
3679
3680 if( SCIPconsIsModifiable(cons) )
3681 return SCIP_OKAY;
3682
3683 consdata = SCIPconsGetData(cons);
3684 assert(consdata != NULL);
3685
3686 /* if an unmodifiable logicor constraint has only two variables, we can add an implication and we will upgrade this
3687 * constraint to a set-packing constraint
3688 */
3689 if( consdata->nvars == 2 )
3690 {
3691 /* add implication if not yet done */
3692 if( !consdata->impladded )
3693 {
3694 SCIP_Bool implinfeasible;
3695 int nimplbdchgs;
3696 SCIP_Bool values[2];
3697
3698 values[0] = FALSE;
3699 values[1] = FALSE;
3700 /* a two-variable logicor constraint x + y >= 1 yields the implication x == 0 -> y == 1, and is represented
3701 * by the clique inequality ~x + ~y <= 1
3702 */
3703 SCIP_CALL( SCIPaddClique(scip, consdata->vars, values, consdata->nvars, FALSE, &implinfeasible, &nimplbdchgs) );
3704 *nchgbds += nimplbdchgs;
3705 if( implinfeasible )
3706 {
3707 *cutoff = TRUE;
3708 return SCIP_OKAY;
3709 }
3710
3711 /* adding the above implication could lead to fixings, which render the constraint redundant */
3712 if ( nimplbdchgs > 0 )
3713 {
3714 SCIP_Bool redundant;
3715
3716 /* remove all variables that are fixed to zero, check redundancy due to fixed-to-one variable */
3717 SCIP_CALL( applyFixings(scip, cons, eventhdlr, &redundant, nchgcoefs, naddconss, ndelconss) );
3718 assert(!SCIPconsIsDeleted(cons));
3719
3720 if( redundant )
3721 {
3722 SCIPdebugMsg(scip, "logic or constraint <%s> is redundant\n", SCIPconsGetName(cons));
3723
3724 SCIP_CALL( SCIPdelCons(scip, cons) );
3725 (*ndelconss)++;
3726
3727 return SCIP_OKAY;
3728 }
3729 }
3730 consdata->impladded = TRUE;
3731 }
3732
3733 /* still we have two variables left, we will upgrade this constraint */
3734 if( consdata->nvars == 2 && conshdlrsetppc != NULL )
3735 {
3736 SCIP_CONS* newcons;
3737 SCIP_VAR* vars[2];
3738
3739 /* get correct variables */
3740 SCIP_CALL( SCIPgetNegatedVar(scip, consdata->vars[0], &vars[0]) );
3741 SCIP_CALL( SCIPgetNegatedVar(scip, consdata->vars[1], &vars[1]) );
3742
3743 SCIP_CALL( SCIPcreateConsSetpack(scip, &newcons, SCIPconsGetName(cons), 2, vars,
3744 SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), SCIPconsIsEnforced(cons),
3745 SCIPconsIsChecked(cons), SCIPconsIsPropagated(cons),
3746 SCIPconsIsLocal(cons), SCIPconsIsModifiable(cons),
3747 SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons), SCIPconsIsStickingAtNode(cons)) );
3748
3749 SCIP_CALL( SCIPaddCons(scip, newcons) );
3750 SCIPdebugPrintCons(scip, newcons, NULL);
3751
3752 SCIP_CALL( SCIPreleaseCons(scip, &newcons) );
3753
3754 SCIPdebugMsg(scip, "logicor constraint <%s> was upgraded to a set-packing constraint\n", SCIPconsGetName(cons));
3755
3756 SCIP_CALL( SCIPdelCons(scip, cons) );
3757 ++(*nupgdconss);
3758 }
3759 }
3760
3761 /* if unmodifiable constraint has no variables, it is infeasible,
3762 * if unmodifiable constraint has only one variable, this one can be fixed and the constraint deleted
3763 */
3764 if( consdata->nvars == 0 )
3765 {
3766 SCIPdebugMsg(scip, "logic or constraint <%s> is infeasible\n", SCIPconsGetName(cons));
3767
3768 *cutoff = TRUE;
3769 }
3770 else if( consdata->nvars == 1 )
3771 {
3772 SCIPdebugMsg(scip, "logic or constraint <%s> has only one variable not fixed to 0.0\n",
3773 SCIPconsGetName(cons));
3774
3775 assert(consdata->vars != NULL);
3776 assert(consdata->vars[0] != NULL);
3777
3778 if( SCIPvarGetStatus(consdata->vars[0]) != SCIP_VARSTATUS_MULTAGGR )
3779 {
3780 SCIPdebugMsg(scip, " -> fix variable and delete constraint\n");
3781
3782 SCIP_CALL( SCIPfixVar(scip, consdata->vars[0], 1.0, &infeasible, &fixed) );
3783 if( infeasible )
3784 {
3785 SCIPdebugMsg(scip, " -> infeasible fixing\n");
3786
3787 *cutoff = TRUE;
3788 return SCIP_OKAY;
3789 }
3790 if( fixed )
3791 (*nfixedvars)++;
3792
3793 SCIP_CALL( SCIPdelCons(scip, cons) );
3794 (*ndelconss)++;
3795 }
3796 else if( conshdlrlinear != NULL )
3797 {
3798 SCIP_Real coef;
3799 SCIP_CONS* conslinear;
3800 char consname[SCIP_MAXSTRLEN];
3801
3802 SCIPdebugMsg(scip, " -> variable is multi-aggregated, upgrade to linear constraint <%s> == 1 \n",
3803 SCIPvarGetName(consdata->vars[0]));
3804
3805 coef = 1.0;
3806 (void) SCIPsnprintf(consname, SCIP_MAXSTRLEN, "fixmaggr_%s_%s", SCIPconsGetName(cons),SCIPvarGetName(consdata->vars[0]) );
3807 SCIP_CALL( SCIPcreateConsLinear(scip, &conslinear, consname, 1, consdata->vars, &coef, 1.0, 1.0,
3808 SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), SCIPconsIsEnforced(cons),
3809 SCIPconsIsChecked(cons), SCIPconsIsPropagated(cons), SCIPconsIsLocal(cons),
3810 SCIPconsIsModifiable(cons), SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons),
3811 SCIPconsIsStickingAtNode(cons)) );
3812
3813 /* add constraint */
3814 SCIP_CALL( SCIPaddCons(scip, conslinear) );
3815 SCIP_CALL( SCIPreleaseCons(scip, &conslinear) );
3816 SCIP_CALL( SCIPdelCons(scip, cons) );
3817
3818 (*ndelconss)++;
3819 (*naddconss)++;
3820 }
3821 }
3822
3823 return SCIP_OKAY;
3824 }
3825
3826
3827 /*
3828 * upgrading of linear constraints
3829 */
3830
3831 /** creates and captures a normalized (with all coefficients +1) logic or constraint */
3832 static
3833 SCIP_RETCODE createNormalizedLogicor(
3834 SCIP* scip, /**< SCIP data structure */
3835 SCIP_CONS** cons, /**< pointer to hold the created constraint */
3836 const char* name, /**< name of constraint */
3837 int nvars, /**< number of variables in the constraint */
3838 SCIP_VAR** vars, /**< array with variables of constraint entries */
3839 SCIP_Real* vals, /**< array with coefficients (+1.0 or -1.0) */
3840 int mult, /**< multiplier on the coefficients(+1 or -1) */
3841 SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP?
3842 * Usually set to TRUE. Set to FALSE for 'lazy constraints'. */
3843 SCIP_Bool separate, /**< should the constraint be separated during LP processing?
3844 * Usually set to TRUE. */
3845 SCIP_Bool enforce, /**< should the constraint be enforced during node processing?
3846 * TRUE for model constraints, FALSE for additional, redundant constraints. */
3847 SCIP_Bool check, /**< should the constraint be checked for feasibility?
3848 * TRUE for model constraints, FALSE for additional, redundant constraints. */
3849 SCIP_Bool propagate, /**< should the constraint be propagated during node processing?
3850 * Usually set to TRUE. */
3851 SCIP_Bool local, /**< is constraint only valid locally?
3852 * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */
3853 SCIP_Bool modifiable, /**< is constraint modifiable (subject to column generation)?
3854 * Usually set to FALSE. In column generation applications, set to TRUE if pricing
3855 * adds coefficients to this constraint. */
3856 SCIP_Bool dynamic, /**< is constraint subject to aging?
3857 * Usually set to FALSE. Set to TRUE for own cuts which
3858 * are separated as constraints. */
3859 SCIP_Bool removable, /**< should the relaxation be removed from the LP due to aging or cleanup?
3860 * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */
3861 SCIP_Bool stickingatnode /**< should the constraint always be kept at the node where it was added, even
3862 * if it may be moved to a more global node?
3863 * Usually set to FALSE. Set to TRUE to for constraints that represent node data. */
3864 )
3865 {
3866 SCIP_VAR** transvars;
3867 int v;
3868
3869 assert(nvars == 0 || vars != NULL);
3870 assert(nvars == 0 || vals != NULL);
3871 assert(mult == +1 || mult == -1);
3872
3873 /* get temporary memory */
3874 SCIP_CALL( SCIPallocBufferArray(scip, &transvars, nvars) );
3875
3876 /* negate positive or negative variables */
3877 for( v = 0; v < nvars; ++v )
3878 {
3879 if( mult * vals[v] > 0.0 )
3880 transvars[v] = vars[v];
3881 else
3882 {
3883 SCIP_CALL( SCIPgetNegatedVar(scip, vars[v], &transvars[v]) );
3884 }
3885 assert(transvars[v] != NULL);
3886 }
3887
3888 /* create the constraint */
3889 SCIP_CALL( SCIPcreateConsLogicor(scip, cons, name, nvars, transvars,
3890 initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode) );
3891
3892 /* free temporary memory */
3893 SCIPfreeBufferArray(scip, &transvars);
3894
3895 return SCIP_OKAY;
3896 }
3897
3898 static
3899 SCIP_DECL_LINCONSUPGD(linconsUpgdLogicor)
3900 { /*lint --e{715}*/
3901 assert(upgdcons != NULL);
3902
3903 /* check, if linear constraint can be upgraded to logic or constraint
3904 * - logic or constraints consist only of binary variables with a
3905 * coefficient of +1.0 or -1.0 (variables with -1.0 coefficients can be negated):
3906 * lhs <= x1 + ... + xp - y1 - ... - yn <= rhs
3907 * - negating all variables y = (1-Y) with negative coefficients gives:
3908 * lhs + n <= x1 + ... + xp + Y1 + ... + Yn <= rhs + n
3909 * - negating all variables x = (1-X) with positive coefficients and multiplying with -1 gives:
3910 * p - rhs <= X1 + ... + Xp + y1 + ... + yn <= p - lhs
3911 * - logic or constraints have left hand side of +1.0, and right hand side of +infinity: x(S) >= 1.0
3912 * -> without negations: (lhs == 1 - n and rhs == +inf) or (lhs == -inf and rhs = p - 1)
3913 */
3914 if( nvars > 2 && nposbin + nnegbin + nposimplbin + nnegimplbin == nvars && ncoeffspone + ncoeffsnone == nvars
3915 && ((SCIPisEQ(scip, lhs, 1.0 - ncoeffsnone) && SCIPisInfinity(scip, rhs))
3916 || (SCIPisInfinity(scip, -lhs) && SCIPisEQ(scip, rhs, ncoeffspone - 1.0))) )
3917 {
3918 int mult;
3919
3920 SCIPdebugMsg(scip, "upgrading constraint <%s> to logic or constraint\n", SCIPconsGetName(cons));
3921
3922 /* check, if we have to multiply with -1 (negate the positive vars) or with +1 (negate the negative vars) */
3923 mult = SCIPisInfinity(scip, rhs) ? +1 : -1;
3924
3925 /* create the logic or constraint (an automatically upgraded constraint is always unmodifiable) */
3926 assert(!SCIPconsIsModifiable(cons));
3927 SCIP_CALL( createNormalizedLogicor(scip, upgdcons, SCIPconsGetName(cons), nvars, vars, vals, mult,
3928 SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), SCIPconsIsEnforced(cons),
3929 SCIPconsIsChecked(cons), SCIPconsIsPropagated(cons),
3930 SCIPconsIsLocal(cons), SCIPconsIsModifiable(cons),
3931 SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons), SCIPconsIsStickingAtNode(cons)) );
3932 }
3933
3934 return SCIP_OKAY;
3935 }
3936
3937 /** helper function to enforce constraints */
3938 static
3939 SCIP_RETCODE enforceConstraint(
3940 SCIP* scip, /**< SCIP data structure */
3941 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
3942 SCIP_CONS** conss, /**< constraints to process */
3943 int nconss, /**< number of constraints */
3944 int nusefulconss, /**< number of useful (non-obsolete) constraints to process */
3945 SCIP_SOL* sol, /**< solution to enforce (NULL for the LP solution) */
3946 SCIP_RESULT* result /**< pointer to store the result of the enforcing call */
3947 )
3948 {
3949 SCIP_CONSHDLRDATA* conshdlrdata;
3950 SCIP_Bool cutoff;
3951 SCIP_Bool separated;
3952 SCIP_Bool reduceddom;
3953 int c;
3954
3955 assert(conshdlr != NULL);
3956 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
3957 assert(nconss == 0 || conss != NULL);
3958 assert(result != NULL);
3959
3960 SCIPdebugMsg(scip, "Enforcing %d logic or constraints for %s solution\n", nconss, sol == NULL ? "LP" : "relaxation");
3961
3962 *result = SCIP_FEASIBLE;
3963
3964 conshdlrdata = SCIPconshdlrGetData(conshdlr);
3965 assert(conshdlrdata != NULL);
3966
3967 cutoff = FALSE;
3968 separated = FALSE;
3969 reduceddom = FALSE;
3970
3971 /* check all useful logic or constraints for feasibility */
3972 for( c = 0; c < nusefulconss && !cutoff && !reduceddom; ++c )
3973 {
3974 SCIP_CALL( separateCons(scip, conss[c], sol, conshdlrdata->eventhdlr, &cutoff, &separated, &reduceddom) );
3975 }
3976
3977 /* check all obsolete logic or constraints for feasibility */
3978 for( c = nusefulconss; c < nconss && !cutoff && !separated && !reduceddom; ++c )
3979 {
3980 SCIP_CALL( separateCons(scip, conss[c], sol, conshdlrdata->eventhdlr, &cutoff, &separated, &reduceddom) );
3981 }
3982
3983 /* return the correct result */
3984 if( cutoff )
3985 *result = SCIP_CUTOFF;
3986 else if( separated )
3987 *result = SCIP_SEPARATED;
3988 else if( reduceddom )
3989 *result = SCIP_REDUCEDDOM;
3990
3991 return SCIP_OKAY;
3992 }
3993
3994
3995 /*
3996 * Callback methods of constraint handler
3997 */
3998
3999 /** copy method for constraint handler plugins (called when SCIP copies plugins) */
4000 static
4001 SCIP_DECL_CONSHDLRCOPY(conshdlrCopyLogicor)
4002 { /*lint --e{715}*/
4003 assert(scip != NULL);
4004 assert(conshdlr != NULL);
4005 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
4006
4007 /* call inclusion method of constraint handler */
4008 SCIP_CALL( SCIPincludeConshdlrLogicor(scip) );
4009
4010 *valid = TRUE;
4011
4012 return SCIP_OKAY;
4013 }
4014
4015 /** destructor of constraint handler to free constraint handler data (called when SCIP is exiting) */
4016 static
4017 SCIP_DECL_CONSFREE(consFreeLogicor)
4018 { /*lint --e{715}*/
4019 SCIP_CONSHDLRDATA* conshdlrdata;
4020
4021 assert(conshdlr != NULL);
4022 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
4023 assert(scip != NULL);
4024
4025 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4026 assert(conshdlrdata != NULL);
4027
4028 /* free constraint handler data */
4029 conshdlrdataFree(scip, &conshdlrdata);
4030
4031 SCIPconshdlrSetData(conshdlr, NULL);
4032
4033 return SCIP_OKAY;
4034 }
4035
4036
4037 /** presolving initialization method of constraint handler (called when presolving is about to begin) */
4038 static
4039 SCIP_DECL_CONSINITPRE(consInitpreLogicor)
4040 { /*lint --e{715}*/
4041 SCIP_CONSHDLRDATA* conshdlrdata;
4042 SCIP_CONSDATA* consdata;
4043 int c;
4044 int v;
4045
4046 assert(conshdlr != NULL);
4047 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4048 assert(conshdlrdata != NULL);
4049
4050 conshdlrdata->nlastcliquesneg = 0;
4051 conshdlrdata->nlastimplsneg = 0;
4052 conshdlrdata->nlastcliquesshorten = 0;
4053 conshdlrdata->nlastimplsshorten = 0;
4054
4055 /* catch all variable event for deleted variables, which is only used in presolving */
4056 for( c = nconss - 1; c >= 0; --c )
4057 {
4058 consdata = SCIPconsGetData(conss[c]);
4059 assert(consdata != NULL);
4060
4061 for( v = consdata->nvars - 1; v >= 0; --v )
4062 {
4063 SCIP_CALL( SCIPcatchVarEvent(scip, consdata->vars[v], SCIP_EVENTTYPE_VARFIXED, conshdlrdata->eventhdlr,
4064 (SCIP_EVENTDATA*)conss[c], NULL) );
4065 }
4066 }
4067
4068 return SCIP_OKAY;
4069 }
4070 /** presolving deinitialization method of constraint handler (called after presolving has been finished) */
4071 static
4072 SCIP_DECL_CONSEXITPRE(consExitpreLogicor)
4073 { /*lint --e{715}*/
4074 SCIP_CONSHDLRDATA* conshdlrdata;
4075 SCIP_CONSDATA* consdata;
4076 int nchgcoefs = 0;
4077 int c;
4078 int v;
4079
4080 assert(conshdlr != NULL);
4081 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4082 assert(conshdlrdata != NULL);
4083
4084 /* drop all variable event for deleted variables, which was only used in presolving */
4085 for( c = 0; c < nconss; ++c )
4086 {
4087 consdata = SCIPconsGetData(conss[c]);
4088 assert(consdata != NULL);
4089
4090 for( v = 0; v < consdata->nvars; ++v )
4091 {
4092 SCIP_CALL( SCIPdropVarEvent(scip, consdata->vars[v], SCIP_EVENTTYPE_VARFIXED, conshdlrdata->eventhdlr,
4093 (SCIP_EVENTDATA*)conss[c], -1) );
4094 }
4095
4096 if( !SCIPconsIsDeleted(conss[c]) && !consdata->presolved )
4097 {
4098 SCIP_Bool redundant;
4099
4100 /* we are not allowed to detect infeasibility in the exitpre stage */
4101 SCIP_CALL( applyFixings(scip, conss[c], conshdlrdata->eventhdlr, &redundant, &nchgcoefs, NULL, NULL) );
4102
4103 /* it may happen that a constraint still contains variables that are fixed to one; for example, this happens
4104 * when variable fixings have been detected in the last presolving round by some other plugins (see #2941)
4105 */
4106 if( redundant )
4107 {
4108 SCIPdebugMsg(scip, "logic or constraint <%s> is redundant (detected during EXITPRE)\n", SCIPconsGetName(conss[c]));
4109
4110 if( SCIPconsIsAdded(conss[c]) )
4111 {
4112 SCIP_CALL( SCIPdelCons(scip, conss[c]) );
4113 }
4114 else
4115 {
4116 /* we set the presolved flag to FALSE since not all fixing are removed if redundancy is detected */
4117 consdata->presolved = FALSE;
4118 }
4119 }
4120 }
4121 }
4122
4123 return SCIP_OKAY;
4124 }
4125
4126 /** solving process initialization method of constraint handler */
4127 static
4128 SCIP_DECL_CONSINITSOL(consInitsolLogicor)
4129 { /*lint --e{715}*/
4130
4131 /* add nlrow representation to NLP, if NLP had been constructed */
4132 if( SCIPisNLPConstructed(scip) )
4133 {
4134 int c;
4135 for( c = 0; c < nconss; ++c )
4136 {
4137 SCIP_CALL( addNlrow(scip, conss[c]) );
4138 }
4139 }
4140
4141 return SCIP_OKAY;
4142 }
4143
4144 /** solving process deinitialization method of constraint handler (called before branch and bound process data is freed) */
4145 static
4146 SCIP_DECL_CONSEXITSOL(consExitsolLogicor)
4147 { /*lint --e{715}*/
4148 SCIP_CONSDATA* consdata;
4149 int c;
4150
4151 /* release the rows and nlrows of all constraints */
4152 for( c = 0; c < nconss; ++c )
4153 {
4154 consdata = SCIPconsGetData(conss[c]);
4155 assert(consdata != NULL);
4156
4157 if( consdata->row != NULL )
4158 {
4159 SCIP_CALL( SCIPreleaseRow(scip, &consdata->row) );
4160 }
4161
4162 if( consdata->nlrow != NULL )
4163 {
4164 SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
4165 }
4166 }
4167
4168 return SCIP_OKAY;
4169 }
4170
4171
4172 /** frees specific constraint data */
4173 static
4174 SCIP_DECL_CONSDELETE(consDeleteLogicor)
4175 { /*lint --e{715}*/
4176 assert(conshdlr != NULL);
4177 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
4178 assert(consdata != NULL);
4179 assert(*consdata != NULL);
4180
4181 if( SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING || SCIPgetStage(scip) == SCIP_STAGE_INITPRESOLVE )
4182 {
4183 SCIP_CONSHDLRDATA* conshdlrdata;
4184 int v;
4185
4186 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4187 assert(conshdlrdata != NULL);
4188
4189 for( v = (*consdata)->nvars - 1; v >= 0; --v )
4190 {
4191 SCIP_CALL( SCIPdropVarEvent(scip, (*consdata)->vars[v], SCIP_EVENTTYPE_VARFIXED, conshdlrdata->eventhdlr,
4192 (SCIP_EVENTDATA*)cons, -1) );
4193 }
4194 }
4195
4196 /* free LP row and logic or constraint */
4197 SCIP_CALL( consdataFree(scip, consdata) );
4198
4199 return SCIP_OKAY;
4200 }
4201
4202
4203 /** transforms constraint data into data belonging to the transformed problem */
4204 static
4205 SCIP_DECL_CONSTRANS(consTransLogicor)
4206 { /*lint --e{715}*/
4207 SCIP_CONSDATA* sourcedata;
4208 SCIP_CONSDATA* targetdata;
4209
4210 /*debugMsg(scip, "Trans method of logic or constraints\n");*/
4211
4212 assert(conshdlr != NULL);
4213 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
4214 assert(SCIPgetStage(scip) == SCIP_STAGE_TRANSFORMING);
4215 assert(sourcecons != NULL);
4216 assert(targetcons != NULL);
4217
4218 sourcedata = SCIPconsGetData(sourcecons);
4219 assert(sourcedata != NULL);
4220 assert(sourcedata->row == NULL); /* in original problem, there cannot be LP rows */
4221
4222 /* create constraint data for target constraint */
4223 SCIP_CALL( consdataCreate(scip, &targetdata, sourcedata->nvars, sourcedata->vars) );
4224
4225 /* create target constraint */
4226 SCIP_CALL( SCIPcreateCons(scip, targetcons, SCIPconsGetName(sourcecons), conshdlr, targetdata,
4227 SCIPconsIsInitial(sourcecons), SCIPconsIsSeparated(sourcecons), SCIPconsIsEnforced(sourcecons),
4228 SCIPconsIsChecked(sourcecons), SCIPconsIsPropagated(sourcecons),
4229 SCIPconsIsLocal(sourcecons), SCIPconsIsModifiable(sourcecons),
4230 SCIPconsIsDynamic(sourcecons), SCIPconsIsRemovable(sourcecons), SCIPconsIsStickingAtNode(sourcecons)) );
4231
4232 return SCIP_OKAY;
4233 }
4234
4235
4236 /** LP initialization method of constraint handler (called before the initial LP relaxation at a node is solved) */
4237 static
4238 SCIP_DECL_CONSINITLP(consInitlpLogicor)
4239 { /*lint --e{715}*/
4240 int c;
4241
4242 *infeasible = FALSE;
4243
4244 for( c = 0; c < nconss && !(*infeasible); ++c )
4245 {
4246 assert(SCIPconsIsInitial(conss[c]));
4247 SCIP_CALL( addCut(scip, conss[c], infeasible) );
4248 }
4249
4250 return SCIP_OKAY;
4251 }
4252
4253
4254 /** separation method of constraint handler for LP solutions */
4255 static
4256 SCIP_DECL_CONSSEPALP(consSepalpLogicor)
4257 { /*lint --e{715}*/
4258 SCIP_CONSHDLRDATA* conshdlrdata;
4259 SCIP_Bool cutoff;
4260 SCIP_Bool separated;
4261 SCIP_Bool reduceddom;
4262 int c;
4263
4264 assert(conshdlr != NULL);
4265 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
4266 assert(nconss == 0 || conss != NULL);
4267 assert(result != NULL);
4268
4269 SCIPdebugMsg(scip, "separating %d/%d logic or constraints\n", nusefulconss, nconss);
4270
4271 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4272 assert(conshdlrdata != NULL);
4273
4274 cutoff = FALSE;
4275 separated = FALSE;
4276 reduceddom = FALSE;
4277
4278 /* check all useful logic or constraints for feasibility */
4279 for( c = 0; c < nusefulconss && !cutoff; ++c )
4280 {
4281 SCIP_CALL( separateCons(scip, conss[c], NULL, conshdlrdata->eventhdlr, &cutoff, &separated, &reduceddom) );
4282 }
4283
4284 /* combine logic or constraints to get more cuts */
4285 /**@todo further cuts of logic or constraints */
4286
4287 /* return the correct result */
4288 if( cutoff )
4289 *result = SCIP_CUTOFF;
4290 else if( reduceddom )
4291 *result = SCIP_REDUCEDDOM;
4292 else if( separated )
4293 *result = SCIP_SEPARATED;
4294 else
4295 *result = SCIP_DIDNOTFIND;
4296
4297 return SCIP_OKAY;
4298 }
4299
4300
4301 /** separation method of constraint handler for arbitrary primal solutions */
4302 static
4303 SCIP_DECL_CONSSEPASOL(consSepasolLogicor)
4304 { /*lint --e{715}*/
4305 SCIP_CONSHDLRDATA* conshdlrdata;
4306 SCIP_Bool cutoff;
4307 SCIP_Bool separated;
4308 SCIP_Bool reduceddom;
4309 int c;
4310
4311 assert(conshdlr != NULL);
4312 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
4313 assert(nconss == 0 || conss != NULL);
4314 assert(result != NULL);
4315
4316 SCIPdebugMsg(scip, "separating %d/%d logic or constraints\n", nusefulconss, nconss);
4317
4318 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4319 assert(conshdlrdata != NULL);
4320
4321 cutoff = FALSE;
4322 separated = FALSE;
4323 reduceddom = FALSE;
4324
4325 /* check all useful logic or constraints for feasibility */
4326 for( c = 0; c < nusefulconss && !cutoff; ++c )
4327 {
4328 SCIP_CALL( separateCons(scip, conss[c], sol, conshdlrdata->eventhdlr, &cutoff, &separated, &reduceddom) );
4329 }
4330
4331 /* combine logic or constraints to get more cuts */
4332 /**@todo further cuts of logic or constraints */
4333
4334 /* return the correct result */
4335 if( cutoff )
4336 *result = SCIP_CUTOFF;
4337 else if( reduceddom )
4338 *result = SCIP_REDUCEDDOM;
4339 else if( separated )
4340 *result = SCIP_SEPARATED;
4341 else
4342 *result = SCIP_DIDNOTFIND;
4343
4344 return SCIP_OKAY;
4345 }
4346
4347
4348 /** constraint enforcing method of constraint handler for LP solutions */
4349 static
4350 SCIP_DECL_CONSENFOLP(consEnfolpLogicor)
4351 { /*lint --e{715}*/
4352 SCIP_CALL( enforceConstraint(scip, conshdlr, conss, nconss, nusefulconss, NULL, result) );
4353
4354 return SCIP_OKAY;
4355 }
4356
4357
4358 /** constraint enforcing method of constraint handler for relaxation solutions */
4359 static
4360 SCIP_DECL_CONSENFORELAX(consEnforelaxLogicor)
4361 { /*lint --e{715}*/
4362 SCIP_CALL( enforceConstraint(scip, conshdlr, conss, nconss, nusefulconss, sol, result) );
4363
4364 return SCIP_OKAY;
4365 }
4366
4367
4368 /** constraint enforcing method of constraint handler for pseudo solutions */
4369 static
4370 SCIP_DECL_CONSENFOPS(consEnfopsLogicor)
4371 { /*lint --e{715}*/
4372 SCIP_CONSHDLRDATA* conshdlrdata;
4373 SCIP_Bool cutoff;
4374 SCIP_Bool infeasible;
4375 SCIP_Bool reduceddom;
4376 SCIP_Bool solvelp;
4377 int c;
4378
4379 assert(conshdlr != NULL);
4380 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
4381 assert(nconss == 0 || conss != NULL);
4382 assert(result != NULL);
4383
4384 SCIPdebugMsg(scip, "pseudo enforcing %d logic or constraints\n", nconss);
4385
4386 *result = SCIP_FEASIBLE;
4387
4388 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4389 assert(conshdlrdata != NULL);
4390
4391 cutoff = FALSE;
4392 infeasible = FALSE;
4393 reduceddom = FALSE;
4394 solvelp = FALSE;
4395
4396 /* check all logic or constraints for feasibility */
4397 for( c = 0; c < nconss && !cutoff && !reduceddom && !solvelp; ++c )
4398 {
4399 SCIP_CALL( enforcePseudo(scip, conss[c], conshdlrdata->eventhdlr, &cutoff, &infeasible, &reduceddom, &solvelp) );
4400 }
4401
4402 if( cutoff )
4403 *result = SCIP_CUTOFF;
4404 else if( reduceddom )
4405 *result = SCIP_REDUCEDDOM;
4406 else if( solvelp )
4407 *result = SCIP_SOLVELP;
4408 else if( infeasible )
4409 *result = SCIP_INFEASIBLE;
4410
4411 return SCIP_OKAY;
4412 }
4413
4414
4415 /** feasibility check method of constraint handler for integral solutions */
4416 static
4417 SCIP_DECL_CONSCHECK(consCheckLogicor)
4418 { /*lint --e{715}*/
4419 SCIP_CONS* cons;
4420 SCIP_CONSDATA* consdata;
4421 int c;
4422
4423 assert(conshdlr != NULL);
4424 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
4425 assert(nconss == 0 || conss != NULL);
4426 assert(result != NULL);
4427
4428 *result = SCIP_FEASIBLE;
4429
4430 /* check all logic or constraints for feasibility */
4431 for( c = 0; c < nconss && (*result == SCIP_FEASIBLE || completely); ++c )
4432 {
4433 cons = conss[c];
4434 consdata = SCIPconsGetData(cons);
4435 assert(consdata != NULL);
4436 if( checklprows || consdata->row == NULL || !SCIProwIsInLP(consdata->row) )
4437 {
4438 if( isConsViolated(scip, cons, sol) )
4439 {
4440 /* constraint is violated */
4441 *result = SCIP_INFEASIBLE;
4442
4443 if( printreason )
4444 {
4445 #ifndef NDEBUG
4446 int v;
4447 for( v = 0; v < consdata->nvars; ++v )
4448 {
4449 assert( consdata->vars[v] != NULL);
4450 assert( SCIPvarIsBinary(consdata->vars[v]) );
4451 assert( SCIPisFeasLT(scip, SCIPgetSolVal(scip, sol, consdata->vars[v]), 1.0) );
4452 }
4453 #endif
4454 SCIP_CALL( SCIPprintCons(scip, cons, NULL) );
4455 SCIPinfoMessage(scip, NULL, ";\n");
4456 SCIPinfoMessage(scip, NULL, "violation: all variables are set to zero\n");
4457 }
4458 }
4459 }
4460 }
4461
4462 return SCIP_OKAY;
4463 }
4464
4465
4466 /** domain propagation method of constraint handler */
4467 static
4468 SCIP_DECL_CONSPROP(consPropLogicor)
4469 { /*lint --e{715}*/
4470 SCIP_CONSHDLRDATA* conshdlrdata;
4471 SCIP_Bool cutoff;
4472 SCIP_Bool reduceddom;
4473 SCIP_Bool addcut;
4474 SCIP_Bool mustcheck;
4475 int c;
4476 #ifndef NDEBUG
4477 SCIP_Bool inpresolve = (SCIPgetStage(scip) < SCIP_STAGE_INITSOLVE);
4478 #endif
4479
4480 assert(conshdlr != NULL);
4481 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
4482 assert(nconss == 0 || conss != NULL);
4483 assert(result != NULL);
4484
4485 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4486 assert(conshdlrdata != NULL);
4487
4488 cutoff = FALSE;
4489 reduceddom = FALSE;
4490
4491 /* propagate all useful logic or constraints */
4492 for( c = 0; c < nusefulconss && !cutoff; ++c )
4493 {
4494 assert(inpresolve || !(SCIPconsGetData(conss[c])->existmultaggr));
4495
4496 SCIPdebugMsg(scip, " propagate constraint %s\n", SCIPconsGetName(conss[c]));
4497 SCIP_CALL( processWatchedVars(scip, conss[c], conshdlrdata->eventhdlr, &cutoff, &reduceddom, &addcut, &mustcheck) );
4498 }
4499
4500 /* return the correct result */
4501 if( cutoff )
4502 *result = SCIP_CUTOFF;
4503 else if( reduceddom )
4504 *result = SCIP_REDUCEDDOM;
4505 else
4506 *result = SCIP_DIDNOTFIND;
4507
4508 return SCIP_OKAY; /*lint !e438*/
4509 }
4510
4511 /** presolving method of constraint handler */
4512 static
4513 SCIP_DECL_CONSPRESOL(consPresolLogicor)
4514 { /*lint --e{715}*/
4515 SCIP_CONSHDLRDATA* conshdlrdata;
4516 SCIP_CONS* cons;
4517 SCIP_CONSDATA* consdata;
4518 unsigned char* entries;
4519 SCIP_Bool redundant;
4520 int c;
4521 int firstchange;
4522 int nentries;
4523 int oldnfixedvars;
4524 int oldnchgbds;
4525 int oldndelconss;
4526 int oldnupgdconss;
4527 int oldnchgcoefs;
4528
4529 assert(conshdlr != NULL);
4530 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
4531 assert(scip != NULL);
4532 assert(result != NULL);
4533
4534 *result = SCIP_DIDNOTFIND;
4535
4536 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4537 assert(conshdlrdata != NULL);
4538
4539 nentries = SCIPgetNVars(scip) - SCIPgetNContVars(scip);
4540
4541 oldnfixedvars = *nfixedvars;
4542 oldnchgbds = *nchgbds;
4543 oldndelconss = *ndelconss;
4544 oldnupgdconss = *nupgdconss;
4545 oldnchgcoefs = *nchgcoefs;
4546
4547 firstchange = INT_MAX;
4548
4549 SCIP_CALL( SCIPallocBufferArray(scip, &entries, nentries) );
4550
4551 /* process constraints */
4552 for( c = 0; c < nconss && *result != SCIP_CUTOFF && !SCIPisStopped(scip); ++c )
4553 {
4554 cons = conss[c];
4555 assert(cons != NULL);
4556 consdata = SCIPconsGetData(cons);
4557 assert(consdata != NULL);
4558
4559 SCIPdebugMsg(scip, "presolving logic or constraint <%s>\n", SCIPconsGetName(cons));
4560
4561 /* force presolving the constraint in the initial round */
4562 if( nrounds == 0 )
4563 {
4564 SCIP_CALL( SCIPenableConsPropagation(scip, cons) );
4565 }
4566
4567 redundant = FALSE;
4568 if( !consdata->presolved )
4569 {
4570 /* remove all variables that are fixed to zero, check redundancy due to fixed-to-one variable */
4571 SCIP_CALL( applyFixings(scip, cons, conshdlrdata->eventhdlr, &redundant, nchgcoefs, naddconss, ndelconss) );
4572 }
4573
4574 if( SCIPconsIsDeleted(cons) )
4575 continue;
4576
4577 /* find pairs of negated variables in constraint: constraint is redundant */
4578 /* find sets of equal variables in constraint: multiple entries of variable can be replaced by single entry */
4579 if( !redundant )
4580 {
4581 SCIP_CALL( mergeMultiples(scip, cons, conshdlrdata->eventhdlr, &entries, &nentries, &redundant, nchgcoefs) );
4582 }
4583
4584 if( redundant )
4585 {
4586 SCIPdebugMsg(scip, "logic or constraint <%s> is redundant\n", SCIPconsGetName(cons));
4587 SCIP_CALL( SCIPdelCons(scip, cons) );
4588 (*ndelconss)++;
4589 *result = SCIP_SUCCESS;
4590 continue;
4591 }
4592 else if( !SCIPconsIsModifiable(cons) )
4593 {
4594 if( consdata->nvars <= 2 )
4595 {
4596 SCIP_Bool cutoff;
4597
4598 /* handle all cases with less than three variables in a logicor constraint */
4599 SCIP_CALL( fixDeleteOrUpgradeCons(scip, cons, conshdlrdata->eventhdlr, conshdlrdata->conshdlrlinear,
4600 conshdlrdata->conshdlrsetppc, nfixedvars, nchgbds, nchgcoefs, ndelconss, naddconss, nupgdconss, &cutoff) );
4601
4602 if( cutoff )
4603 {
4604 *result = SCIP_CUTOFF;
4605 goto TERMINATE;
4606 }
4607 else if( *nfixedvars > oldnfixedvars || *nchgbds > oldnchgbds || *nchgcoefs > oldnchgcoefs
4608 || *ndelconss > oldndelconss || *nupgdconss > oldnupgdconss )
4609 *result = SCIP_SUCCESS;
4610
4611 if( SCIPconsIsDeleted(cons) )
4612 continue;
4613 }
4614 }
4615
4616 /* perform dual reductions */
4617 if( conshdlrdata->dualpresolving && SCIPallowStrongDualReds(scip) )
4618 {
4619 SCIP_CALL( dualPresolving(scip, cons, conshdlrdata->eventhdlr, nfixedvars, ndelconss, nchgcoefs, result) );
4620
4621 /* if dual reduction deleted the constraint we take the next */
4622 if( !SCIPconsIsActive(cons) )
4623 continue;
4624
4625 /* in dualpresolving we may have removed variables, so we need to take care of special cases */
4626 if( consdata->nvars <= 2 )
4627 {
4628 SCIP_Bool cutoff;
4629
4630 /* handle all cases with less than three variables in a logicor constraint */
4631 SCIP_CALL( fixDeleteOrUpgradeCons(scip, cons, conshdlrdata->eventhdlr, conshdlrdata->conshdlrlinear,
4632 conshdlrdata->conshdlrsetppc, nfixedvars, nchgbds, nchgcoefs, ndelconss, naddconss, nupgdconss, &cutoff) );
4633
4634 if( cutoff )
4635 {
4636 *result = SCIP_CUTOFF;
4637 goto TERMINATE;
4638 }
4639 else if( *nfixedvars > oldnfixedvars || *nchgbds > oldnchgbds || *nchgcoefs > oldnchgcoefs
4640 || *ndelconss > oldndelconss || *nupgdconss > oldnupgdconss )
4641 *result = SCIP_SUCCESS;
4642
4643 if( SCIPconsIsDeleted(cons) )
4644 continue;
4645 }
4646 }
4647
4648 /* remember the first changed constraint to begin the next redundancy round with */
4649 if( firstchange == INT_MAX && consdata->changed )
4650 firstchange = c;
4651
4652 assert(consdata->nvars >= 2 || SCIPconsIsModifiable(cons));
4653 }
4654
4655 assert(*result != SCIP_CUTOFF);
4656
4657 /* fast preprocessing of pairs of logic or constraints, used for equal constraints */
4658 if( firstchange < nconss && conshdlrdata->presolusehashing )
4659 {
4660 /* detect redundant constraints; fast version with hash table instead of pairwise comparison */
4661 SCIP_CALL( detectRedundantConstraints(scip, SCIPblkmem(scip), conss, nconss, &firstchange, ndelconss) );
4662 }
4663
4664 /* preprocess pairs of logic or constraints and apply negated clique presolving */
4665 if( SCIPisPresolveFinished(scip) )
4666 {
4667 SCIP_Bool cutoff = FALSE;
4668
4669 /* check constraints for redundancy */
4670 if( conshdlrdata->presolpairwise && (presoltiming & SCIP_PRESOLTIMING_EXHAUSTIVE) != 0 )
4671 {
4672 SCIP_CALL( removeRedundantConssAndNonzeros(scip, conss, nconss, &entries, &nentries, conshdlrdata->eventhdlr,
4673 conshdlrdata->usestrengthening, &firstchange, nfixedvars, ndelconss, nchgcoefs, &cutoff) );
4674
4675 if( cutoff )
4676 {
4677 *result = SCIP_CUTOFF;
4678 goto TERMINATE;
4679 }
4680 }
4681
4682 if( SCIPisPresolveFinished(scip) )
4683 {
4684 /* try to tighten constraints by reducing the number of variables in the constraints using implications and
4685 * cliques, also derive fixations through them, @see SCIPshrinkDisjunctiveVarSet()
4686 */
4687 if( conshdlrdata->useimplications && (presoltiming & SCIP_PRESOLTIMING_EXHAUSTIVE) != 0 )
4688 {
4689 SCIP_CALL( shortenConss(scip, conshdlrdata, conshdlrdata->eventhdlr, conss, nconss,
4690 &entries, &nentries, nfixedvars, ndelconss, nchgcoefs, &cutoff) );
4691
4692 if( cutoff )
4693 {
4694 *result = SCIP_CUTOFF;
4695 goto TERMINATE;
4696 }
4697 }
4698
4699 /* check for redundant constraints due to negated clique information */
4700 if( conshdlrdata->usenegatedclique && (presoltiming & SCIP_PRESOLTIMING_MEDIUM) != 0 )
4701 {
4702 SCIP_CALL( removeConstraintsDueToNegCliques(scip, conshdlr, conshdlrdata->conshdlrsetppc,
4703 conshdlrdata->eventhdlr, conss, nconss, &entries, &nentries, nfixedvars, ndelconss,
4704 nupgdconss, nchgcoefs, &cutoff) );
4705
4706 if( cutoff )
4707 {
4708 *result = SCIP_CUTOFF;
4709 goto TERMINATE;
4710 }
4711 }
4712 }
4713 }
4714
4715 TERMINATE:
4716
4717 SCIPfreeBufferArray(scip, &entries);
4718
4719 return SCIP_OKAY;
4720 }
4721
4722
4723 /** propagation conflict resolving method of constraint handler */
4724 static
4725 SCIP_DECL_CONSRESPROP(consRespropLogicor)
4726 { /*lint --e{715}*/
4727 SCIP_CONSDATA* consdata;
4728 #ifndef NDEBUG
4729 SCIP_Bool infervarfound;
4730 #endif
4731 int v;
4732
4733 assert(conshdlr != NULL);
4734 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
4735 assert(cons != NULL);
4736 assert(infervar != NULL);
4737 assert(result != NULL);
4738
4739 consdata = SCIPconsGetData(cons);
4740 assert(consdata != NULL);
4741
4742 SCIPdebugMsg(scip, "conflict resolving method of logic or constraint handler\n");
4743
4744 /* the only deductions are variables infered to 1.0 on logic or constraints where all other variables
4745 * are assigned to zero
4746 */
4747 assert(SCIPgetVarLbAtIndex(scip, infervar, bdchgidx, TRUE) > 0.5); /* the inference variable must be assigned to one */
4748
4749 #ifndef NDEBUG
4750 infervarfound = FALSE;
4751 #endif
4752 for( v = 0; v < consdata->nvars; ++v )
4753 {
4754 if( consdata->vars[v] != infervar )
4755 {
4756 /* the reason variable must have been assigned to zero */
4757 assert(SCIPgetVarUbAtIndex(scip, consdata->vars[v], bdchgidx, FALSE) < 0.5);
4758 SCIP_CALL( SCIPaddConflictBinvar(scip, consdata->vars[v]) );
4759 }
4760 #ifndef NDEBUG
4761 else
4762 {
4763 assert(!infervarfound);
4764 infervarfound = TRUE;
4765 }
4766 #endif
4767 }
4768 assert(infervarfound);
4769
4770 *result = SCIP_SUCCESS;
4771
4772 return SCIP_OKAY;
4773 }
4774
4775
4776 /** variable rounding lock method of constraint handler */
4777 static
4778 SCIP_DECL_CONSLOCK(consLockLogicor)
4779 { /*lint --e{715}*/
4780 SCIP_CONSDATA* consdata;
4781 int i;
4782
4783 consdata = SCIPconsGetData(cons);
4784 assert(consdata != NULL);
4785
4786 /* lock every single coefficient */
4787 for( i = 0; i < consdata->nvars; ++i )
4788 {
4789 SCIP_CALL( SCIPaddVarLocksType(scip, consdata->vars[i], locktype, nlockspos, nlocksneg) );
4790 }
4791
4792 return SCIP_OKAY;
4793 }
4794
4795
4796 /** constraint activation notification method of constraint handler */
4797 static
4798 SCIP_DECL_CONSACTIVE(consActiveLogicor)
4799 { /*lint --e{715}*/
4800 SCIP_CONSHDLRDATA* conshdlrdata;
4801 SCIP_CONSDATA* consdata;
4802
4803 assert(conshdlr != NULL);
4804 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
4805 assert(cons != NULL);
4806 assert(SCIPconsIsTransformed(cons));
4807
4808 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4809 assert(conshdlrdata != NULL);
4810 consdata = SCIPconsGetData(cons);
4811 assert(consdata != NULL);
4812 assert(consdata->watchedvar1 == -1 || consdata->watchedvar1 != consdata->watchedvar2);
4813
4814 SCIPdebugMsg(scip, "activating information for logic or constraint <%s>\n", SCIPconsGetName(cons));
4815 SCIPdebug( SCIP_CALL(consdataPrint(scip, consdata, NULL, TRUE)) );
4816
4817 /* catch events on watched variables */
4818 if( consdata->watchedvar1 != -1 )
4819 {
4820 SCIP_CALL( SCIPcatchVarEvent(scip, consdata->vars[consdata->watchedvar1],
4821 SCIP_EVENTTYPE_UBTIGHTENED | SCIP_EVENTTYPE_LBRELAXED, conshdlrdata->eventhdlr, (SCIP_EVENTDATA*)cons,
4822 &consdata->filterpos1) );
4823 }
4824 if( consdata->watchedvar2 != -1 )
4825 {
4826 SCIP_CALL( SCIPcatchVarEvent(scip, consdata->vars[consdata->watchedvar2],
4827 SCIP_EVENTTYPE_UBTIGHTENED | SCIP_EVENTTYPE_LBRELAXED, conshdlrdata->eventhdlr, (SCIP_EVENTDATA*)cons,
4828 &consdata->filterpos2) );
4829 }
4830
4831 if( SCIPgetStage(scip) == SCIP_STAGE_SOLVING && SCIPisNLPConstructed(scip) )
4832 {
4833 SCIP_CALL( addNlrow(scip, cons) );
4834 }
4835
4836 return SCIP_OKAY;
4837 }
4838
4839
4840 /** constraint deactivation notification method of constraint handler */
4841 static
4842 SCIP_DECL_CONSDEACTIVE(consDeactiveLogicor)
4843 { /*lint --e{715}*/
4844 SCIP_CONSHDLRDATA* conshdlrdata;
4845 SCIP_CONSDATA* consdata;
4846
4847 assert(conshdlr != NULL);
4848 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
4849 assert(cons != NULL);
4850 assert(SCIPconsIsTransformed(cons));
4851
4852 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4853 assert(conshdlrdata != NULL);
4854 consdata = SCIPconsGetData(cons);
4855 assert(consdata != NULL);
4856 assert(consdata->watchedvar1 == -1 || consdata->watchedvar1 != consdata->watchedvar2);
4857
4858 SCIPdebugMsg(scip, "deactivating information for logic or constraint <%s>\n", SCIPconsGetName(cons));
4859 SCIPdebug( SCIP_CALL(consdataPrint(scip, consdata, NULL, TRUE)) );
4860
4861 /* drop events on watched variables */
4862 if( consdata->watchedvar1 != -1 )
4863 {
4864 assert(consdata->filterpos1 != -1);
4865 SCIP_CALL( SCIPdropVarEvent(scip, consdata->vars[consdata->watchedvar1],
4866 SCIP_EVENTTYPE_UBTIGHTENED | SCIP_EVENTTYPE_LBRELAXED, conshdlrdata->eventhdlr, (SCIP_EVENTDATA*)cons,
4867 consdata->filterpos1) );
4868 consdata->watchedvar1 = -1;
4869 consdata->filterpos1 = -1;
4870 }
4871 if( consdata->watchedvar2 != -1 )
4872 {
4873 assert(consdata->filterpos2 != -1);
4874 SCIP_CALL( SCIPdropVarEvent(scip, consdata->vars[consdata->watchedvar2],
4875 SCIP_EVENTTYPE_UBTIGHTENED | SCIP_EVENTTYPE_LBRELAXED, conshdlrdata->eventhdlr, (SCIP_EVENTDATA*)cons,
4876 consdata->filterpos2) );
4877 consdata->watchedvar2 = -1;
4878 consdata->filterpos2 = -1;
4879 }
4880
4881 /* remove row from NLP, if still in solving
4882 * if we are in exitsolve, the whole NLP will be freed anyway
4883 */
4884 if( SCIPgetStage(scip) == SCIP_STAGE_SOLVING && consdata->nlrow != NULL )
4885 {
4886 SCIP_CALL( SCIPdelNlRow(scip, consdata->nlrow) );
4887 }
4888
4889 return SCIP_OKAY;
4890 }
4891
4892
4893 /** constraint display method of constraint handler */
4894 static
4895 SCIP_DECL_CONSPRINT(consPrintLogicor)
4896 { /*lint --e{715}*/
4897 assert( scip != NULL );
4898 assert( conshdlr != NULL );
4899 assert( cons != NULL );
4900
4901 SCIP_CALL( consdataPrint(scip, SCIPconsGetData(cons), file, FALSE) );
4902
4903 return SCIP_OKAY;
4904 }
4905
4906 /** constraint copying method of constraint handler */
4907 static
4908 SCIP_DECL_CONSCOPY(consCopyLogicor)
4909 { /*lint --e{715}*/
4910 SCIP_VAR** sourcevars;
4911 const char* consname;
4912 int nvars;
4913
4914 /* get variables and coefficients of the source constraint */
4915 sourcevars = SCIPgetVarsLogicor(sourcescip, sourcecons);
4916 nvars = SCIPgetNVarsLogicor(sourcescip, sourcecons);
4917
4918 if( name != NULL )
4919 consname = name;
4920 else
4921 consname = SCIPconsGetName(sourcecons);
4922
4923 /* copy the logic using the linear constraint copy method */
4924 SCIP_CALL( SCIPcopyConsLinear(scip, cons, sourcescip, consname, nvars, sourcevars, NULL,
4925 1.0, SCIPinfinity(scip), varmap, consmap,
4926 initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode, global, valid) );
4927 assert(cons != NULL);
4928
4929 return SCIP_OKAY;
4930 }
4931
4932 /** constraint parsing method of constraint handler */
4933 static
4934 SCIP_DECL_CONSPARSE(consParseLogicor)
4935 { /*lint --e{715}*/
4936 SCIP_VAR** vars;
4937 char* strcopy;
4938 char* endptr;
4939 char* startptr;
4940 int requiredsize;
4941 int varssize;
4942 int nvars;
4943
4944 SCIPdebugMsg(scip, "parse <%s> as logicor constraint\n", str);
4945
4946 *success = FALSE;
4947
4948 /* cutoff "logicor" from the constraint string */
4949 startptr = strchr((char*)str, '(');
4950
4951 if( startptr == NULL )
4952 {
4953 SCIPerrorMessage("missing starting character '(' parsing logicor\n");
4954 return SCIP_OKAY;
4955 }
4956
4957 /* skip '(' */
4958 ++startptr;
4959
4960 /* find end character ')' */
4961 endptr = strrchr(startptr, ')');
4962
4963 if( endptr == NULL )
4964 {
4965 SCIPerrorMessage("missing ending character ')' parsing logicor\n");
4966 return SCIP_OKAY;
4967 }
4968 assert(endptr >= startptr);
4969
4970 if( endptr > startptr )
4971 {
4972 /* copy string for parsing; note that isspace() in SCIPparseVarsList() requires that strcopy ends with '\0' */
4973 SCIP_CALL( SCIPduplicateBufferArray(scip, &strcopy, startptr, (int)(endptr-startptr+1)) );
4974 strcopy[endptr-startptr] = '\0';
4975 varssize = 100;
4976 nvars = 0;
4977
4978 /* allocate buffer array for variables */
4979 SCIP_CALL( SCIPallocBufferArray(scip, &vars, varssize) );
4980
4981 /* parse string */
4982 SCIP_CALL( SCIPparseVarsList(scip, strcopy, vars, &nvars, varssize, &requiredsize, &endptr, ',', success) );
4983
4984 if( *success )
4985 {
4986 /* check if the size of the variable array was great enough */
4987 if( varssize < requiredsize )
4988 {
4989 /* reallocate memory */
4990 varssize = requiredsize;
4991 SCIP_CALL( SCIPreallocBufferArray(scip, &vars, varssize) );
4992
4993 /* parse string again with the correct size of the variable array */
4994 SCIP_CALL( SCIPparseVarsList(scip, strcopy, vars, &nvars, varssize, &requiredsize, &endptr, ',', success) );
4995 }
4996
4997 assert(*success);
4998 assert(varssize >= requiredsize);
4999
5000 /* create logicor constraint */
5001 SCIP_CALL( SCIPcreateConsLogicor(scip, cons, name, nvars, vars,
5002 initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode) );
5003 }
5004
5005 /* free buffers */
5006 SCIPfreeBufferArray(scip, &vars);
5007 SCIPfreeBufferArray(scip, &strcopy);
5008 }
5009 else
5010 {
5011 if( !modifiable )
5012 {
5013 SCIPerrorMessage("cannot create empty logicor constraint\n");
5014 return SCIP_OKAY;
5015 }
5016
5017 /* create empty logicor constraint */
5018 SCIP_CALL( SCIPcreateConsLogicor(scip, cons, name, 0, NULL,
5019 initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode) );
5020
5021 *success = TRUE;
5022 }
5023
5024 return SCIP_OKAY;
5025 }
5026
5027 /** constraint method of constraint handler which returns the variables (if possible) */
5028 static
5029 SCIP_DECL_CONSGETVARS(consGetVarsLogicor)
5030 { /*lint --e{715}*/
5031 SCIP_CONSDATA* consdata;
5032
5033 consdata = SCIPconsGetData(cons);
5034 assert(consdata != NULL);
5035
5036 if( varssize < consdata->nvars )
5037 (*success) = FALSE;
5038 else
5039 {
5040 assert(vars != NULL);
5041
5042 BMScopyMemoryArray(vars, consdata->vars, consdata->nvars);
5043 (*success) = TRUE;
5044 }
5045
5046 return SCIP_OKAY;
5047 }
5048
5049 /** constraint method of constraint handler which returns the number of variables (if possible) */
5050 static
5051 SCIP_DECL_CONSGETNVARS(consGetNVarsLogicor)
5052 { /*lint --e{715}*/
5053 SCIP_CONSDATA* consdata;
5054
5055 consdata = SCIPconsGetData(cons);
5056 assert(consdata != NULL);
5057
5058 (*nvars) = consdata->nvars;
5059 (*success) = TRUE;
5060
5061 return SCIP_OKAY;
5062 }
5063
5064 /*
5065 * Callback methods of event handler
5066 */
5067
5068 static
5069 SCIP_DECL_EVENTEXEC(eventExecLogicor)
5070 { /*lint --e{715}*/
5071 assert(eventhdlr != NULL);
5072 assert(eventdata != NULL);
5073 assert(strcmp(SCIPeventhdlrGetName(eventhdlr), EVENTHDLR_NAME) == 0);
5074 assert(event != NULL);
5075
5076 SCIPdebugMsg(scip, "exec method of event handler for logic or constraints\n");
5077
5078 if( SCIPeventGetType(event) == SCIP_EVENTTYPE_LBRELAXED )
5079 {
5080 SCIPdebugMsg(scip, "enabling constraint cons <%s> at depth %d\n", SCIPconsGetName((SCIP_CONS*)eventdata), SCIPgetDepth(scip));
5081
5082 SCIP_CALL( SCIPenableCons(scip, (SCIP_CONS*)eventdata) );
5083 SCIP_CALL( SCIPenableConsPropagation(scip, (SCIP_CONS*)eventdata) );
5084 }
5085 else if( SCIPeventGetType(event) == SCIP_EVENTTYPE_UBTIGHTENED )
5086 {
5087 SCIP_CALL( SCIPenableConsPropagation(scip, (SCIP_CONS*)eventdata) );
5088 }
5089
5090 if( SCIPeventGetType(event) == SCIP_EVENTTYPE_VARFIXED )
5091 {
5092 SCIP_VAR* var = SCIPeventGetVar(event);
5093 SCIP_CONS* cons = (SCIP_CONS*)eventdata;
5094 SCIP_CONSDATA* consdata;
5095
5096 assert(cons != NULL);
5097 consdata = SCIPconsGetData(cons);
5098 assert(consdata != NULL);
5099
5100 /* we only catch this event in presolving stage */
5101 assert(SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING);
5102 assert(var != NULL);
5103
5104 consdata->presolved = FALSE;
5105
5106 if( SCIPvarGetStatus(var) != SCIP_VARSTATUS_FIXED )
5107 {
5108 if( SCIPconsIsActive(cons) )
5109 {
5110 if( SCIPvarGetLbGlobal(var) < 0.5 && SCIPvarGetUbGlobal(var) > 0.5 )
5111 consdata->merged = FALSE;
5112
5113 if( !consdata->existmultaggr )
5114 {
5115 if( SCIPvarGetStatus(SCIPvarGetProbvar(var)) == SCIP_VARSTATUS_MULTAGGR )
5116 consdata->existmultaggr = TRUE;
5117 }
5118 }
5119 }
5120 }
5121
5122 return SCIP_OKAY;
5123 }
5124
5125
5126 /*
5127 * Callback methods of conflict handler
5128 */
5129
5130 /** conflict processing method of conflict handler (called when conflict was found) */
5131 static
5132 SCIP_DECL_CONFLICTEXEC(conflictExecLogicor)
5133 { /*lint --e{715}*/
5134 SCIP_VAR** vars;
5135 int i;
5136
5137 assert(conflicthdlr != NULL);
5138 assert(strcmp(SCIPconflicthdlrGetName(conflicthdlr), CONFLICTHDLR_NAME) == 0);
5139 assert(bdchginfos != NULL || nbdchginfos == 0);
5140 assert(result != NULL);
5141
5142 *result = SCIP_DIDNOTRUN;
5143
5144 /* don't process already resolved conflicts */
5145 if( resolved )
5146 return SCIP_OKAY;
5147
5148 /* if the conflict consists of only two (binary) variables, it will be handled by the setppc conflict handler */
5149 if( nbdchginfos == 2 )
5150 return SCIP_OKAY;
5151
5152 *result = SCIP_DIDNOTFIND;
5153
5154 /* create array of variables in conflict constraint */
5155 SCIP_CALL( SCIPallocBufferArray(scip, &vars, nbdchginfos) );
5156 for( i = 0; i < nbdchginfos; ++i )
5157 {
5158 assert(bdchginfos != NULL); /* for flexelint */
5159 assert(bdchginfos[i] != NULL);
5160
5161 vars[i] = SCIPbdchginfoGetVar(bdchginfos[i]);
5162
5163 /* we can only treat binary variables */
5164 if( !SCIPvarIsBinary(vars[i]) )
5165 break;
5166
5167 /* if the variable is fixed to one in the conflict set, we have to use its negation */
5168 if( SCIPbdchginfoGetNewbound(bdchginfos[i]) > 0.5 )
5169 {
5170 SCIP_CALL( SCIPgetNegatedVar(scip, vars[i], &vars[i]) );
5171 }
5172 }
5173
5174 if( i == nbdchginfos )
5175 {
5176 SCIP_CONS* cons;
5177 char consname[SCIP_MAXSTRLEN];
5178
5179 /* create a constraint out of the conflict set */
5180 (void) SCIPsnprintf(consname, SCIP_MAXSTRLEN, "cf%d_%" SCIP_LONGINT_FORMAT, SCIPgetNRuns(scip), SCIPgetNConflictConssApplied(scip));
5181 SCIP_CALL( SCIPcreateConsLogicor(scip, &cons, consname, nbdchginfos, vars,
5182 FALSE, separate, FALSE, FALSE, TRUE, local, FALSE, dynamic, removable, FALSE) );
5183
5184 /* add conflict to SCIP */
5185 SCIP_CALL( SCIPaddConflict(scip, node, cons, validnode, conftype, cutoffinvolved) );
5186
5187 *result = SCIP_CONSADDED;
5188 }
5189
5190 /* free temporary memory */
5191 SCIPfreeBufferArray(scip, &vars);
5192
5193 return SCIP_OKAY;
5194 }
5195
5196
5197 /*
5198 * constraint specific interface methods
5199 */
5200
5201 /** creates the handler for logic or constraints and includes it in SCIP */
5202 SCIP_RETCODE SCIPincludeConshdlrLogicor(
5203 SCIP* scip /**< SCIP data structure */
5204 )
5205 {
5206 SCIP_CONSHDLRDATA* conshdlrdata;
5207 SCIP_CONSHDLR* conshdlr;
5208 SCIP_CONFLICTHDLR* conflicthdlr;
5209 SCIP_EVENTHDLR* eventhdlr;
5210
5211 /* create event handler for events on watched variables */
5212 SCIP_CALL( SCIPincludeEventhdlrBasic(scip, &eventhdlr, EVENTHDLR_NAME, EVENTHDLR_DESC,
5213 eventExecLogicor, NULL) );
5214
5215 /* create conflict handler for logic or constraints */
5216 SCIP_CALL( SCIPincludeConflicthdlrBasic(scip, &conflicthdlr, CONFLICTHDLR_NAME, CONFLICTHDLR_DESC, CONFLICTHDLR_PRIORITY,
5217 conflictExecLogicor, NULL) );
5218
5219 /* create constraint handler data */
5220 SCIP_CALL( conshdlrdataCreate(scip, &conshdlrdata, eventhdlr) );
5221
5222 /* include constraint handler */
5223 SCIP_CALL( SCIPincludeConshdlrBasic(scip, &conshdlr, CONSHDLR_NAME, CONSHDLR_DESC,
5224 CONSHDLR_ENFOPRIORITY, CONSHDLR_CHECKPRIORITY, CONSHDLR_EAGERFREQ, CONSHDLR_NEEDSCONS,
5225 consEnfolpLogicor, consEnfopsLogicor, consCheckLogicor, consLockLogicor,
5226 conshdlrdata) );
5227 assert(conshdlr != NULL);
5228
5229 /* set non-fundamental callbacks via specific setter functions */
5230 SCIP_CALL( SCIPsetConshdlrActive(scip, conshdlr, consActiveLogicor) );
5231 SCIP_CALL( SCIPsetConshdlrCopy(scip, conshdlr, conshdlrCopyLogicor, consCopyLogicor) );
5232 SCIP_CALL( SCIPsetConshdlrDeactive(scip, conshdlr, consDeactiveLogicor) );
5233 SCIP_CALL( SCIPsetConshdlrDelete(scip, conshdlr, consDeleteLogicor) );
5234 SCIP_CALL( SCIPsetConshdlrExitpre(scip, conshdlr, consExitpreLogicor) );
5235 SCIP_CALL( SCIPsetConshdlrInitsol(scip, conshdlr, consInitsolLogicor) );
5236 SCIP_CALL( SCIPsetConshdlrExitsol(scip, conshdlr, consExitsolLogicor) );
5237 SCIP_CALL( SCIPsetConshdlrFree(scip, conshdlr, consFreeLogicor) );
5238 SCIP_CALL( SCIPsetConshdlrGetVars(scip, conshdlr, consGetVarsLogicor) );
5239 SCIP_CALL( SCIPsetConshdlrGetNVars(scip, conshdlr, consGetNVarsLogicor) );
5240 SCIP_CALL( SCIPsetConshdlrInitpre(scip, conshdlr, consInitpreLogicor) );
5241 SCIP_CALL( SCIPsetConshdlrInitlp(scip, conshdlr, consInitlpLogicor) );
5242 SCIP_CALL( SCIPsetConshdlrParse(scip, conshdlr, consParseLogicor) );
5243 SCIP_CALL( SCIPsetConshdlrPresol(scip, conshdlr, consPresolLogicor,CONSHDLR_MAXPREROUNDS, CONSHDLR_PRESOLTIMING) );
5244 SCIP_CALL( SCIPsetConshdlrPrint(scip, conshdlr, consPrintLogicor) );
5245 SCIP_CALL( SCIPsetConshdlrProp(scip, conshdlr, consPropLogicor, CONSHDLR_PROPFREQ, CONSHDLR_DELAYPROP,
5246 CONSHDLR_PROP_TIMING) );
5247 SCIP_CALL( SCIPsetConshdlrResprop(scip, conshdlr, consRespropLogicor) );
5248 SCIP_CALL( SCIPsetConshdlrSepa(scip, conshdlr, consSepalpLogicor, consSepasolLogicor, CONSHDLR_SEPAFREQ,
5249 CONSHDLR_SEPAPRIORITY, CONSHDLR_DELAYSEPA) );
5250 SCIP_CALL( SCIPsetConshdlrTrans(scip, conshdlr, consTransLogicor) );
5251 SCIP_CALL( SCIPsetConshdlrEnforelax(scip, conshdlr, consEnforelaxLogicor) );
5252
5253 conshdlrdata->conshdlrlinear = SCIPfindConshdlr(scip, "linear");
5254 conshdlrdata->conshdlrsetppc = SCIPfindConshdlr(scip, "setppc");
5255
5256 if( conshdlrdata->conshdlrlinear != NULL )
5257 {
5258 /* include the linear constraint to logicor constraint upgrade in the linear constraint handler */
5259 SCIP_CALL( SCIPincludeLinconsUpgrade(scip, linconsUpgdLogicor, LINCONSUPGD_PRIORITY, CONSHDLR_NAME) );
5260 }
5261
5262 /* logic or constraint handler parameters */
5263 SCIP_CALL( SCIPaddBoolParam(scip,
5264 "constraints/logicor/presolpairwise",
5265 "should pairwise constraint comparison be performed in presolving?",
5266 &conshdlrdata->presolpairwise, TRUE, DEFAULT_PRESOLPAIRWISE, NULL, NULL) );
5267 SCIP_CALL( SCIPaddBoolParam(scip,
5268 "constraints/logicor/presolusehashing",
5269 "should hash table be used for detecting redundant constraints in advance",
5270 &conshdlrdata->presolusehashing, TRUE, DEFAULT_PRESOLUSEHASHING, NULL, NULL) );
5271 SCIP_CALL( SCIPaddBoolParam(scip,
5272 "constraints/logicor/dualpresolving",
5273 "should dual presolving steps be performed?",
5274 &conshdlrdata->dualpresolving, TRUE, DEFAULT_DUALPRESOLVING, NULL, NULL) );
5275 SCIP_CALL( SCIPaddBoolParam(scip,
5276 "constraints/logicor/negatedclique",
5277 "should negated clique information be used in presolving",
5278 &conshdlrdata->usenegatedclique, TRUE, DEFAULT_NEGATEDCLIQUE, NULL, NULL) );
5279 SCIP_CALL( SCIPaddBoolParam(scip,
5280 "constraints/logicor/implications",
5281 "should implications/cliques be used in presolving",
5282 &conshdlrdata->useimplications, TRUE, DEFAULT_IMPLICATIONS, NULL, NULL) );
5283 SCIP_CALL( SCIPaddBoolParam(scip,
5284 "constraints/logicor/strengthen",
5285 "should pairwise constraint comparison try to strengthen constraints by removing superflous non-zeros?",
5286 &conshdlrdata->usestrengthening, TRUE, DEFAULT_STRENGTHEN, NULL, NULL) );
5287
5288 return SCIP_OKAY;
5289 }
5290
5291
5292 /** creates and captures a logic or constraint
5293 *
5294 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
5295 */
5296 SCIP_RETCODE SCIPcreateConsLogicor(
5297 SCIP* scip, /**< SCIP data structure */
5298 SCIP_CONS** cons, /**< pointer to hold the created constraint */
5299 const char* name, /**< name of constraint */
5300 int nvars, /**< number of variables in the constraint */
5301 SCIP_VAR** vars, /**< array with variables of constraint entries */
5302 SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP?
5303 * Usually set to TRUE. Set to FALSE for 'lazy constraints'. */
5304 SCIP_Bool separate, /**< should the constraint be separated during LP processing?
5305 * Usually set to TRUE. */
5306 SCIP_Bool enforce, /**< should the constraint be enforced during node processing?
5307 * TRUE for model constraints, FALSE for additional, redundant constraints. */
5308 SCIP_Bool check, /**< should the constraint be checked for feasibility?
5309 * TRUE for model constraints, FALSE for additional, redundant constraints. */
5310 SCIP_Bool propagate, /**< should the constraint be propagated during node processing?
5311 * Usually set to TRUE. */
5312 SCIP_Bool local, /**< is constraint only valid locally?
5313 * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */
5314 SCIP_Bool modifiable, /**< is constraint modifiable (subject to column generation)?
5315 * Usually set to FALSE. In column generation applications, set to TRUE if pricing
5316 * adds coefficients to this constraint. */
5317 SCIP_Bool dynamic, /**< is constraint subject to aging?
5318 * Usually set to FALSE. Set to TRUE for own cuts which
5319 * are separated as constraints. */
5320 SCIP_Bool removable, /**< should the relaxation be removed from the LP due to aging or cleanup?
5321 * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */
5322 SCIP_Bool stickingatnode /**< should the constraint always be kept at the node where it was added, even
5323 * if it may be moved to a more global node?
5324 * Usually set to FALSE. Set to TRUE to for constraints that represent node data. */
5325 )
5326 {
5327 SCIP_CONSHDLR* conshdlr;
5328 SCIP_CONSDATA* consdata;
5329
5330 assert(scip != NULL);
5331
5332 /* find the logicor constraint handler */
5333 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
5334 if( conshdlr == NULL )
5335 {
5336 SCIPerrorMessage("logic or constraint handler not found\n");
5337 return SCIP_INVALIDCALL;
5338 }
5339
5340 /* create the constraint specific data */
5341 SCIP_CALL( consdataCreate(scip, &consdata, nvars, vars) );
5342
5343 /* create constraint */
5344 SCIP_CALL( SCIPcreateCons(scip, cons, name, conshdlr, consdata, initial, separate, enforce, check, propagate,
5345 local, modifiable, dynamic, removable, stickingatnode) );
5346
5347 if( SCIPisTransformed(scip) && SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING )
5348 {
5349 SCIP_CONSHDLRDATA* conshdlrdata;
5350 int v;
5351
5352 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5353 assert(conshdlrdata != NULL);
5354
5355 for( v = consdata->nvars - 1; v >= 0; --v )
5356 {
5357 SCIP_CALL( SCIPcatchVarEvent(scip, consdata->vars[v], SCIP_EVENTTYPE_VARFIXED, conshdlrdata->eventhdlr,
5358 (SCIP_EVENTDATA*)(*cons), NULL) );
5359 }
5360 }
5361
5362 return SCIP_OKAY;
5363 }
5364
5365 /** creates and captures a logicor constraint
5366 * in its most basic version, i. e., all constraint flags are set to their basic value as explained for the
5367 * method SCIPcreateConsLogicor(); all flags can be set via SCIPsetConsFLAGNAME-methods in scip.h
5368 *
5369 * @see SCIPcreateConsLogicor() for information about the basic constraint flag configuration
5370 *
5371 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
5372 */
5373 SCIP_RETCODE SCIPcreateConsBasicLogicor(
5374 SCIP* scip, /**< SCIP data structure */
5375 SCIP_CONS** cons, /**< pointer to hold the created constraint */
5376 const char* name, /**< name of constraint */
5377 int nvars, /**< number of variables in the constraint */
5378 SCIP_VAR** vars /**< array with variables of constraint entries */
5379 )
5380 {
5381 assert(scip != NULL);
5382
5383 SCIP_CALL( SCIPcreateConsLogicor(scip, cons, name, nvars, vars,
5384 TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
5385
5386 return SCIP_OKAY;
5387 }
5388
5389 /** adds coefficient in logic or constraint */
5390 SCIP_RETCODE SCIPaddCoefLogicor(
5391 SCIP* scip, /**< SCIP data structure */
5392 SCIP_CONS* cons, /**< logicor constraint */
5393 SCIP_VAR* var /**< variable to add to the constraint */
5394 )
5395 {
5396 assert(var != NULL);
5397
5398 /*debugMsg(scip, "adding variable <%s> to logicor constraint <%s>\n",
5399 SCIPvarGetName(var), SCIPconsGetName(cons));*/
5400
5401 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
5402 {
5403 SCIPerrorMessage("constraint is not a logic or constraint\n");
5404 return SCIP_INVALIDDATA;
5405 }
5406
5407 SCIP_CALL( addCoef(scip, cons, var) );
5408
5409 return SCIP_OKAY;
5410 }
5411
5412 /** gets number of variables in logic or constraint */
5413 int SCIPgetNVarsLogicor(
5414 SCIP* scip, /**< SCIP data structure */
5415 SCIP_CONS* cons /**< constraint data */
5416 )
5417 {
5418 SCIP_CONSDATA* consdata;
5419
5420 assert(scip != NULL);
5421
5422 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
5423 {
5424 SCIPerrorMessage("constraint is not a logic or constraint\n");
5425 SCIPABORT();
5426 return -1; /*lint !e527*/
5427 }
5428
5429 consdata = SCIPconsGetData(cons);
5430 assert(consdata != NULL);
5431
5432 return consdata->nvars;
5433 }
5434
5435 /** gets array of variables in logic or constraint */
5436 SCIP_VAR** SCIPgetVarsLogicor(
5437 SCIP* scip, /**< SCIP data structure */
5438 SCIP_CONS* cons /**< constraint data */
5439 )
5440 {
5441 SCIP_CONSDATA* consdata;
5442
5443 assert(scip != NULL);
5444
5445 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
5446 {
5447 SCIPerrorMessage("constraint is not a logic or constraint\n");
5448 SCIPABORT();
5449 return NULL; /*lint !e527*/
5450 }
5451
5452 consdata = SCIPconsGetData(cons);
5453 assert(consdata != NULL);
5454
5455 return consdata->vars;
5456 }
5457
5458 /** gets the dual solution of the logic or constraint in the current LP */
5459 SCIP_Real SCIPgetDualsolLogicor(
5460 SCIP* scip, /**< SCIP data structure */
5461 SCIP_CONS* cons /**< constraint data */
5462 )
5463 {
5464 SCIP_CONSDATA* consdata;
5465
5466 assert(scip != NULL);
5467
5468 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
5469 {
5470 SCIPerrorMessage("constraint is not a logic or constraint\n");
5471 SCIPABORT();
5472 return SCIP_INVALID; /*lint !e527*/
5473 }
5474
5475 consdata = SCIPconsGetData(cons);
5476 assert(consdata != NULL);
5477
5478 if( consdata->row != NULL )
5479 return SCIProwGetDualsol(consdata->row);
5480 else
5481 return 0.0;
5482 }
5483
5484 /** gets the dual Farkas value of the logic or constraint in the current infeasible LP */
5485 SCIP_Real SCIPgetDualfarkasLogicor(
5486 SCIP* scip, /**< SCIP data structure */
5487 SCIP_CONS* cons /**< constraint data */
5488 )
5489 {
5490 SCIP_CONSDATA* consdata;
5491
5492 assert(scip != NULL);
5493
5494 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
5495 {
5496 SCIPerrorMessage("constraint is not a logic or constraint\n");
5497 SCIPABORT();
5498 return SCIP_INVALID; /*lint !e527*/
5499 }
5500
5501 consdata = SCIPconsGetData(cons);
5502 assert(consdata != NULL);
5503
5504 if( consdata->row != NULL )
5505 return SCIProwGetDualfarkas(consdata->row);
5506 else
5507 return 0.0;
5508 }
5509
5510 /** returns the linear relaxation of the given logic or constraint; may return NULL if no LP row was yet created;
5511 * the user must not modify the row!
5512 */
5513 SCIP_ROW* SCIPgetRowLogicor(
5514 SCIP* scip, /**< SCIP data structure */
5515 SCIP_CONS* cons /**< constraint data */
5516 )
5517 {
5518 SCIP_CONSDATA* consdata;
5519
5520 assert(scip != NULL);
5521
5522 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
5523 {
5524 SCIPerrorMessage("constraint is not a logic or constraint\n");
5525 SCIPABORT();
5526 return NULL; /*lint !e527*/
5527 }
5528
5529 consdata = SCIPconsGetData(cons);
5530 assert(consdata != NULL);
5531
5532 return consdata->row;
5533 }
5534
5535 /** cleans up (multi-)aggregations and fixings from logicor constraints */
5536 SCIP_RETCODE SCIPcleanupConssLogicor(
5537 SCIP* scip, /**< SCIP data structure */
5538 SCIP_Bool onlychecked, /**< should only checked constraints be cleaned up? */
5539 int* naddconss, /**< pointer to count number of added (linear) constraints */
5540 int* ndelconss, /**< pointer to count number of deleted (logicor) constraints */
5541 int* nchgcoefs /**< pointer to count number of changed coefficients */
5542 )
5543 {
5544 SCIP_CONSHDLR* conshdlr;
5545 SCIP_EVENTHDLR* eventhdlr;
5546 SCIP_CONS** conss;
5547 unsigned char* entries;
5548 int nconss;
5549 int nentries;
5550 int i;
5551
5552 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
5553 if( conshdlr == NULL )
5554 return SCIP_OKAY;
5555
5556 assert(naddconss != NULL);
5557 assert(ndelconss != NULL);
5558 assert(nchgcoefs != NULL);
5559
5560 eventhdlr = SCIPconshdlrGetData(conshdlr)->eventhdlr;
5561 nconss = onlychecked ? SCIPconshdlrGetNCheckConss(conshdlr) : SCIPconshdlrGetNActiveConss(conshdlr);
5562 conss = onlychecked ? SCIPconshdlrGetCheckConss(conshdlr) : SCIPconshdlrGetConss(conshdlr);
5563
5564 nentries = SCIPgetNVars(scip) - SCIPgetNContVars(scip);
5565 SCIP_CALL( SCIPallocBufferArray(scip, &entries, nentries) );
5566
5567 /* loop backwards since then deleted constraints do not interfere with the loop */
5568 for( i = nconss - 1; i > 0; --i )
5569 {
5570 SCIP_CONS* cons;
5571 SCIP_Bool redundant;
5572
5573 cons = conss[i];
5574 redundant = FALSE;
5575
5576 SCIP_CALL( applyFixings(scip, cons, eventhdlr, &redundant, nchgcoefs, naddconss, ndelconss) );
5577
5578 if( SCIPconsIsDeleted(cons) )
5579 continue;
5580
5581 /* merge constraint */
5582 if( !redundant )
5583 {
5584 SCIP_CALL( mergeMultiples(scip, cons, eventhdlr, &entries, &nentries, &redundant, nchgcoefs) );
5585 }
5586
5587 if( redundant )
5588 {
5589 SCIP_CALL( SCIPdelCons(scip, cons) );
5590 ++(*ndelconss);
5591 }
5592 }
5593
5594 SCIPfreeBufferArray(scip, &entries);
5595
5596 return SCIP_OKAY;
5597 }
5598