1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 2 /* */ 3 /* This file is part of the program and library */ 4 /* SCIP --- Solving Constraint Integer Programs */ 5 /* */ 6 /* Copyright (c) 2002-2023 Zuse Institute Berlin (ZIB) */ 7 /* */ 8 /* Licensed under the Apache License, Version 2.0 (the "License"); */ 9 /* you may not use this file except in compliance with the License. */ 10 /* You may obtain a copy of the License at */ 11 /* */ 12 /* http://www.apache.org/licenses/LICENSE-2.0 */ 13 /* */ 14 /* Unless required by applicable law or agreed to in writing, software */ 15 /* distributed under the License is distributed on an "AS IS" BASIS, */ 16 /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ 17 /* See the License for the specific language governing permissions and */ 18 /* limitations under the License. */ 19 /* */ 20 /* You should have received a copy of the Apache-2.0 license */ 21 /* along with SCIP; see the file LICENSE. If not visit scipopt.org. */ 22 /* */ 23 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 24 25 /**@file cons_or.c 26 * @ingroup DEFPLUGINS_CONS 27 * @brief Constraint handler for "or" constraints, \f$r = x_1 \vee x_2 \vee \dots \vee x_n\f$ 28 * @author Tobias Achterberg 29 * @author Stefan Heinz 30 * @author Michael Winkler 31 * 32 * This constraint handler deals with "or" constraint. These are constraint of the form: 33 * 34 * \f[ 35 * r = x_1 \vee x_2 \vee \dots \vee x_n 36 * \f] 37 * 38 * where \f$x_i\f$ is a binary variable for all \f$i\f$. Hence, \f$r\f$ is also of binary type. The variable \f$r\f$ is 39 * called resultant and the \f$x\f$'s operators. 40 */ 41 42 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/ 43 44 #include "blockmemshell/memory.h" 45 #include "scip/cons_and.h" 46 #include "scip/cons_or.h" 47 #include "scip/pub_cons.h" 48 #include "scip/pub_event.h" 49 #include "scip/pub_lp.h" 50 #include "scip/pub_message.h" 51 #include "scip/pub_misc.h" 52 #include "scip/pub_var.h" 53 #include "scip/scip_conflict.h" 54 #include "scip/scip_cons.h" 55 #include "scip/scip_copy.h" 56 #include "scip/scip_cut.h" 57 #include "scip/scip_event.h" 58 #include "scip/scip_general.h" 59 #include "scip/scip_lp.h" 60 #include "scip/scip_mem.h" 61 #include "scip/scip_message.h" 62 #include "scip/scip_numerics.h" 63 #include "scip/scip_prob.h" 64 #include "scip/scip_probing.h" 65 #include "scip/scip_sol.h" 66 #include "scip/scip_tree.h" 67 #include "scip/scip_var.h" 68 #include "scip/symmetry_graph.h" 69 #include "symmetry/struct_symmetry.h" 70 #include <string.h> 71 72 73 /* constraint handler properties */ 74 #define CONSHDLR_NAME "or" 75 #define CONSHDLR_DESC "constraint handler for or constraints: r = or(x1, ..., xn)" 76 #define CONSHDLR_SEPAPRIORITY +850000 /**< priority of the constraint handler for separation */ 77 #define CONSHDLR_ENFOPRIORITY -850000 /**< priority of the constraint handler for constraint enforcing */ 78 #define CONSHDLR_CHECKPRIORITY -850000 /**< priority of the constraint handler for checking feasibility */ 79 #define CONSHDLR_SEPAFREQ 0 /**< frequency for separating cuts; zero means to separate only in the root node */ 80 #define CONSHDLR_PROPFREQ 1 /**< frequency for propagating domains; zero means only preprocessing propagation */ 81 #define CONSHDLR_EAGERFREQ 100 /**< frequency for using all instead of only the useful constraints in separation, 82 * propagation and enforcement, -1 for no eager evaluations, 0 for first only */ 83 #define CONSHDLR_MAXPREROUNDS -1 /**< maximal number of presolving rounds the constraint handler participates in (-1: no limit) */ 84 #define CONSHDLR_DELAYSEPA FALSE /**< should separation method be delayed, if other separators found cuts? */ 85 #define CONSHDLR_DELAYPROP FALSE /**< should propagation method be delayed, if other propagators found reductions? */ 86 #define CONSHDLR_NEEDSCONS TRUE /**< should the constraint handler be skipped, if no constraints are available? */ 87 88 #define CONSHDLR_PROP_TIMING SCIP_PROPTIMING_BEFORELP /**< propagation timing mask of the constraint handler */ 89 #define CONSHDLR_PRESOLTIMING SCIP_PRESOLTIMING_MEDIUM /**< presolving timing of the constraint handler (fast, medium, or exhaustive) */ 90 91 #define EVENTHDLR_NAME "or" 92 #define EVENTHDLR_DESC "event handler for or constraints" 93 94 95 /* 96 * Data structures 97 */ 98 99 /** constraint data for or constraints */ 100 struct SCIP_ConsData 101 { 102 SCIP_VAR** vars; /**< variables in the or operation */ 103 SCIP_VAR* resvar; /**< resultant variable */ 104 SCIP_ROW** rows; /**< rows for linear relaxation of or constraint */ 105 int nvars; /**< number of variables in or operation */ 106 int varssize; /**< size of vars array */ 107 int rowssize; /**< size of rows array */ 108 int watchedvar1; /**< position of first watched operator variable */ 109 int watchedvar2; /**< position of second watched operator variable */ 110 int filterpos1; /**< event filter position of first watched operator variable */ 111 int filterpos2; /**< event filter position of second watched operator variable */ 112 unsigned int propagated:1; /**< is constraint already preprocessed/propagated? */ 113 unsigned int nofixedone:1; /**< is none of the operator variables fixed to TRUE? */ 114 unsigned int impladded:1; /**< were the implications of the constraint already added? */ 115 unsigned int opimpladded:1; /**< was the implication for 2 operands with fixed resultant added? */ 116 }; 117 118 /** constraint handler data */ 119 struct SCIP_ConshdlrData 120 { 121 SCIP_EVENTHDLR* eventhdlr; /**< event handler for events on watched variables */ 122 }; 123 124 125 /* 126 * Propagation rules 127 */ 128 129 enum Proprule 130 { 131 PROPRULE_1 = 0, /**< v_i = TRUE => r = TRUE */ 132 PROPRULE_2 = 1, /**< r = FALSE => v_i = FALSE for all i */ 133 PROPRULE_3 = 2, /**< v_i = FALSE for all i => r = FALSE */ 134 PROPRULE_4 = 3, /**< r = TRUE, v_i = FALSE for all i except j => v_j = TRUE */ 135 PROPRULE_INVALID = 4 /**< propagation was applied without a specific propagation rule */ 136 }; 137 typedef enum Proprule PROPRULE; 138 139 140 /* 141 * Local methods 142 */ 143 144 /** installs rounding locks for the given variable in the given or constraint */ 145 static 146 SCIP_RETCODE lockRounding( 147 SCIP* scip, /**< SCIP data structure */ 148 SCIP_CONS* cons, /**< or constraint */ 149 SCIP_VAR* var /**< variable of constraint entry */ 150 ) 151 { 152 assert(!SCIPconsIsLockedType(cons, SCIP_LOCKTYPE_CONFLICT)); 153 154 /* rounding in both directions may violate the constraint */ 155 SCIP_CALL( SCIPlockVarCons(scip, var, cons, TRUE, TRUE) ); 156 157 return SCIP_OKAY; 158 } 159 160 /** removes rounding locks for the given variable in the given or constraint */ 161 static 162 SCIP_RETCODE unlockRounding( 163 SCIP* scip, /**< SCIP data structure */ 164 SCIP_CONS* cons, /**< or constraint */ 165 SCIP_VAR* var /**< variable of constraint entry */ 166 ) 167 { 168 assert(!SCIPconsIsLockedType(cons, SCIP_LOCKTYPE_CONFLICT)); 169 170 /* rounding in both directions may violate the constraint */ 171 SCIP_CALL( SCIPunlockVarCons(scip, var, cons, TRUE, TRUE) ); 172 173 return SCIP_OKAY; 174 } 175 176 /** creates constraint handler data */ 177 static 178 SCIP_RETCODE conshdlrdataCreate( 179 SCIP* scip, /**< SCIP data structure */ 180 SCIP_CONSHDLRDATA** conshdlrdata, /**< pointer to store the constraint handler data */ 181 SCIP_EVENTHDLR* eventhdlr /**< event handler */ 182 ) 183 { 184 assert(scip != NULL); 185 assert(conshdlrdata != NULL); 186 assert(eventhdlr != NULL); 187 188 SCIP_CALL( SCIPallocBlockMemory(scip, conshdlrdata) ); 189 190 /* set event handler for catching events on watched variables */ 191 (*conshdlrdata)->eventhdlr = eventhdlr; 192 193 return SCIP_OKAY; 194 } 195 196 /** frees constraint handler data */ 197 static 198 void conshdlrdataFree( 199 SCIP* scip, /**< SCIP data structure */ 200 SCIP_CONSHDLRDATA** conshdlrdata /**< pointer to the constraint handler data */ 201 ) 202 { 203 assert(conshdlrdata != NULL); 204 assert(*conshdlrdata != NULL); 205 206 SCIPfreeBlockMemory(scip, conshdlrdata); 207 } 208 209 /** gets number of LP rows needed for the LP relaxation of the constraint */ 210 static 211 int consdataGetNRows( 212 SCIP_CONSDATA* consdata /**< constraint data */ 213 ) 214 { 215 assert(consdata != NULL); 216 217 return consdata->nvars + 1; 218 } 219 220 /** catches events for the watched variable at given position */ 221 static 222 SCIP_RETCODE consdataCatchWatchedEvents( 223 SCIP* scip, /**< SCIP data structure */ 224 SCIP_CONSDATA* consdata, /**< or constraint data */ 225 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */ 226 int pos, /**< array position of variable to catch bound change events for */ 227 int* filterpos /**< pointer to store position of event filter entry */ 228 ) 229 { 230 assert(consdata != NULL); 231 assert(consdata->vars != NULL); 232 assert(eventhdlr != NULL); 233 assert(0 <= pos && pos < consdata->nvars); 234 assert(filterpos != NULL); 235 236 /* catch tightening events for upper bound and relaxed events for lower bounds on watched variable */ 237 SCIP_CALL( SCIPcatchVarEvent(scip, consdata->vars[pos], SCIP_EVENTTYPE_UBTIGHTENED | SCIP_EVENTTYPE_LBRELAXED, 238 eventhdlr, (SCIP_EVENTDATA*)consdata, filterpos) ); 239 240 return SCIP_OKAY; 241 } 242 243 244 /** drops events for the watched variable at given position */ 245 static 246 SCIP_RETCODE consdataDropWatchedEvents( 247 SCIP* scip, /**< SCIP data structure */ 248 SCIP_CONSDATA* consdata, /**< or constraint data */ 249 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */ 250 int pos, /**< array position of watched variable to drop bound change events for */ 251 int filterpos /**< position of event filter entry */ 252 ) 253 { 254 assert(consdata != NULL); 255 assert(consdata->vars != NULL); 256 assert(eventhdlr != NULL); 257 assert(0 <= pos && pos < consdata->nvars); 258 assert(filterpos >= 0); 259 260 /* drop tightening events for upper bound and relaxed events for lower bounds on watched variable */ 261 SCIP_CALL( SCIPdropVarEvent(scip, consdata->vars[pos], SCIP_EVENTTYPE_UBTIGHTENED | SCIP_EVENTTYPE_LBRELAXED, 262 eventhdlr, (SCIP_EVENTDATA*)consdata, filterpos) ); 263 264 return SCIP_OKAY; 265 } 266 267 /** catches needed events on all variables of constraint, except the special ones for watched variables */ 268 static 269 SCIP_RETCODE consdataCatchEvents( 270 SCIP* scip, /**< SCIP data structure */ 271 SCIP_CONSDATA* consdata, /**< or constraint data */ 272 SCIP_EVENTHDLR* eventhdlr /**< event handler to call for the event processing */ 273 ) 274 { 275 int i; 276 277 assert(consdata != NULL); 278 279 /* catch bound change events for both bounds on resultant variable */ 280 SCIP_CALL( SCIPcatchVarEvent(scip, consdata->resvar, SCIP_EVENTTYPE_BOUNDCHANGED, 281 eventhdlr, (SCIP_EVENTDATA*)consdata, NULL) ); 282 283 /* catch tightening events for lower bound and relaxed events for upper bounds on operator variables */ 284 for( i = 0; i < consdata->nvars; ++i ) 285 { 286 SCIP_CALL( SCIPcatchVarEvent(scip, consdata->vars[i], SCIP_EVENTTYPE_LBTIGHTENED | SCIP_EVENTTYPE_UBRELAXED, 287 eventhdlr, (SCIP_EVENTDATA*)consdata, NULL) ); 288 } 289 290 return SCIP_OKAY; 291 } 292 293 /** drops events on all variables of constraint, except the special ones for watched variables */ 294 static 295 SCIP_RETCODE consdataDropEvents( 296 SCIP* scip, /**< SCIP data structure */ 297 SCIP_CONSDATA* consdata, /**< or constraint data */ 298 SCIP_EVENTHDLR* eventhdlr /**< event handler to call for the event processing */ 299 ) 300 { 301 int i; 302 303 assert(consdata != NULL); 304 305 /* drop bound change events for both bounds on resultant variable */ 306 SCIP_CALL( SCIPdropVarEvent(scip, consdata->resvar, SCIP_EVENTTYPE_BOUNDCHANGED, 307 eventhdlr, (SCIP_EVENTDATA*)consdata, -1) ); 308 309 /* drop tightening events for lower bound and relaxed events for upper bounds on operator variables */ 310 for( i = 0; i < consdata->nvars; ++i ) 311 { 312 SCIP_CALL( SCIPdropVarEvent(scip, consdata->vars[i], SCIP_EVENTTYPE_LBTIGHTENED | SCIP_EVENTTYPE_UBRELAXED, 313 eventhdlr, (SCIP_EVENTDATA*)consdata, -1) ); 314 } 315 316 return SCIP_OKAY; 317 } 318 319 /** stores the given variable numbers as watched variables, and updates the event processing */ 320 static 321 SCIP_RETCODE consdataSwitchWatchedvars( 322 SCIP* scip, /**< SCIP data structure */ 323 SCIP_CONSDATA* consdata, /**< or constraint data */ 324 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */ 325 int watchedvar1, /**< new first watched variable */ 326 int watchedvar2 /**< new second watched variable */ 327 ) 328 { 329 assert(consdata != NULL); 330 assert(watchedvar1 == -1 || watchedvar1 != watchedvar2); 331 assert(watchedvar1 != -1 || watchedvar2 == -1); 332 assert(watchedvar1 == -1 || (0 <= watchedvar1 && watchedvar1 < consdata->nvars)); 333 assert(watchedvar2 == -1 || (0 <= watchedvar2 && watchedvar2 < consdata->nvars)); 334 335 /* if one watched variable is equal to the old other watched variable, just switch positions */ 336 if( watchedvar1 == consdata->watchedvar2 || watchedvar2 == consdata->watchedvar1 ) 337 { 338 int tmp; 339 340 tmp = consdata->watchedvar1; 341 consdata->watchedvar1 = consdata->watchedvar2; 342 consdata->watchedvar2 = tmp; 343 tmp = consdata->filterpos1; 344 consdata->filterpos1 = consdata->filterpos2; 345 consdata->filterpos2 = tmp; 346 } 347 assert(watchedvar1 == -1 || watchedvar1 != consdata->watchedvar2); 348 assert(watchedvar2 == -1 || watchedvar2 != consdata->watchedvar1); 349 350 /* drop events on old watched variables */ 351 if( consdata->watchedvar1 != -1 && consdata->watchedvar1 != watchedvar1 ) 352 { 353 assert(consdata->filterpos1 != -1); 354 SCIP_CALL( consdataDropWatchedEvents(scip, consdata, eventhdlr, consdata->watchedvar1, consdata->filterpos1) ); 355 } 356 if( consdata->watchedvar2 != -1 && consdata->watchedvar2 != watchedvar2 ) 357 { 358 assert(consdata->filterpos2 != -1); 359 SCIP_CALL( consdataDropWatchedEvents(scip, consdata, eventhdlr, consdata->watchedvar2, consdata->filterpos2) ); 360 } 361 362 /* catch events on new watched variables */ 363 if( watchedvar1 != -1 && watchedvar1 != consdata->watchedvar1 ) 364 { 365 SCIP_CALL( consdataCatchWatchedEvents(scip, consdata, eventhdlr, watchedvar1, &consdata->filterpos1) ); 366 } 367 if( watchedvar2 != -1 && watchedvar2 != consdata->watchedvar2 ) 368 { 369 SCIP_CALL( consdataCatchWatchedEvents(scip, consdata, eventhdlr, watchedvar2, &consdata->filterpos2) ); 370 } 371 372 /* set the new watched variables */ 373 consdata->watchedvar1 = watchedvar1; 374 consdata->watchedvar2 = watchedvar2; 375 376 return SCIP_OKAY; 377 } 378 379 /** ensures, that the vars array can store at least num entries */ 380 static 381 SCIP_RETCODE consdataEnsureVarsSize( 382 SCIP* scip, /**< SCIP data structure */ 383 SCIP_CONSDATA* consdata, /**< linear constraint data */ 384 int num /**< minimum number of entries to store */ 385 ) 386 { 387 assert(consdata != NULL); 388 assert(consdata->nvars <= consdata->varssize); 389 390 if( num > consdata->varssize ) 391 { 392 int newsize; 393 394 newsize = SCIPcalcMemGrowSize(scip, num); 395 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->vars, consdata->varssize, newsize) ); 396 consdata->varssize = newsize; 397 } 398 assert(num <= consdata->varssize); 399 400 return SCIP_OKAY; 401 } 402 403 /** creates constraint data for or constraint */ 404 static 405 SCIP_RETCODE consdataCreate( 406 SCIP* scip, /**< SCIP data structure */ 407 SCIP_CONSDATA** consdata, /**< pointer to store the constraint data */ 408 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */ 409 int nvars, /**< number of variables in the or operation */ 410 SCIP_VAR** vars, /**< variables in or operation */ 411 SCIP_VAR* resvar /**< resultant variable */ 412 ) 413 { 414 assert(consdata != NULL); 415 assert(nvars == 0 || vars != NULL); 416 assert(resvar != NULL); 417 418 SCIP_CALL( SCIPallocBlockMemory(scip, consdata) ); 419 SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*consdata)->vars, vars, nvars) ); 420 (*consdata)->resvar = resvar; 421 (*consdata)->rows = NULL; 422 (*consdata)->nvars = nvars; 423 (*consdata)->varssize = nvars; 424 (*consdata)->rowssize = 0; 425 (*consdata)->watchedvar1 = -1; 426 (*consdata)->watchedvar2 = -1; 427 (*consdata)->filterpos1 = -1; 428 (*consdata)->filterpos2 = -1; 429 (*consdata)->propagated = FALSE; 430 (*consdata)->nofixedone = FALSE; 431 (*consdata)->impladded = FALSE; 432 (*consdata)->opimpladded = FALSE; 433 434 /* get transformed variables, if we are in the transformed problem */ 435 if( SCIPisTransformed(scip) ) 436 { 437 SCIP_CALL( SCIPgetTransformedVars(scip, (*consdata)->nvars, (*consdata)->vars, (*consdata)->vars) ); 438 SCIP_CALL( SCIPgetTransformedVar(scip, (*consdata)->resvar, &(*consdata)->resvar) ); 439 440 /* catch needed events on variables */ 441 SCIP_CALL( consdataCatchEvents(scip, *consdata, eventhdlr) ); 442 } 443 444 return SCIP_OKAY; 445 } 446 447 /** releases LP rows of constraint data and frees rows array */ 448 static 449 SCIP_RETCODE consdataFreeRows( 450 SCIP* scip, /**< SCIP data structure */ 451 SCIP_CONSDATA* consdata /**< constraint data */ 452 ) 453 { 454 int r; 455 456 assert(consdata != NULL); 457 458 if( consdata->rows != NULL ) 459 { 460 int nrows; 461 462 nrows = consdataGetNRows(consdata); 463 464 for( r = 0; r < nrows; ++r ) 465 { 466 SCIP_CALL( SCIPreleaseRow(scip, &consdata->rows[r]) ); 467 } 468 SCIPfreeBlockMemoryArray(scip, &consdata->rows, consdata->rowssize); 469 } 470 471 return SCIP_OKAY; 472 } 473 474 /** frees constraint data for or constraint */ 475 static 476 SCIP_RETCODE consdataFree( 477 SCIP* scip, /**< SCIP data structure */ 478 SCIP_CONSDATA** consdata, /**< pointer to the constraint data */ 479 SCIP_EVENTHDLR* eventhdlr /**< event handler to call for the event processing */ 480 ) 481 { 482 assert(consdata != NULL); 483 assert(*consdata != NULL); 484 485 if( SCIPisTransformed(scip) ) 486 { 487 /* drop events for watched variables */ 488 SCIP_CALL( consdataSwitchWatchedvars(scip, *consdata, eventhdlr, -1, -1) ); 489 490 /* drop all other events on variables */ 491 SCIP_CALL( consdataDropEvents(scip, *consdata, eventhdlr) ); 492 } 493 else 494 { 495 assert((*consdata)->watchedvar1 == -1); 496 assert((*consdata)->watchedvar2 == -1); 497 } 498 499 /* release and free the rows */ 500 SCIP_CALL( consdataFreeRows(scip, *consdata) ); 501 502 SCIPfreeBlockMemoryArray(scip, &(*consdata)->vars, (*consdata)->varssize); 503 SCIPfreeBlockMemory(scip, consdata); 504 505 return SCIP_OKAY; 506 } 507 508 /** prints or constraint to file stream */ 509 static 510 SCIP_RETCODE consdataPrint( 511 SCIP* scip, /**< SCIP data structure */ 512 SCIP_CONSDATA* consdata, /**< or constraint data */ 513 FILE* file /**< output file (or NULL for standard output) */ 514 ) 515 { 516 assert(consdata != NULL); 517 518 /* print resultant */ 519 SCIP_CALL( SCIPwriteVarName(scip, file, consdata->resvar, TRUE) ); 520 521 /* start the variable list */ 522 SCIPinfoMessage(scip, file, " == or("); 523 524 /* print variable list */ 525 SCIP_CALL( SCIPwriteVarsList(scip, file, consdata->vars, consdata->nvars, TRUE, ',') ); 526 527 /* close the variable list */ 528 SCIPinfoMessage(scip, file, ")"); 529 530 return SCIP_OKAY; 531 } 532 533 /** adds coefficient in or constraint */ 534 static 535 SCIP_RETCODE addCoef( 536 SCIP* scip, /**< SCIP data structure */ 537 SCIP_CONS* cons, /**< linear constraint */ 538 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */ 539 SCIP_VAR* var /**< variable to add to the constraint */ 540 ) 541 { 542 SCIP_CONSDATA* consdata; 543 SCIP_Bool transformed; 544 545 assert(var != NULL); 546 547 consdata = SCIPconsGetData(cons); 548 assert(consdata != NULL); 549 assert(consdata->rows == NULL); 550 551 /* are we in the transformed problem? */ 552 transformed = SCIPconsIsTransformed(cons); 553 554 /* always use transformed variables in transformed constraints */ 555 if( transformed ) 556 { 557 SCIP_CALL( SCIPgetTransformedVar(scip, var, &var) ); 558 } 559 assert(var != NULL); 560 assert(transformed == SCIPvarIsTransformed(var)); 561 562 SCIP_CALL( consdataEnsureVarsSize(scip, consdata, consdata->nvars+1) ); 563 consdata->vars[consdata->nvars] = var; 564 consdata->nvars++; 565 566 /* if we are in transformed problem, catch the variable's events */ 567 if( transformed ) 568 { 569 /* catch bound change events of variable */ 570 SCIP_CALL( SCIPcatchVarEvent(scip, var, SCIP_EVENTTYPE_LBTIGHTENED | SCIP_EVENTTYPE_UBRELAXED, 571 eventhdlr, (SCIP_EVENTDATA*)consdata, NULL) ); 572 } 573 574 /* install the rounding locks for the new variable */ 575 SCIP_CALL( lockRounding(scip, cons, var) ); 576 577 /**@todo update LP rows */ 578 if( consdata->rows != NULL ) 579 { 580 SCIPerrorMessage("cannot add coefficients to or constraint after LP relaxation was created\n"); 581 return SCIP_INVALIDCALL; 582 } 583 584 return SCIP_OKAY; 585 } 586 587 /** deletes coefficient at given position from or constraint data */ 588 static 589 SCIP_RETCODE delCoefPos( 590 SCIP* scip, /**< SCIP data structure */ 591 SCIP_CONS* cons, /**< or constraint */ 592 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */ 593 int pos /**< position of coefficient to delete */ 594 ) 595 { 596 SCIP_CONSDATA* consdata; 597 598 assert(eventhdlr != NULL); 599 600 consdata = SCIPconsGetData(cons); 601 assert(consdata != NULL); 602 assert(0 <= pos && pos < consdata->nvars); 603 assert(SCIPconsIsTransformed(cons) == SCIPvarIsTransformed(consdata->vars[pos])); 604 605 /* remove the rounding locks of the variable */ 606 SCIP_CALL( unlockRounding(scip, cons, consdata->vars[pos]) ); 607 608 if( SCIPconsIsTransformed(cons) ) 609 { 610 /* drop bound change events of variable */ 611 SCIP_CALL( SCIPdropVarEvent(scip, consdata->vars[pos], SCIP_EVENTTYPE_LBTIGHTENED | SCIP_EVENTTYPE_UBRELAXED, 612 eventhdlr, (SCIP_EVENTDATA*)consdata, -1) ); 613 } 614 615 if( SCIPconsIsTransformed(cons) ) 616 { 617 /* if the position is watched, stop watching the position */ 618 if( consdata->watchedvar1 == pos ) 619 { 620 SCIP_CALL( consdataSwitchWatchedvars(scip, consdata, eventhdlr, consdata->watchedvar2, -1) ); 621 } 622 if( consdata->watchedvar2 == pos ) 623 { 624 SCIP_CALL( consdataSwitchWatchedvars(scip, consdata, eventhdlr, consdata->watchedvar1, -1) ); 625 } 626 } 627 assert(pos != consdata->watchedvar1); 628 assert(pos != consdata->watchedvar2); 629 630 /* move the last variable to the free slot */ 631 consdata->vars[pos] = consdata->vars[consdata->nvars-1]; 632 consdata->nvars--; 633 634 /* if the last variable (that moved) was watched, update the watched position */ 635 if( consdata->watchedvar1 == consdata->nvars ) 636 consdata->watchedvar1 = pos; 637 if( consdata->watchedvar2 == consdata->nvars ) 638 consdata->watchedvar2 = pos; 639 640 consdata->propagated = FALSE; 641 642 return SCIP_OKAY; 643 } 644 645 /** deletes all zero-fixed variables */ 646 static 647 SCIP_RETCODE applyFixings( 648 SCIP* scip, /**< SCIP data structure */ 649 SCIP_CONS* cons, /**< or constraint */ 650 SCIP_EVENTHDLR* eventhdlr /**< event handler to call for the event processing */ 651 ) 652 { 653 SCIP_CONSDATA* consdata; 654 SCIP_VAR* var; 655 int v; 656 657 consdata = SCIPconsGetData(cons); 658 assert(consdata != NULL); 659 assert(consdata->nvars == 0 || consdata->vars != NULL); 660 661 v = 0; 662 while( v < consdata->nvars ) 663 { 664 var = consdata->vars[v]; 665 assert(SCIPvarIsBinary(var)); 666 667 if( SCIPvarGetUbGlobal(var) < 0.5 ) 668 { 669 assert(SCIPisFeasEQ(scip, SCIPvarGetLbGlobal(var), 0.0)); 670 SCIP_CALL( delCoefPos(scip, cons, eventhdlr, v) ); 671 } 672 else 673 { 674 SCIP_VAR* repvar; 675 SCIP_Bool negated; 676 677 /* get binary representative of variable */ 678 SCIP_CALL( SCIPgetBinvarRepresentative(scip, var, &repvar, &negated) ); 679 680 /* check, if the variable should be replaced with the representative */ 681 if( repvar != var ) 682 { 683 /* delete old (aggregated) variable */ 684 SCIP_CALL( delCoefPos(scip, cons, eventhdlr, v) ); 685 686 /* add representative instead */ 687 SCIP_CALL( addCoef(scip, cons, eventhdlr, repvar) ); 688 } 689 else 690 ++v; 691 } 692 } 693 694 SCIPdebugMsg(scip, "after fixings: "); 695 SCIPdebug( SCIP_CALL(consdataPrint(scip, consdata, NULL)) ); 696 SCIPdebugMsgPrint(scip, "\n"); 697 698 return SCIP_OKAY; 699 } 700 701 /** creates LP rows corresponding to or constraint: 702 * - for each operator variable vi: resvar - vi >= 0 703 * - one additional row: resvar - v1 - ... - vn <= 0 704 */ 705 static 706 SCIP_RETCODE createRelaxation( 707 SCIP* scip, /**< SCIP data structure */ 708 SCIP_CONS* cons /**< constraint to check */ 709 ) 710 { 711 SCIP_CONSDATA* consdata; 712 char rowname[SCIP_MAXSTRLEN]; 713 int nvars; 714 int i; 715 716 consdata = SCIPconsGetData(cons); 717 assert(consdata != NULL); 718 assert(consdata->rows == NULL); 719 720 nvars = consdata->nvars; 721 722 /* get memory for rows */ 723 consdata->rowssize = consdataGetNRows(consdata); 724 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->rows, consdata->rowssize) ); 725 assert(consdata->rowssize == nvars+1); 726 727 /* create operator rows */ 728 for( i = 0; i < nvars; ++i ) 729 { 730 (void) SCIPsnprintf(rowname, SCIP_MAXSTRLEN, "%s_%d", SCIPconsGetName(cons), i); 731 SCIP_CALL( SCIPcreateEmptyRowCons(scip, &consdata->rows[i], cons, rowname, 0.0, SCIPinfinity(scip), 732 SCIPconsIsLocal(cons), SCIPconsIsModifiable(cons), SCIPconsIsRemovable(cons)) ); 733 SCIP_CALL( SCIPaddVarToRow(scip, consdata->rows[i], consdata->resvar, 1.0) ); 734 SCIP_CALL( SCIPaddVarToRow(scip, consdata->rows[i], consdata->vars[i], -1.0) ); 735 } 736 737 /* create additional row */ 738 (void) SCIPsnprintf(rowname, SCIP_MAXSTRLEN, "%s_add", SCIPconsGetName(cons)); 739 SCIP_CALL( SCIPcreateEmptyRowCons(scip, &consdata->rows[nvars], cons, rowname, -SCIPinfinity(scip), 0.0, 740 SCIPconsIsLocal(cons), SCIPconsIsModifiable(cons), SCIPconsIsRemovable(cons)) ); 741 SCIP_CALL( SCIPaddVarToRow(scip, consdata->rows[nvars], consdata->resvar, 1.0) ); 742 SCIP_CALL( SCIPaddVarsToRowSameCoef(scip, consdata->rows[nvars], nvars, consdata->vars, -1.0) ); 743 744 return SCIP_OKAY; 745 } 746 747 /** adds linear relaxation of or constraint to the LP */ 748 static 749 SCIP_RETCODE addRelaxation( 750 SCIP* scip, /**< SCIP data structure */ 751 SCIP_CONS* cons, /**< constraint to check */ 752 SCIP_Bool* infeasible /**< pointer to store whether an infeasibility was detected */ 753 ) 754 { 755 SCIP_CONSDATA* consdata; 756 int r; 757 int nrows; 758 759 consdata = SCIPconsGetData(cons); 760 assert(consdata != NULL); 761 762 if( consdata->rows == NULL ) 763 { 764 SCIP_CALL( createRelaxation(scip, cons) ); 765 } 766 assert( consdata->rows != NULL ); 767 768 nrows = consdataGetNRows(consdata); 769 770 for( r = 0; r < nrows && !(*infeasible); ++r ) 771 { 772 if( !SCIProwIsInLP(consdata->rows[r]) ) 773 { 774 SCIP_CALL( SCIPaddRow(scip, consdata->rows[r], FALSE, infeasible) ); 775 } 776 } 777 778 return SCIP_OKAY; 779 } 780 781 /** checks or constraint for feasibility of given solution: returns TRUE iff constraint is feasible */ 782 static 783 SCIP_RETCODE checkCons( 784 SCIP* scip, /**< SCIP data structure */ 785 SCIP_CONS* cons, /**< constraint to check */ 786 SCIP_SOL* sol, /**< solution to check, NULL for current solution */ 787 SCIP_Bool checklprows, /**< Do constraints represented by rows in the current LP have to be checked? */ 788 SCIP_Bool printreason, /**< Should the reason for the violation be printed? */ 789 SCIP_Bool* violated /**< pointer to store whether the constraint is violated */ 790 ) 791 { 792 SCIP_CONSDATA* consdata; 793 SCIP_Bool mustcheck; 794 int r; 795 796 assert(violated != NULL); 797 798 consdata = SCIPconsGetData(cons); 799 assert(consdata != NULL); 800 801 *violated = FALSE; 802 803 /* check, if we can skip this feasibility check, because all rows are in the LP and doesn't have to be checked */ 804 mustcheck = checklprows; 805 mustcheck = mustcheck || (consdata->rows == NULL); 806 if( !mustcheck ) 807 { 808 int nrows; 809 810 assert(consdata->rows != NULL); 811 812 nrows = consdataGetNRows(consdata); 813 814 for( r = 0; r < nrows; ++r ) 815 { 816 mustcheck = !SCIProwIsInLP(consdata->rows[r]); 817 if( mustcheck ) 818 break; 819 } 820 } 821 822 /* check feasibility of constraint if necessary */ 823 if( mustcheck ) 824 { 825 SCIP_Real solval; 826 SCIP_Real maxsolval; 827 SCIP_Real sumsolval; 828 SCIP_Real viol; 829 int maxsolind; 830 int i; 831 832 /* increase age of constraint; age is reset to zero, if a violation was found only in case we are in 833 * enforcement 834 */ 835 if( sol == NULL ) 836 { 837 SCIP_CALL( SCIPincConsAge(scip, cons) ); 838 } 839 840 maxsolind = 0; 841 maxsolval = 0.0; 842 sumsolval = 0.0; 843 844 /* evaluate operator variables */ 845 for( i = 0; i < consdata->nvars; ++i ) 846 { 847 solval = SCIPgetSolVal(scip, sol, consdata->vars[i]); 848 849 if( solval > maxsolval ) 850 { 851 maxsolind = i; 852 maxsolval = solval; 853 } 854 855 sumsolval += solval; 856 } 857 858 /* the resultant must be at least as large as every operator 859 * and at most as large as the sum of operators 860 */ 861 solval = SCIPgetSolVal(scip, sol, consdata->resvar); 862 viol = MAX3(0.0, maxsolval - solval, solval - sumsolval); 863 864 if( SCIPisFeasPositive(scip, viol) ) 865 { 866 *violated = TRUE; 867 868 /* only reset constraint age if we are in enforcement */ 869 if( sol == NULL ) 870 { 871 SCIP_CALL( SCIPresetConsAge(scip, cons) ); 872 } 873 874 if( printreason ) 875 { 876 SCIP_CALL( SCIPprintCons(scip, cons, NULL) ); 877 SCIPinfoMessage(scip, NULL, ";\n"); 878 SCIPinfoMessage(scip, NULL, "violation:"); 879 880 if( SCIPisFeasPositive(scip, maxsolval - solval) ) 881 { 882 SCIPinfoMessage(scip, NULL, " operand <%s> = TRUE and resultant <%s> = FALSE\n", 883 SCIPvarGetName(consdata->vars[maxsolind]), SCIPvarGetName(consdata->resvar)); 884 } 885 else 886 { 887 SCIPinfoMessage(scip, NULL, " all operands are FALSE and resultant <%s> = TRUE\n", 888 SCIPvarGetName(consdata->resvar)); 889 } 890 } 891 } 892 893 if( sol != NULL ) 894 SCIPupdateSolConsViolation(scip, sol, viol, viol); 895 } 896 897 return SCIP_OKAY; 898 } 899 900 /** separates current LP solution */ 901 static 902 SCIP_RETCODE separateCons( 903 SCIP* scip, /**< SCIP data structure */ 904 SCIP_CONS* cons, /**< constraint to check */ 905 SCIP_SOL* sol, /**< primal CIP solution, NULL for current LP solution */ 906 SCIP_Bool* separated /**< pointer to store whether a cut was found */ 907 ) 908 { 909 SCIP_CONSDATA* consdata; 910 SCIP_Real feasibility; 911 int r; 912 int nrows; 913 914 assert(separated != NULL); 915 916 consdata = SCIPconsGetData(cons); 917 assert(consdata != NULL); 918 919 *separated = FALSE; 920 921 /* create all necessary rows for the linear relaxation */ 922 if( consdata->rows == NULL ) 923 { 924 SCIP_CALL( createRelaxation(scip, cons) ); 925 } 926 assert(consdata->rows != NULL); 927 928 nrows = consdataGetNRows(consdata); 929 930 /* test all rows for feasibility and add infeasible rows */ 931 for( r = 0; r < nrows; ++r ) 932 { 933 if( !SCIProwIsInLP(consdata->rows[r]) ) 934 { 935 feasibility = SCIPgetRowSolFeasibility(scip, consdata->rows[r], sol); 936 if( SCIPisFeasNegative(scip, feasibility) ) 937 { 938 SCIP_Bool infeasible; 939 940 SCIP_CALL( SCIPaddRow(scip, consdata->rows[r], FALSE, &infeasible) ); 941 assert( ! infeasible ); 942 *separated = TRUE; 943 } 944 } 945 } 946 947 return SCIP_OKAY; 948 } 949 950 /** analyzes conflicting FALSE assignment to resultant of given constraint, and adds conflict constraint to problem */ 951 static 952 SCIP_RETCODE analyzeConflictZero( 953 SCIP* scip, /**< SCIP data structure */ 954 SCIP_CONS* cons, /**< or constraint that detected the conflict */ 955 int truepos /**< position of operand that is fixed to TRUE */ 956 ) 957 { 958 SCIP_CONSDATA* consdata; 959 960 /* conflict analysis can only be applied in solving stage and if it is applicable */ 961 if( (SCIPgetStage(scip) != SCIP_STAGE_SOLVING && !SCIPinProbing(scip)) || !SCIPisConflictAnalysisApplicable(scip) ) 962 return SCIP_OKAY; 963 964 consdata = SCIPconsGetData(cons); 965 assert(consdata != NULL); 966 assert(SCIPvarGetUbLocal(consdata->resvar) < 0.5); 967 assert(0 <= truepos && truepos < consdata->nvars); 968 assert(SCIPvarGetLbLocal(consdata->vars[truepos]) > 0.5); 969 970 /* initialize conflict analysis, and add resultant and single operand variable to conflict candidate queue */ 971 SCIP_CALL( SCIPinitConflictAnalysis(scip, SCIP_CONFTYPE_PROPAGATION, FALSE) ); 972 973 SCIP_CALL( SCIPaddConflictBinvar(scip, consdata->resvar) ); 974 SCIP_CALL( SCIPaddConflictBinvar(scip, consdata->vars[truepos]) ); 975 976 /* analyze the conflict */ 977 SCIP_CALL( SCIPanalyzeConflictCons(scip, cons, NULL) ); 978 979 return SCIP_OKAY; 980 } 981 982 /** analyzes conflicting TRUE assignment to resultant of given constraint, and adds conflict constraint to problem */ 983 static 984 SCIP_RETCODE analyzeConflictOne( 985 SCIP* scip, /**< SCIP data structure */ 986 SCIP_CONS* cons /**< or constraint that detected the conflict */ 987 ) 988 { 989 SCIP_CONSDATA* consdata; 990 int v; 991 992 assert(!SCIPconsIsModifiable(cons)); 993 994 /* conflict analysis can only be applied in solving stage and if it is applicable */ 995 if( (SCIPgetStage(scip) != SCIP_STAGE_SOLVING && !SCIPinProbing(scip)) || !SCIPisConflictAnalysisApplicable(scip) ) 996 return SCIP_OKAY; 997 998 consdata = SCIPconsGetData(cons); 999 assert(consdata != NULL); 1000 assert(SCIPvarGetLbLocal(consdata->resvar) > 0.5); 1001 1002 /* initialize conflict analysis, and add all variables of infeasible constraint to conflict candidate queue */ 1003 SCIP_CALL( SCIPinitConflictAnalysis(scip, SCIP_CONFTYPE_PROPAGATION, FALSE) ); 1004 1005 SCIP_CALL( SCIPaddConflictBinvar(scip, consdata->resvar) ); 1006 for( v = 0; v < consdata->nvars; ++v ) 1007 { 1008 assert(SCIPvarGetUbLocal(consdata->vars[v]) < 0.5); 1009 SCIP_CALL( SCIPaddConflictBinvar(scip, consdata->vars[v]) ); 1010 } 1011 1012 /* analyze the conflict */ 1013 SCIP_CALL( SCIPanalyzeConflictCons(scip, cons, NULL) ); 1014 1015 return SCIP_OKAY; 1016 } 1017 1018 /** propagates constraint with the following rules: 1019 * (1) v_i = TRUE => r = TRUE 1020 * (2) r = FALSE => v_i = FALSE for all i 1021 * (3) v_i = FALSE for all i => r = FALSE 1022 * (4) r = TRUE, v_i = FALSE for all i except j => v_j = TRUE 1023 */ 1024 static 1025 SCIP_RETCODE propagateCons( 1026 SCIP* scip, /**< SCIP data structure */ 1027 SCIP_CONS* cons, /**< or constraint to be processed */ 1028 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */ 1029 SCIP_Bool* cutoff, /**< pointer to store TRUE, if the node can be cut off */ 1030 int* nfixedvars /**< pointer to add up the number of found domain reductions */ 1031 ) 1032 { 1033 SCIP_CONSDATA* consdata; 1034 SCIP_VAR* resvar; 1035 SCIP_VAR** vars; 1036 int nvars; 1037 int watchedvar1; 1038 int watchedvar2; 1039 int i; 1040 SCIP_Bool infeasible; 1041 SCIP_Bool tightened; 1042 1043 assert(cutoff != NULL); 1044 assert(nfixedvars != NULL); 1045 1046 consdata = SCIPconsGetData(cons); 1047 assert(consdata != NULL); 1048 1049 resvar = consdata->resvar; 1050 vars = consdata->vars; 1051 nvars = consdata->nvars; 1052 1053 /* don't process the constraint, if none of the operator variables was fixed to TRUE, and if the watched variables 1054 * and the resultant weren't fixed to any value since last propagation call 1055 */ 1056 if( consdata->propagated ) 1057 { 1058 assert(consdata->nofixedone); 1059 assert(SCIPisFeasEQ(scip, SCIPvarGetUbLocal(resvar), 1.0)); 1060 return SCIP_OKAY; 1061 } 1062 1063 /* increase age of constraint; age is reset to zero, if a conflict or a propagation was found */ 1064 if( !SCIPinRepropagation(scip) ) 1065 { 1066 SCIP_CALL( SCIPincConsAge(scip, cons) ); 1067 } 1068 1069 /* if one of the operator variables was fixed to TRUE, the resultant can be fixed to TRUE (rule (1)) */ 1070 if( !consdata->nofixedone ) 1071 { 1072 for( i = 0; i < nvars && SCIPvarGetLbLocal(vars[i]) < 0.5; ++i ) /* search fixed operator */ 1073 {} 1074 if( i < nvars ) 1075 { 1076 SCIPdebugMsg(scip, "constraint <%s>: operator var <%s> fixed to 1.0 -> fix resultant <%s> to 1.0\n", 1077 SCIPconsGetName(cons), SCIPvarGetName(vars[i]), SCIPvarGetName(resvar)); 1078 SCIP_CALL( SCIPinferBinvarCons(scip, resvar, TRUE, cons, (int)PROPRULE_1, &infeasible, &tightened) ); 1079 if( infeasible ) 1080 { 1081 /* use conflict analysis to get a conflict constraint out of the conflicting assignment */ 1082 SCIP_CALL( analyzeConflictZero(scip, cons, i) ); 1083 SCIP_CALL( SCIPresetConsAge(scip, cons) ); 1084 *cutoff = TRUE; 1085 } 1086 else 1087 { 1088 SCIP_CALL( SCIPdelConsLocal(scip, cons) ); 1089 if( tightened ) 1090 { 1091 SCIP_CALL( SCIPresetConsAge(scip, cons) ); 1092 (*nfixedvars)++; 1093 } 1094 } 1095 1096 return SCIP_OKAY; 1097 } 1098 else 1099 consdata->nofixedone = TRUE; 1100 } 1101 assert(consdata->nofixedone); 1102 1103 /* if resultant is fixed to FALSE, all operator variables can be fixed to FALSE (rule (2)) */ 1104 if( SCIPvarGetUbLocal(resvar) < 0.5 ) 1105 { 1106 for( i = 0; i < nvars && !(*cutoff); ++i ) 1107 { 1108 SCIPdebugMsg(scip, "constraint <%s>: resultant var <%s> fixed to 0.0 -> fix operator var <%s> to 0.0\n", 1109 SCIPconsGetName(cons), SCIPvarGetName(resvar), SCIPvarGetName(vars[i])); 1110 SCIP_CALL( SCIPinferBinvarCons(scip, vars[i], FALSE, cons, (int)PROPRULE_2, &infeasible, &tightened) ); 1111 if( infeasible ) 1112 { 1113 /* use conflict analysis to get a conflict constraint out of the conflicting assignment */ 1114 SCIP_CALL( analyzeConflictZero(scip, cons, i) ); 1115 SCIP_CALL( SCIPresetConsAge(scip, cons) ); 1116 *cutoff = TRUE; 1117 } 1118 else if( tightened ) 1119 { 1120 SCIP_CALL( SCIPresetConsAge(scip, cons) ); 1121 (*nfixedvars)++; 1122 } 1123 } 1124 1125 if( !(*cutoff) ) 1126 { 1127 SCIP_CALL( SCIPdelConsLocal(scip, cons) ); 1128 } 1129 1130 return SCIP_OKAY; 1131 } 1132 1133 /* rules (3) and (4) can only be applied, if we know all operator variables */ 1134 if( SCIPconsIsModifiable(cons) ) 1135 return SCIP_OKAY; 1136 1137 /* rules (3) and (4) cannot be applied, if we have at least two unfixed variables left; 1138 * that means, we only have to watch (i.e. capture events) of two variables, and switch to other variables 1139 * if these ones get fixed 1140 */ 1141 watchedvar1 = consdata->watchedvar1; 1142 watchedvar2 = consdata->watchedvar2; 1143 1144 /* check, if watched variables are still unfixed */ 1145 if( watchedvar1 != -1 ) 1146 { 1147 assert(SCIPvarGetLbLocal(vars[watchedvar1]) < 0.5); /* otherwise, rule (1) could be applied */ 1148 if( SCIPvarGetUbLocal(vars[watchedvar1]) < 0.5 ) 1149 watchedvar1 = -1; 1150 } 1151 if( watchedvar2 != -1 ) 1152 { 1153 assert(SCIPvarGetLbLocal(vars[watchedvar2]) < 0.5); /* otherwise, rule (1) could be applied */ 1154 if( SCIPvarGetUbLocal(vars[watchedvar2]) < 0.5 ) 1155 watchedvar2 = -1; 1156 } 1157 1158 /* if only one watched variable is still unfixed, make it the first one */ 1159 if( watchedvar1 == -1 ) 1160 { 1161 watchedvar1 = watchedvar2; 1162 watchedvar2 = -1; 1163 } 1164 assert(watchedvar1 != -1 || watchedvar2 == -1); 1165 1166 /* if the watched variables are invalid (fixed), find new ones if existing */ 1167 if( watchedvar2 == -1 ) 1168 { 1169 for( i = 0; i < nvars; ++i ) 1170 { 1171 assert(SCIPvarGetLbLocal(vars[i]) < 0.5); /* otherwise, rule (1) could be applied */ 1172 if( SCIPvarGetUbLocal(vars[i]) > 0.5 ) 1173 { 1174 if( watchedvar1 == -1 ) 1175 { 1176 assert(watchedvar2 == -1); 1177 watchedvar1 = i; 1178 } 1179 else if( watchedvar1 != i ) 1180 { 1181 watchedvar2 = i; 1182 break; 1183 } 1184 } 1185 } 1186 } 1187 assert(watchedvar1 != -1 || watchedvar2 == -1); 1188 1189 /* if all variables are fixed to FALSE, the resultant can also be fixed to FALSE (rule (3)) */ 1190 if( watchedvar1 == -1 ) 1191 { 1192 assert(watchedvar2 == -1); 1193 1194 SCIPdebugMsg(scip, "constraint <%s>: all operator vars fixed to 0.0 -> fix resultant <%s> to 0.0\n", 1195 SCIPconsGetName(cons), SCIPvarGetName(resvar)); 1196 SCIP_CALL( SCIPinferBinvarCons(scip, resvar, FALSE, cons, (int)PROPRULE_3, &infeasible, &tightened) ); 1197 if( infeasible ) 1198 { 1199 /* use conflict analysis to get a conflict constraint out of the conflicting assignment */ 1200 SCIP_CALL( analyzeConflictOne(scip, cons) ); 1201 SCIP_CALL( SCIPresetConsAge(scip, cons) ); 1202 *cutoff = TRUE; 1203 } 1204 else 1205 { 1206 SCIP_CALL( SCIPdelConsLocal(scip, cons) ); 1207 if( tightened ) 1208 { 1209 SCIP_CALL( SCIPresetConsAge(scip, cons) ); 1210 (*nfixedvars)++; 1211 } 1212 } 1213 1214 return SCIP_OKAY; 1215 } 1216 1217 /* if resultant is fixed to TRUE, and only one operator variable is not fixed to FALSE, this operator variable 1218 * can be fixed to TRUE (rule (4)) 1219 */ 1220 if( SCIPvarGetLbLocal(resvar) > 0.5 && watchedvar2 == -1 ) 1221 { 1222 assert(watchedvar1 != -1); 1223 1224 SCIPdebugMsg(scip, "constraint <%s>: resultant <%s> fixed to 1.0, only one unfixed operand -> fix operand <%s> to 1.0\n", 1225 SCIPconsGetName(cons), SCIPvarGetName(resvar), SCIPvarGetName(vars[watchedvar1])); 1226 SCIP_CALL( SCIPinferBinvarCons(scip, vars[watchedvar1], TRUE, cons, (int)PROPRULE_4, &infeasible, &tightened) ); 1227 if( infeasible ) 1228 { 1229 /* use conflict analysis to get a conflict constraint out of the conflicting assignment */ 1230 SCIP_CALL( analyzeConflictOne(scip, cons) ); 1231 SCIP_CALL( SCIPresetConsAge(scip, cons) ); 1232 *cutoff = TRUE; 1233 } 1234 else 1235 { 1236 SCIP_CALL( SCIPdelConsLocal(scip, cons) ); 1237 if( tightened ) 1238 { 1239 SCIP_CALL( SCIPresetConsAge(scip, cons) ); 1240 (*nfixedvars)++; 1241 } 1242 } 1243 1244 return SCIP_OKAY; 1245 } 1246 1247 /* switch to the new watched variables */ 1248 SCIP_CALL( consdataSwitchWatchedvars(scip, consdata, eventhdlr, watchedvar1, watchedvar2) ); 1249 1250 /* mark the constraint propagated */ 1251 consdata->propagated = TRUE; 1252 1253 return SCIP_OKAY; 1254 } 1255 1256 /** resolves a conflict on the given variable by supplying the variables needed for applying the corresponding 1257 * propagation rule (see propagateCons()): 1258 * (1) v_i = TRUE => r = TRUE 1259 * (2) r = FALSE => v_i = FALSE for all i 1260 * (3) v_i = FALSE for all i => r = FALSE 1261 * (4) r = TRUE, v_i = FALSE for all i except j => v_j = TRUE 1262 */ 1263 static 1264 SCIP_RETCODE resolvePropagation( 1265 SCIP* scip, /**< SCIP data structure */ 1266 SCIP_CONS* cons, /**< constraint that inferred the bound change */ 1267 SCIP_VAR* infervar, /**< variable that was deduced */ 1268 PROPRULE proprule, /**< propagation rule that deduced the value */ 1269 SCIP_BDCHGIDX* bdchgidx, /**< bound change index (time stamp of bound change), or NULL for current time */ 1270 SCIP_RESULT* result /**< pointer to store the result of the propagation conflict resolving call */ 1271 ) 1272 { 1273 SCIP_CONSDATA* consdata; 1274 SCIP_VAR** vars; 1275 int nvars; 1276 int i; 1277 1278 assert(result != NULL); 1279 1280 consdata = SCIPconsGetData(cons); 1281 assert(consdata != NULL); 1282 vars = consdata->vars; 1283 nvars = consdata->nvars; 1284 1285 switch( proprule ) 1286 { 1287 case PROPRULE_1: 1288 /* the resultant was inferred to TRUE, because one operand variable was TRUE */ 1289 assert(SCIPgetVarLbAtIndex(scip, infervar, bdchgidx, TRUE) > 0.5); 1290 assert(infervar == consdata->resvar); 1291 for( i = 0; i < nvars; ++i ) 1292 { 1293 if( SCIPgetVarLbAtIndex(scip, vars[i], bdchgidx, FALSE) > 0.5 ) 1294 { 1295 SCIP_CALL( SCIPaddConflictBinvar(scip, vars[i]) ); 1296 break; 1297 } 1298 } 1299 assert(i < nvars); 1300 *result = SCIP_SUCCESS; 1301 break; 1302 1303 case PROPRULE_2: 1304 /* the operand variable was inferred to FALSE, because the resultant was FALSE */ 1305 assert(SCIPgetVarUbAtIndex(scip, infervar, bdchgidx, TRUE) < 0.5); 1306 assert(SCIPgetVarUbAtIndex(scip, consdata->resvar, bdchgidx, FALSE) < 0.5); 1307 SCIP_CALL( SCIPaddConflictBinvar(scip, consdata->resvar) ); 1308 *result = SCIP_SUCCESS; 1309 break; 1310 1311 case PROPRULE_3: 1312 /* the resultant was inferred to FALSE, because all operand variables were FALSE */ 1313 assert(SCIPgetVarUbAtIndex(scip, infervar, bdchgidx, TRUE) < 0.5); 1314 assert(infervar == consdata->resvar); 1315 for( i = 0; i < nvars; ++i ) 1316 { 1317 assert(SCIPgetVarUbAtIndex(scip, vars[i], bdchgidx, FALSE) < 0.5); 1318 SCIP_CALL( SCIPaddConflictBinvar(scip, vars[i]) ); 1319 } 1320 *result = SCIP_SUCCESS; 1321 break; 1322 1323 case PROPRULE_4: 1324 /* the operand variable was inferred to TRUE, because the resultant was TRUE and all other operands were FALSE */ 1325 assert(SCIPgetVarLbAtIndex(scip, infervar, bdchgidx, TRUE) > 0.5); 1326 assert(SCIPgetVarLbAtIndex(scip, consdata->resvar, bdchgidx, FALSE) > 0.5); 1327 SCIP_CALL( SCIPaddConflictBinvar(scip, consdata->resvar) ); 1328 for( i = 0; i < nvars; ++i ) 1329 { 1330 if( vars[i] != infervar ) 1331 { 1332 assert(SCIPgetVarUbAtIndex(scip, vars[i], bdchgidx, FALSE) < 0.5); 1333 SCIP_CALL( SCIPaddConflictBinvar(scip, vars[i]) ); 1334 } 1335 } 1336 *result = SCIP_SUCCESS; 1337 break; 1338 1339 case PROPRULE_INVALID: 1340 default: 1341 SCIPerrorMessage("invalid inference information %d in or constraint <%s>\n", proprule, SCIPconsGetName(cons)); 1342 return SCIP_INVALIDDATA; 1343 } 1344 1345 return SCIP_OKAY; 1346 } 1347 1348 /** upgrades unmodifiable or constraint into an and constraint on negated variables */ 1349 static 1350 SCIP_RETCODE upgradeCons( 1351 SCIP* scip, /**< SCIP data structure */ 1352 SCIP_CONS* cons, /**< constraint that inferred the bound change */ 1353 int* nupgdconss /**< pointer to count the number of constraint upgrades */ 1354 ) 1355 { 1356 SCIP_CONSDATA* consdata; 1357 SCIP_VAR** negvars; 1358 SCIP_VAR* negresvar; 1359 SCIP_CONS* andcons; 1360 int i; 1361 1362 assert(nupgdconss != NULL); 1363 1364 /* we cannot upgrade a modifiable constraint, since we don't know what additional variables to expect */ 1365 if( SCIPconsIsModifiable(cons) ) 1366 return SCIP_OKAY; 1367 1368 SCIPdebugMsg(scip, "upgrading or constraint <%s> into equivalent and constraint on negated variables\n", 1369 SCIPconsGetName(cons)); 1370 1371 consdata = SCIPconsGetData(cons); 1372 assert(consdata != NULL); 1373 1374 /* get the negated versions of the variables */ 1375 SCIP_CALL( SCIPallocBufferArray(scip, &negvars, consdata->nvars) ); 1376 for( i = 0; i < consdata->nvars; ++i ) 1377 { 1378 SCIP_CALL( SCIPgetNegatedVar(scip, consdata->vars[i], &negvars[i]) ); 1379 } 1380 SCIP_CALL( SCIPgetNegatedVar(scip, consdata->resvar, &negresvar) ); 1381 1382 /* create and add the and constraint */ 1383 SCIP_CALL( SCIPcreateConsAnd(scip, &andcons, SCIPconsGetName(cons), negresvar, consdata->nvars, negvars, 1384 SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), SCIPconsIsEnforced(cons), SCIPconsIsChecked(cons), 1385 SCIPconsIsPropagated(cons), SCIPconsIsLocal(cons), SCIPconsIsModifiable(cons), 1386 SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons), SCIPconsIsStickingAtNode(cons)) ); 1387 SCIP_CALL( SCIPaddCons(scip, andcons) ); 1388 SCIP_CALL( SCIPreleaseCons(scip, &andcons) ); 1389 1390 /* delete the or constraint */ 1391 SCIP_CALL( SCIPdelCons(scip, cons) ); 1392 1393 /* free temporary memory */ 1394 SCIPfreeBufferArray(scip, &negvars); 1395 1396 (*nupgdconss)++; 1397 1398 return SCIP_OKAY; 1399 } 1400 1401 /** adds symmetry information of constraint to a symmetry detection graph */ 1402 static 1403 SCIP_RETCODE addSymmetryInformation( 1404 SCIP* scip, /**< SCIP pointer */ 1405 SYM_SYMTYPE symtype, /**< type of symmetries that need to be added */ 1406 SCIP_CONS* cons, /**< constraint */ 1407 SYM_GRAPH* graph, /**< symmetry detection graph */ 1408 SCIP_Bool* success /**< pointer to store whether symmetry information could be added */ 1409 ) 1410 { 1411 SCIP_CONSDATA* consdata; 1412 SCIP_VAR** orvars; 1413 SCIP_VAR** vars; 1414 SCIP_Real* vals; 1415 SCIP_Real constant = 0.0; 1416 int nlocvars; 1417 int nvars; 1418 int i; 1419 1420 assert(scip != NULL); 1421 assert(cons != NULL); 1422 assert(graph != NULL); 1423 assert(success != NULL); 1424 1425 consdata = SCIPconsGetData(cons); 1426 assert(consdata != NULL); 1427 1428 /* get active variables of the constraint */ 1429 nvars = SCIPgetNVars(scip); 1430 nlocvars = SCIPgetNVarsOr(scip, cons); 1431 1432 SCIP_CALL( SCIPallocBufferArray(scip, &vars, nvars) ); 1433 SCIP_CALL( SCIPallocBufferArray(scip, &vals, nvars) ); 1434 1435 orvars = SCIPgetVarsOr(scip, cons); 1436 for( i = 0; i < consdata->nvars; ++i ) 1437 { 1438 vars[i] = orvars[i]; 1439 vals[i] = 1.0; 1440 } 1441 1442 assert(SCIPgetResultantOr(scip, cons) != NULL); 1443 vars[nlocvars] = SCIPgetResultantOr(scip, cons); 1444 vals[nlocvars++] = 2.0; 1445 assert(nlocvars <= nvars); 1446 1447 SCIP_CALL( SCIPgetActiveVariables(scip, symtype, &vars, &vals, &nlocvars, &constant, SCIPisTransformed(scip)) ); 1448 1449 /* represent the OR constraint via the gadget for linear constraints and use the constant as lhs/rhs to 1450 * distinguish different OR constraints (OR constraints do not have an intrinsic right-hand side) 1451 */ 1452 SCIP_CALL( SCIPextendPermsymDetectionGraphLinear(scip, graph, vars, vals, nlocvars, 1453 cons, -constant, -constant, success) ); 1454 1455 SCIPfreeBufferArray(scip, &vals); 1456 SCIPfreeBufferArray(scip, &vars); 1457 1458 return SCIP_OKAY; 1459 } 1460 1461 /* 1462 * Callback methods of constraint handler 1463 */ 1464 1465 /** copy method for constraint handler plugins (called when SCIP copies plugins) */ 1466 static 1467 SCIP_DECL_CONSHDLRCOPY(conshdlrCopyOr) 1468 { /*lint --e{715}*/ 1469 assert(scip != NULL); 1470 assert(conshdlr != NULL); 1471 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0); 1472 1473 /* call inclusion method of constraint handler */ 1474 SCIP_CALL( SCIPincludeConshdlrOr(scip) ); 1475 1476 *valid = TRUE; 1477 1478 return SCIP_OKAY; 1479 } 1480 1481 /** destructor of constraint handler to free constraint handler data (called when SCIP is exiting) */ 1482 static 1483 SCIP_DECL_CONSFREE(consFreeOr) 1484 { /*lint --e{715}*/ 1485 SCIP_CONSHDLRDATA* conshdlrdata; 1486 1487 /* free constraint handler data */ 1488 conshdlrdata = SCIPconshdlrGetData(conshdlr); 1489 assert(conshdlrdata != NULL); 1490 1491 conshdlrdataFree(scip, &conshdlrdata); 1492 1493 SCIPconshdlrSetData(conshdlr, NULL); 1494 1495 return SCIP_OKAY; 1496 } 1497 1498 1499 /** solving process deinitialization method of constraint handler (called before branch and bound process data is freed) */ 1500 static 1501 SCIP_DECL_CONSEXITSOL(consExitsolOr) 1502 { /*lint --e{715}*/ 1503 SCIP_CONSDATA* consdata; 1504 int c; 1505 1506 /* release and free the rows of all constraints */ 1507 for( c = 0; c < nconss; ++c ) 1508 { 1509 consdata = SCIPconsGetData(conss[c]); 1510 SCIP_CALL( consdataFreeRows(scip, consdata) ); 1511 } 1512 1513 return SCIP_OKAY; 1514 } 1515 1516 1517 /** frees specific constraint data */ 1518 static 1519 SCIP_DECL_CONSDELETE(consDeleteOr) 1520 { /*lint --e{715}*/ 1521 SCIP_CONSHDLRDATA* conshdlrdata; 1522 1523 conshdlrdata = SCIPconshdlrGetData(conshdlr); 1524 assert(conshdlrdata != NULL); 1525 1526 SCIP_CALL( consdataFree(scip, consdata, conshdlrdata->eventhdlr) ); 1527 1528 return SCIP_OKAY; 1529 } 1530 1531 1532 /** transforms constraint data into data belonging to the transformed problem */ 1533 static 1534 SCIP_DECL_CONSTRANS(consTransOr) 1535 { /*lint --e{715}*/ 1536 SCIP_CONSHDLRDATA* conshdlrdata; 1537 SCIP_CONSDATA* sourcedata; 1538 SCIP_CONSDATA* targetdata; 1539 1540 conshdlrdata = SCIPconshdlrGetData(conshdlr); 1541 assert(conshdlrdata != NULL); 1542 1543 sourcedata = SCIPconsGetData(sourcecons); 1544 assert(sourcedata != NULL); 1545 1546 /* create target constraint data */ 1547 SCIP_CALL( consdataCreate(scip, &targetdata, conshdlrdata->eventhdlr, 1548 sourcedata->nvars, sourcedata->vars, sourcedata->resvar) ); 1549 1550 /* create target constraint */ 1551 SCIP_CALL( SCIPcreateCons(scip, targetcons, SCIPconsGetName(sourcecons), conshdlr, targetdata, 1552 SCIPconsIsInitial(sourcecons), SCIPconsIsSeparated(sourcecons), SCIPconsIsEnforced(sourcecons), 1553 SCIPconsIsChecked(sourcecons), SCIPconsIsPropagated(sourcecons), 1554 SCIPconsIsLocal(sourcecons), SCIPconsIsModifiable(sourcecons), 1555 SCIPconsIsDynamic(sourcecons), SCIPconsIsRemovable(sourcecons), SCIPconsIsStickingAtNode(sourcecons)) ); 1556 1557 return SCIP_OKAY; 1558 } 1559 1560 1561 /** LP initialization method of constraint handler (called before the initial LP relaxation at a node is solved) */ 1562 static 1563 SCIP_DECL_CONSINITLP(consInitlpOr) 1564 { /*lint --e{715}*/ 1565 int i; 1566 1567 *infeasible = FALSE; 1568 1569 for( i = 0; i < nconss && !(*infeasible); i++ ) 1570 { 1571 assert(SCIPconsIsInitial(conss[i])); 1572 SCIP_CALL( addRelaxation(scip, conss[i], infeasible) ); 1573 } 1574 1575 return SCIP_OKAY; 1576 } 1577 1578 1579 /** separation method of constraint handler for LP solutions */ 1580 static 1581 SCIP_DECL_CONSSEPALP(consSepalpOr) 1582 { /*lint --e{715}*/ 1583 SCIP_Bool separated; 1584 int c; 1585 1586 *result = SCIP_DIDNOTFIND; 1587 1588 /* separate all useful constraints */ 1589 for( c = 0; c < nusefulconss; ++c ) 1590 { 1591 SCIP_CALL( separateCons(scip, conss[c], NULL, &separated) ); 1592 if( separated ) 1593 *result = SCIP_SEPARATED; 1594 } 1595 1596 /* combine constraints to get more cuts */ 1597 /**@todo combine constraints to get further cuts */ 1598 1599 return SCIP_OKAY; 1600 } 1601 1602 1603 /** separation method of constraint handler for arbitrary primal solutions */ 1604 static 1605 SCIP_DECL_CONSSEPASOL(consSepasolOr) 1606 { /*lint --e{715}*/ 1607 SCIP_Bool separated; 1608 int c; 1609 1610 *result = SCIP_DIDNOTFIND; 1611 1612 /* separate all useful constraints */ 1613 for( c = 0; c < nusefulconss; ++c ) 1614 { 1615 SCIP_CALL( separateCons(scip, conss[c], sol, &separated) ); 1616 if( separated ) 1617 *result = SCIP_SEPARATED; 1618 } 1619 1620 /* combine constraints to get more cuts */ 1621 /**@todo combine constraints to get further cuts */ 1622 1623 return SCIP_OKAY; 1624 } 1625 1626 1627 /** constraint enforcing method of constraint handler for LP solutions */ 1628 static 1629 SCIP_DECL_CONSENFOLP(consEnfolpOr) 1630 { /*lint --e{715}*/ 1631 SCIP_Bool violated; 1632 int i; 1633 1634 /* method is called only for integral solutions, because the enforcing priority is negative */ 1635 for( i = 0; i < nconss; i++ ) 1636 { 1637 SCIP_CALL( checkCons(scip, conss[i], NULL, FALSE, FALSE, &violated) ); 1638 if( violated ) 1639 { 1640 SCIP_Bool separated; 1641 1642 SCIP_CALL( separateCons(scip, conss[i], NULL, &separated) ); 1643 1644 /* if the solution is integral, the separation always finds a cut 1645 * if some implicit binary variable is not integral, then some other constraint needs to be enforced first 1646 */ 1647 if( separated ) 1648 *result = SCIP_SEPARATED; 1649 else 1650 *result = SCIP_INFEASIBLE; 1651 1652 return SCIP_OKAY; 1653 } 1654 } 1655 *result = SCIP_FEASIBLE; 1656 1657 return SCIP_OKAY; 1658 } 1659 1660 1661 /** constraint enforcing method of constraint handler for relaxation solutions */ 1662 static 1663 SCIP_DECL_CONSENFORELAX(consEnforelaxOr) 1664 { /*lint --e{715}*/ 1665 SCIP_Bool violated; 1666 int i; 1667 1668 /* method is called only for integral solutions, because the enforcing priority is negative */ 1669 for( i = 0; i < nconss; i++ ) 1670 { 1671 SCIP_CALL( checkCons(scip, conss[i], sol, FALSE, FALSE, &violated) ); 1672 if( violated ) 1673 { 1674 SCIP_Bool separated; 1675 1676 SCIP_CALL( separateCons(scip, conss[i], sol, &separated) ); 1677 1678 /* if the solution is integral, the separation always finds a cut 1679 * if some implicit binary variable is not integral, then some other constraint needs to be enforced first 1680 */ 1681 if( separated ) 1682 *result = SCIP_SEPARATED; 1683 else 1684 *result = SCIP_INFEASIBLE; 1685 1686 return SCIP_OKAY; 1687 } 1688 } 1689 *result = SCIP_FEASIBLE; 1690 1691 return SCIP_OKAY; 1692 } 1693 1694 1695 /** constraint enforcing method of constraint handler for pseudo solutions */ 1696 static 1697 SCIP_DECL_CONSENFOPS(consEnfopsOr) 1698 { /*lint --e{715}*/ 1699 SCIP_Bool violated; 1700 int i; 1701 1702 /* method is called only for integral solutions, because the enforcing priority is negative */ 1703 for( i = 0; i < nconss; i++ ) 1704 { 1705 SCIP_CALL( checkCons(scip, conss[i], NULL, TRUE, FALSE, &violated) ); 1706 if( violated ) 1707 { 1708 *result = SCIP_INFEASIBLE; 1709 return SCIP_OKAY; 1710 } 1711 } 1712 *result = SCIP_FEASIBLE; 1713 1714 return SCIP_OKAY; 1715 } 1716 1717 1718 /** feasibility check method of constraint handler for integral solutions */ 1719 static 1720 SCIP_DECL_CONSCHECK(consCheckOr) 1721 { /*lint --e{715}*/ 1722 int i; 1723 1724 *result = SCIP_FEASIBLE; 1725 1726 /* method is called only for integral solutions, because the enforcing priority is negative */ 1727 for( i = 0; i < nconss && (*result == SCIP_FEASIBLE || completely); i++ ) 1728 { 1729 SCIP_Bool violated = FALSE; 1730 1731 SCIP_CALL( checkCons(scip, conss[i], sol, checklprows, printreason, &violated) ); 1732 1733 if( violated ) 1734 *result = SCIP_INFEASIBLE; 1735 } 1736 1737 return SCIP_OKAY; 1738 } 1739 1740 1741 /** domain propagation method of constraint handler */ 1742 static 1743 SCIP_DECL_CONSPROP(consPropOr) 1744 { /*lint --e{715}*/ 1745 SCIP_CONSHDLRDATA* conshdlrdata; 1746 SCIP_Bool cutoff; 1747 int nfixedvars; 1748 int c; 1749 1750 conshdlrdata = SCIPconshdlrGetData(conshdlr); 1751 assert(conshdlrdata != NULL); 1752 1753 cutoff = FALSE; 1754 nfixedvars = 0; 1755 1756 /* propagate all useful constraints */ 1757 for( c = 0; c < nusefulconss && !cutoff; ++c ) 1758 { 1759 SCIP_CALL( propagateCons(scip, conss[c], conshdlrdata->eventhdlr, &cutoff, &nfixedvars) ); 1760 } 1761 1762 /* return the correct result */ 1763 if( cutoff ) 1764 *result = SCIP_CUTOFF; 1765 else if( nfixedvars > 0 ) 1766 *result = SCIP_REDUCEDDOM; 1767 else 1768 *result = SCIP_DIDNOTFIND; 1769 1770 return SCIP_OKAY; 1771 } 1772 1773 1774 /** presolving method of constraint handler */ 1775 static 1776 SCIP_DECL_CONSPRESOL(consPresolOr) 1777 { /*lint --e{715}*/ 1778 SCIP_CONSHDLRDATA* conshdlrdata; 1779 SCIP_CONS* cons; 1780 SCIP_CONSDATA* consdata; 1781 SCIP_Bool cutoff; 1782 SCIP_Bool redundant; 1783 SCIP_Bool aggregated; 1784 int oldnfixedvars; 1785 int oldnaggrvars; 1786 int oldnupgdconss; 1787 int c; 1788 1789 assert(result != NULL); 1790 1791 *result = SCIP_DIDNOTFIND; 1792 oldnfixedvars = *nfixedvars; 1793 oldnaggrvars = *naggrvars; 1794 oldnupgdconss = *nupgdconss; 1795 1796 conshdlrdata = SCIPconshdlrGetData(conshdlr); 1797 assert(conshdlrdata != NULL); 1798 1799 /* process constraints */ 1800 cutoff = FALSE; 1801 for( c = 0; c < nconss && !cutoff && !SCIPisStopped(scip); ++c ) 1802 { 1803 cons = conss[c]; 1804 assert(cons != NULL); 1805 consdata = SCIPconsGetData(cons); 1806 assert(consdata != NULL); 1807 1808 /* force presolving the constraint in the initial round */ 1809 if( nrounds == 0 ) 1810 consdata->propagated = FALSE; 1811 1812 /* propagate constraint */ 1813 SCIP_CALL( propagateCons(scip, cons, conshdlrdata->eventhdlr, &cutoff, nfixedvars) ); 1814 1815 /* remove all variables that are fixed to one */ 1816 SCIP_CALL( applyFixings(scip, cons, conshdlrdata->eventhdlr) ); 1817 1818 /* transform or constraints into and constraints on the negated variables in order to improve 1819 * the pairwise constraint presolving possibilities 1820 */ 1821 SCIP_CALL( upgradeCons(scip, cons, nupgdconss) ); 1822 1823 if( !cutoff && !SCIPconsIsDeleted(cons) && !SCIPconsIsModifiable(cons) ) 1824 { 1825 assert(consdata->nvars >= 1); /* otherwise, propagateCons() has deleted the constraint */ 1826 1827 /* if only one variable is left, the resultant has to be equal to this single variable */ 1828 if( consdata->nvars == 1 ) 1829 { 1830 SCIPdebugMsg(scip, "or constraint <%s> has only one variable not fixed to 0.0\n", SCIPconsGetName(cons)); 1831 1832 assert(consdata->vars != NULL); 1833 assert(SCIPisFeasEQ(scip, SCIPvarGetLbGlobal(consdata->vars[0]), 0.0)); 1834 assert(SCIPisFeasEQ(scip, SCIPvarGetUbGlobal(consdata->vars[0]), 1.0)); 1835 1836 /* aggregate variables: resultant - operand == 0 */ 1837 SCIP_CALL( SCIPaggregateVars(scip, consdata->resvar, consdata->vars[0], 1.0, -1.0, 0.0, 1838 &cutoff, &redundant, &aggregated) ); 1839 assert(redundant || SCIPdoNotAggr(scip)); 1840 1841 if( aggregated ) 1842 { 1843 assert(redundant); 1844 (*naggrvars)++; 1845 } 1846 1847 if( redundant ) 1848 { 1849 /* delete constraint */ 1850 SCIP_CALL( SCIPdelCons(scip, cons) ); 1851 (*ndelconss)++; 1852 } 1853 } 1854 else if( !consdata->impladded ) 1855 { 1856 int i; 1857 1858 /* add implications: resultant == 0 -> all operands == 0 */ 1859 for( i = 0; i < consdata->nvars && !cutoff; ++i ) 1860 { 1861 int nimplbdchgs; 1862 1863 SCIP_CALL( SCIPaddVarImplication(scip, consdata->resvar, FALSE, consdata->vars[i], 1864 SCIP_BOUNDTYPE_UPPER, 0.0, &cutoff, &nimplbdchgs) ); 1865 *nchgbds += nimplbdchgs; 1866 } 1867 consdata->impladded = TRUE; 1868 } 1869 1870 /* if in r = x or y, the resultant is fixed to one, add implication x = 0 -> y = 1 */ 1871 if( !cutoff && SCIPconsIsActive(cons) && consdata->nvars == 2 && !consdata->opimpladded 1872 && SCIPvarGetLbGlobal(consdata->resvar) > 0.5 ) 1873 { 1874 int nimplbdchgs; 1875 1876 SCIP_CALL( SCIPaddVarImplication(scip, consdata->vars[0], FALSE, consdata->vars[1], 1877 SCIP_BOUNDTYPE_LOWER, 1.0, &cutoff, &nimplbdchgs) ); 1878 (*nchgbds) += nimplbdchgs; 1879 consdata->opimpladded = TRUE; 1880 } 1881 } 1882 } 1883 1884 /* return the correct result code */ 1885 if( cutoff ) 1886 *result = SCIP_CUTOFF; 1887 else if( *nfixedvars > oldnfixedvars || *naggrvars > oldnaggrvars || *nupgdconss > oldnupgdconss ) 1888 *result = SCIP_SUCCESS; 1889 1890 return SCIP_OKAY; 1891 } 1892 1893 1894 /** propagation conflict resolving method of constraint handler */ 1895 static 1896 SCIP_DECL_CONSRESPROP(consRespropOr) 1897 { /*lint --e{715}*/ 1898 SCIP_CALL( resolvePropagation(scip, cons, infervar, (PROPRULE)inferinfo, bdchgidx, result) ); 1899 1900 return SCIP_OKAY; 1901 } 1902 1903 1904 /** variable rounding lock method of constraint handler */ 1905 static 1906 SCIP_DECL_CONSLOCK(consLockOr) 1907 { /*lint --e{715}*/ 1908 SCIP_CONSDATA* consdata; 1909 int i; 1910 1911 assert(locktype == SCIP_LOCKTYPE_MODEL); 1912 1913 consdata = SCIPconsGetData(cons); 1914 assert(consdata != NULL); 1915 1916 /* lock resultant variable */ 1917 SCIP_CALL( SCIPaddVarLocksType(scip, consdata->resvar, locktype, nlockspos + nlocksneg, nlockspos + nlocksneg) ); 1918 1919 /* lock all operand variables */ 1920 for( i = 0; i < consdata->nvars; ++i ) 1921 { 1922 SCIP_CALL( SCIPaddVarLocksType(scip, consdata->vars[i], locktype, nlockspos + nlocksneg, nlockspos + nlocksneg) ); 1923 } 1924 1925 return SCIP_OKAY; 1926 } 1927 1928 1929 /** constraint display method of constraint handler */ 1930 static 1931 SCIP_DECL_CONSPRINT(consPrintOr) 1932 { /*lint --e{715}*/ 1933 assert( scip != NULL ); 1934 assert( conshdlr != NULL ); 1935 assert( cons != NULL ); 1936 1937 SCIP_CALL( consdataPrint(scip, SCIPconsGetData(cons), file) ); 1938 1939 return SCIP_OKAY; 1940 } 1941 1942 /** constraint copying method of constraint handler */ 1943 static 1944 SCIP_DECL_CONSCOPY(consCopyOr) 1945 { /*lint --e{715}*/ 1946 SCIP_VAR** sourcevars; 1947 SCIP_VAR** vars; 1948 SCIP_VAR* sourceresvar; 1949 SCIP_VAR* resvar; 1950 int nvars; 1951 int v; 1952 1953 assert(valid != NULL); 1954 (*valid) = TRUE; 1955 resvar = NULL; 1956 1957 /* get variables that need to be copied */ 1958 sourceresvar = SCIPgetResultantOr(sourcescip, sourcecons); 1959 sourcevars = SCIPgetVarsOr(sourcescip, sourcecons); 1960 nvars = SCIPgetNVarsOr(sourcescip, sourcecons); 1961 1962 if( nvars == -1 ) 1963 return SCIP_INVALIDCALL; 1964 1965 /* allocate buffer array */ 1966 SCIP_CALL( SCIPallocBufferArray(scip, &vars, nvars) ); 1967 1968 /* map operand variables to active variables of the target SCIP */ 1969 for( v = 0; v < nvars && *valid; ++v ) 1970 { 1971 SCIP_CALL( SCIPgetVarCopy(sourcescip, scip, sourcevars[v], &vars[v], varmap, consmap, global, valid) ); 1972 assert(!(*valid) || vars[v] != NULL); 1973 } 1974 1975 /* map resultant to active variable of the target SCIP */ 1976 if( *valid ) 1977 { 1978 SCIP_CALL( SCIPgetVarCopy(sourcescip, scip, sourceresvar, &resvar, varmap, consmap, global, valid) ); 1979 assert(!(*valid) || resvar != NULL); 1980 1981 if( *valid ) 1982 { 1983 assert(resvar != NULL); 1984 SCIP_CALL( SCIPcreateConsOr(scip, cons, SCIPconsGetName(sourcecons), resvar, nvars, vars, 1985 initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode) ); 1986 } 1987 } 1988 1989 /* free buffer array */ 1990 SCIPfreeBufferArray(scip, &vars); 1991 1992 return SCIP_OKAY; 1993 } 1994 1995 /** constraint parsing method of constraint handler */ 1996 static 1997 SCIP_DECL_CONSPARSE(consParseOr) 1998 { /*lint --e{715}*/ 1999 SCIP_VAR** vars; 2000 SCIP_VAR* resvar; 2001 char* strcopy; 2002 char* token; 2003 char* saveptr; 2004 char* endptr; 2005 int requiredsize; 2006 int varssize; 2007 int nvars; 2008 2009 SCIPdebugMsg(scip, "parse <%s> as or constraint\n", str); 2010 2011 *success = FALSE; 2012 2013 /* copy string for truncating it */ 2014 SCIP_CALL( SCIPduplicateBufferArray(scip, &strcopy, str, (int)(strlen(str)+1))); 2015 2016 /* cutoff "or" form the constraint string */ 2017 token = SCIPstrtok(strcopy, "=", &saveptr ); 2018 2019 /* parse variable name */ 2020 SCIP_CALL( SCIPparseVarName(scip, token, &resvar, &endptr) ); 2021 2022 if( resvar == NULL ) 2023 { 2024 SCIPerrorMessage("resultant variable does not exist\n"); 2025 } 2026 else 2027 { 2028 /* cutoff "or" form the constraint string */ 2029 (void) SCIPstrtok(NULL, "(", &saveptr ); 2030 2031 /* cutoff ")" form the constraint string */ 2032 token = SCIPstrtok(NULL, ")", &saveptr ); 2033 2034 varssize = 100; 2035 nvars = 0; 2036 2037 /* allocate buffer array for variables */ 2038 SCIP_CALL( SCIPallocBufferArray(scip, &vars, varssize) ); 2039 2040 /* parse string */ 2041 SCIP_CALL( SCIPparseVarsList(scip, token, vars, &nvars, varssize, &requiredsize, &endptr, ',', success) ); 2042 2043 if( *success ) 2044 { 2045 /* check if the size of the variable array was great enough */ 2046 if( varssize < requiredsize ) 2047 { 2048 /* reallocate memory */ 2049 varssize = requiredsize; 2050 SCIP_CALL( SCIPreallocBufferArray(scip, &vars, varssize) ); 2051 2052 /* parse string again with the correct size of the variable array */ 2053 SCIP_CALL( SCIPparseVarsList(scip, token, vars, &nvars, varssize, &requiredsize, &endptr, ',', success) ); 2054 } 2055 2056 assert(*success); 2057 assert(varssize >= requiredsize); 2058 2059 /* create and constraint */ 2060 SCIP_CALL( SCIPcreateConsOr(scip, cons, name, resvar, nvars, vars, 2061 initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode) ); 2062 } 2063 2064 /* free variable buffer */ 2065 SCIPfreeBufferArray(scip, &vars); 2066 } 2067 2068 /* free string buffer */ 2069 SCIPfreeBufferArray(scip, &strcopy); 2070 2071 return SCIP_OKAY; 2072 } 2073 2074 /** constraint method of constraint handler which returns the variables (if possible) */ 2075 static 2076 SCIP_DECL_CONSGETVARS(consGetVarsOr) 2077 { /*lint --e{715}*/ 2078 SCIP_CONSDATA* consdata; 2079 2080 consdata = SCIPconsGetData(cons); 2081 assert(consdata != NULL); 2082 2083 if( varssize < consdata->nvars + 1 ) 2084 (*success) = FALSE; 2085 else 2086 { 2087 BMScopyMemoryArray(vars, consdata->vars, consdata->nvars); 2088 vars[consdata->nvars] = consdata->resvar; 2089 (*success) = TRUE; 2090 } 2091 2092 return SCIP_OKAY; 2093 } 2094 2095 /** constraint method of constraint handler which returns the number of variable (if possible) */ 2096 static 2097 SCIP_DECL_CONSGETNVARS(consGetNVarsOr) 2098 { /*lint --e{715}*/ 2099 SCIP_CONSDATA* consdata; 2100 2101 assert(cons != NULL); 2102 2103 consdata = SCIPconsGetData(cons); 2104 assert(consdata != NULL); 2105 2106 (*nvars) = consdata->nvars + 1; 2107 (*success) = TRUE; 2108 2109 return SCIP_OKAY; 2110 } 2111 2112 /** constraint handler method which returns the permutation symmetry detection graph of a constraint */ 2113 static 2114 SCIP_DECL_CONSGETPERMSYMGRAPH(consGetPermsymGraphOr) 2115 { /*lint --e{715}*/ 2116 SCIP_CALL( addSymmetryInformation(scip, SYM_SYMTYPE_PERM, cons, graph, success) ); 2117 2118 return SCIP_OKAY; 2119 } 2120 2121 /** constraint handler method which returns the signed permutation symmetry detection graph of a constraint */ 2122 static 2123 SCIP_DECL_CONSGETSIGNEDPERMSYMGRAPH(consGetSignedPermsymGraphOr) 2124 { /*lint --e{715}*/ 2125 SCIP_CALL( addSymmetryInformation(scip, SYM_SYMTYPE_SIGNPERM, cons, graph, success) ); 2126 2127 return SCIP_OKAY; 2128 } 2129 2130 /* 2131 * Callback methods of event handler 2132 */ 2133 2134 static 2135 SCIP_DECL_EVENTEXEC(eventExecOr) 2136 { /*lint --e{715}*/ 2137 SCIP_CONSDATA* consdata; 2138 2139 assert(eventhdlr != NULL); 2140 assert(eventdata != NULL); 2141 assert(event != NULL); 2142 2143 consdata = (SCIP_CONSDATA*)eventdata; 2144 assert(consdata != NULL); 2145 2146 /* check, if the variable was fixed to one */ 2147 if( SCIPeventGetType(event) == SCIP_EVENTTYPE_LBTIGHTENED ) 2148 consdata->nofixedone = FALSE; 2149 2150 consdata->propagated = FALSE; 2151 2152 return SCIP_OKAY; 2153 } 2154 2155 2156 /* 2157 * constraint specific interface methods 2158 */ 2159 2160 /** creates the handler for or constraints and includes it in SCIP */ 2161 SCIP_RETCODE SCIPincludeConshdlrOr( 2162 SCIP* scip /**< SCIP data structure */ 2163 ) 2164 { 2165 SCIP_CONSHDLRDATA* conshdlrdata; 2166 SCIP_CONSHDLR* conshdlr; 2167 SCIP_EVENTHDLR* eventhdlr; 2168 2169 /* create event handler for events on variables */ 2170 SCIP_CALL( SCIPincludeEventhdlrBasic(scip, &eventhdlr, EVENTHDLR_NAME, EVENTHDLR_DESC, 2171 eventExecOr, NULL) ); 2172 2173 /* create constraint handler data */ 2174 SCIP_CALL( conshdlrdataCreate(scip, &conshdlrdata, eventhdlr) ); 2175 2176 /* include constraint handler */ 2177 SCIP_CALL( SCIPincludeConshdlrBasic(scip, &conshdlr, CONSHDLR_NAME, CONSHDLR_DESC, 2178 CONSHDLR_ENFOPRIORITY, CONSHDLR_CHECKPRIORITY, CONSHDLR_EAGERFREQ, CONSHDLR_NEEDSCONS, 2179 consEnfolpOr, consEnfopsOr, consCheckOr, consLockOr, 2180 conshdlrdata) ); 2181 assert(conshdlr != NULL); 2182 2183 /* set non-fundamental callbacks via specific setter functions */ 2184 SCIP_CALL( SCIPsetConshdlrCopy(scip, conshdlr, conshdlrCopyOr, consCopyOr) ); 2185 SCIP_CALL( SCIPsetConshdlrDelete(scip, conshdlr, consDeleteOr) ); 2186 SCIP_CALL( SCIPsetConshdlrExitsol(scip, conshdlr, consExitsolOr) ); 2187 SCIP_CALL( SCIPsetConshdlrFree(scip, conshdlr, consFreeOr) ); 2188 SCIP_CALL( SCIPsetConshdlrGetVars(scip, conshdlr, consGetVarsOr) ); 2189 SCIP_CALL( SCIPsetConshdlrGetNVars(scip, conshdlr, consGetNVarsOr) ); 2190 SCIP_CALL( SCIPsetConshdlrInitlp(scip, conshdlr, consInitlpOr) ); 2191 SCIP_CALL( SCIPsetConshdlrParse(scip, conshdlr, consParseOr) ); 2192 SCIP_CALL( SCIPsetConshdlrPresol(scip, conshdlr, consPresolOr, CONSHDLR_MAXPREROUNDS, CONSHDLR_PRESOLTIMING) ); 2193 SCIP_CALL( SCIPsetConshdlrPrint(scip, conshdlr, consPrintOr) ); 2194 SCIP_CALL( SCIPsetConshdlrProp(scip, conshdlr, consPropOr, CONSHDLR_PROPFREQ, CONSHDLR_DELAYPROP, 2195 CONSHDLR_PROP_TIMING) ); 2196 SCIP_CALL( SCIPsetConshdlrResprop(scip, conshdlr, consRespropOr) ); 2197 SCIP_CALL( SCIPsetConshdlrSepa(scip, conshdlr, consSepalpOr, consSepasolOr, CONSHDLR_SEPAFREQ, CONSHDLR_SEPAPRIORITY, 2198 CONSHDLR_DELAYSEPA) ); 2199 SCIP_CALL( SCIPsetConshdlrTrans(scip, conshdlr, consTransOr) ); 2200 SCIP_CALL( SCIPsetConshdlrEnforelax(scip, conshdlr, consEnforelaxOr) ); 2201 SCIP_CALL( SCIPsetConshdlrGetPermsymGraph(scip, conshdlr, consGetPermsymGraphOr) ); 2202 SCIP_CALL( SCIPsetConshdlrGetSignedPermsymGraph(scip, conshdlr, consGetSignedPermsymGraphOr) ); 2203 2204 return SCIP_OKAY; 2205 } 2206 2207 /** creates and captures an or constraint 2208 * 2209 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons() 2210 */ 2211 SCIP_RETCODE SCIPcreateConsOr( 2212 SCIP* scip, /**< SCIP data structure */ 2213 SCIP_CONS** cons, /**< pointer to hold the created constraint */ 2214 const char* name, /**< name of constraint */ 2215 SCIP_VAR* resvar, /**< resultant variable of the operation */ 2216 int nvars, /**< number of operator variables in the constraint */ 2217 SCIP_VAR** vars, /**< array with operator variables of constraint */ 2218 SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP? 2219 * Usually set to TRUE. Set to FALSE for 'lazy constraints'. */ 2220 SCIP_Bool separate, /**< should the constraint be separated during LP processing? 2221 * Usually set to TRUE. */ 2222 SCIP_Bool enforce, /**< should the constraint be enforced during node processing? 2223 * TRUE for model constraints, FALSE for additional, redundant constraints. */ 2224 SCIP_Bool check, /**< should the constraint be checked for feasibility? 2225 * TRUE for model constraints, FALSE for additional, redundant constraints. */ 2226 SCIP_Bool propagate, /**< should the constraint be propagated during node processing? 2227 * Usually set to TRUE. */ 2228 SCIP_Bool local, /**< is constraint only valid locally? 2229 * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */ 2230 SCIP_Bool modifiable, /**< is constraint modifiable (subject to column generation)? 2231 * Usually set to FALSE. In column generation applications, set to TRUE if pricing 2232 * adds coefficients to this constraint. */ 2233 SCIP_Bool dynamic, /**< is constraint subject to aging? 2234 * Usually set to FALSE. Set to TRUE for own cuts which 2235 * are separated as constraints. */ 2236 SCIP_Bool removable, /**< should the relaxation be removed from the LP due to aging or cleanup? 2237 * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */ 2238 SCIP_Bool stickingatnode /**< should the constraint always be kept at the node where it was added, even 2239 * if it may be moved to a more global node? 2240 * Usually set to FALSE. Set to TRUE to for constraints that represent node data. */ 2241 ) 2242 { 2243 SCIP_CONSHDLR* conshdlr; 2244 SCIP_CONSHDLRDATA* conshdlrdata; 2245 SCIP_CONSDATA* consdata; 2246 2247 /* find the or constraint handler */ 2248 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME); 2249 if( conshdlr == NULL ) 2250 { 2251 SCIPerrorMessage("or constraint handler not found\n"); 2252 return SCIP_PLUGINNOTFOUND; 2253 } 2254 2255 conshdlrdata = SCIPconshdlrGetData(conshdlr); 2256 assert(conshdlrdata != NULL); 2257 2258 /* create constraint data */ 2259 SCIP_CALL( consdataCreate(scip, &consdata, conshdlrdata->eventhdlr, nvars, vars, resvar) ); 2260 2261 /* create constraint */ 2262 SCIP_CALL( SCIPcreateCons(scip, cons, name, conshdlr, consdata, initial, separate, enforce, check, propagate, 2263 local, modifiable, dynamic, removable, stickingatnode) ); 2264 2265 return SCIP_OKAY; 2266 } 2267 2268 /** creates and captures an or constraint 2269 * in its most basic variant, i. e., with all constraint flags set to their default values 2270 * 2271 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons() 2272 */ 2273 SCIP_RETCODE SCIPcreateConsBasicOr( 2274 SCIP* scip, /**< SCIP data structure */ 2275 SCIP_CONS** cons, /**< pointer to hold the created constraint */ 2276 const char* name, /**< name of constraint */ 2277 SCIP_VAR* resvar, /**< resultant variable of the operation */ 2278 int nvars, /**< number of operator variables in the constraint */ 2279 SCIP_VAR** vars /**< array with operator variables of constraint */ 2280 ) 2281 { 2282 SCIP_CALL( SCIPcreateConsOr(scip, cons, name, resvar, nvars, vars, TRUE, TRUE, TRUE, TRUE, TRUE, 2283 FALSE, FALSE, FALSE, FALSE, FALSE) ); 2284 2285 return SCIP_OKAY; 2286 } 2287 2288 /** gets number of variables in or constraint */ 2289 int SCIPgetNVarsOr( 2290 SCIP* scip, /**< SCIP data structure */ 2291 SCIP_CONS* cons /**< constraint data */ 2292 ) 2293 { 2294 SCIP_CONSDATA* consdata; 2295 2296 assert(scip != NULL); 2297 2298 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 ) 2299 { 2300 SCIPerrorMessage("constraint is not an or constraint\n"); 2301 SCIPABORT(); 2302 return -1; /*lint !e527*/ 2303 } 2304 2305 consdata = SCIPconsGetData(cons); 2306 assert(consdata != NULL); 2307 2308 return consdata->nvars; 2309 } 2310 2311 /** gets array of variables in or constraint */ 2312 SCIP_VAR** SCIPgetVarsOr( 2313 SCIP* scip, /**< SCIP data structure */ 2314 SCIP_CONS* cons /**< constraint data */ 2315 ) 2316 { 2317 SCIP_CONSDATA* consdata; 2318 2319 assert(scip != NULL); 2320 2321 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 ) 2322 { 2323 SCIPerrorMessage("constraint is not an or constraint\n"); 2324 SCIPABORT(); 2325 return NULL; /*lint !e527*/ 2326 } 2327 2328 consdata = SCIPconsGetData(cons); 2329 assert(consdata != NULL); 2330 2331 return consdata->vars; 2332 } 2333 2334 /** gets the resultant variable in or constraint */ 2335 SCIP_VAR* SCIPgetResultantOr( 2336 SCIP* scip, /**< SCIP data structure */ 2337 SCIP_CONS* cons /**< constraint data */ 2338 ) 2339 { 2340 SCIP_CONSDATA* consdata; 2341 2342 assert(scip != NULL); 2343 2344 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 ) 2345 { 2346 SCIPerrorMessage("constraint is not a or constraint\n"); 2347 SCIPABORT(); 2348 return NULL; /*lint !e527*/ 2349 } 2350 2351 consdata = SCIPconsGetData(cons); 2352 assert(consdata != NULL); 2353 2354 return consdata->resvar; 2355 } 2356