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_cumulative.c 26 * @ingroup DEFPLUGINS_CONS 27 * @brief constraint handler for cumulative constraints 28 * @author Timo Berthold 29 * @author Stefan Heinz 30 * @author Jens Schulz 31 * 32 * Given: 33 * - a set of jobs, represented by their integer start time variables \f$S_j\f$, their array of processing times \f$p_j\f$ and of 34 * their demands \f$d_j\f$. 35 * - an integer resource capacity \f$C\f$ 36 * 37 * The cumulative constraint ensures that for each point in time \f$t\f$ \f$\sum_{j: S_j \leq t < S_j + p_j} d_j \leq C\f$ holds. 38 * 39 * Separation: 40 * - can be done using binary start time model, see Pritskers, Watters and Wolfe 41 * - or by just separating relatively weak cuts on the integer start time variables 42 * 43 * Propagation: 44 * - time tabling, Klein & Scholl (1999) 45 * - Edge-finding from Petr Vilim, adjusted and simplified for dynamic repropagation 46 * (2009) 47 * - energetic reasoning, see Baptiste, Le Pape, Nuijten (2001) 48 * 49 */ 50 51 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/ 52 53 #include <assert.h> 54 #include <string.h> 55 56 #include "tclique/tclique.h" 57 #include "scip/cons_cumulative.h" 58 #include "scip/cons_linking.h" 59 #include "scip/cons_knapsack.h" 60 #include "scip/scipdefplugins.h" 61 62 /**@name Constraint handler properties 63 * 64 * @{ 65 */ 66 67 /* constraint handler properties */ 68 #define CONSHDLR_NAME "cumulative" 69 #define CONSHDLR_DESC "cumulative constraint handler" 70 #define CONSHDLR_SEPAPRIORITY 2100000 /**< priority of the constraint handler for separation */ 71 #define CONSHDLR_ENFOPRIORITY -2040000 /**< priority of the constraint handler for constraint enforcing */ 72 #define CONSHDLR_CHECKPRIORITY -3030000 /**< priority of the constraint handler for checking feasibility */ 73 #define CONSHDLR_SEPAFREQ 1 /**< frequency for separating cuts; zero means to separate only in the root node */ 74 #define CONSHDLR_PROPFREQ 1 /**< frequency for propagating domains; zero means only preprocessing propagation */ 75 #define CONSHDLR_EAGERFREQ 100 /**< frequency for using all instead of only the useful constraints in separation, 76 * propagation and enforcement, -1 for no eager evaluations, 0 for first only */ 77 #define CONSHDLR_MAXPREROUNDS -1 /**< maximal number of presolving rounds the constraint handler participates in (-1: no limit) */ 78 #define CONSHDLR_DELAYSEPA FALSE /**< should separation method be delayed, if other separators found cuts? */ 79 #define CONSHDLR_DELAYPROP FALSE /**< should propagation method be delayed, if other propagators found reductions? */ 80 #define CONSHDLR_NEEDSCONS TRUE /**< should the constraint handler be skipped, if no constraints are available? */ 81 82 #define CONSHDLR_PRESOLTIMING SCIP_PRESOLTIMING_ALWAYS 83 #define CONSHDLR_PROP_TIMING SCIP_PROPTIMING_BEFORELP 84 85 /**@} */ 86 87 /**@name Default parameter values 88 * 89 * @{ 90 */ 91 92 /* default parameter values */ 93 94 /* separation */ 95 #define DEFAULT_USEBINVARS FALSE /**< should the binary representation be used? */ 96 #define DEFAULT_LOCALCUTS FALSE /**< should cuts be added only locally? */ 97 #define DEFAULT_USECOVERCUTS TRUE /**< should covering cuts be added? */ 98 #define DEFAULT_CUTSASCONSS TRUE /**< should the cuts be created as knapsack constraints? */ 99 #define DEFAULT_SEPAOLD TRUE /**< shall old sepa algo be applied? */ 100 101 /* propagation */ 102 #define DEFAULT_TTINFER TRUE /**< should time-table (core-times) propagator be used to infer bounds? */ 103 #define DEFAULT_EFCHECK FALSE /**< should edge-finding be used to detect an overload? */ 104 #define DEFAULT_EFINFER FALSE /**< should edge-finding be used to infer bounds? */ 105 #define DEFAULT_USEADJUSTEDJOBS FALSE /**< should during edge-finding jobs be adusted which run on the border of the effective time horizon? */ 106 #define DEFAULT_TTEFCHECK TRUE /**< should time-table edge-finding be used to detect an overload? */ 107 #define DEFAULT_TTEFINFER TRUE /**< should time-table edge-finding be used to infer bounds? */ 108 109 /* presolving */ 110 #define DEFAULT_DUALPRESOLVE TRUE /**< should dual presolving be applied? */ 111 #define DEFAULT_COEFTIGHTENING FALSE /**< should coeffisient tightening be applied? */ 112 #define DEFAULT_NORMALIZE TRUE /**< should demands and capacity be normalized? */ 113 #define DEFAULT_PRESOLPAIRWISE TRUE /**< should pairwise constraint comparison be performed in presolving? */ 114 #define DEFAULT_DISJUNCTIVE TRUE /**< extract disjunctive constraints? */ 115 #define DEFAULT_DETECTDISJUNCTIVE TRUE /**< search for conflict set via maximal cliques to detect disjunctive constraints */ 116 #define DEFAULT_DETECTVARBOUNDS TRUE /**< search for conflict set via maximal cliques to detect variable bound constraints */ 117 #define DEFAULT_MAXNODES 10000LL /**< number of branch-and-bound nodes to solve an independent cumulative constraint (-1: no limit) */ 118 119 /* enforcement */ 120 #define DEFAULT_FILLBRANCHCANDS FALSE /**< should branching candidates be added to storage? */ 121 122 /* conflict analysis */ 123 #define DEFAULT_USEBDWIDENING TRUE /**< should bound widening be used during conflict analysis? */ 124 125 /**@} */ 126 127 /**@name Event handler properties 128 * 129 * @{ 130 */ 131 132 #define EVENTHDLR_NAME "cumulative" 133 #define EVENTHDLR_DESC "bound change event handler for cumulative constraints" 134 135 /**@} */ 136 137 /* 138 * Data structures 139 */ 140 141 /** constraint data for cumulative constraints */ 142 struct SCIP_ConsData 143 { 144 SCIP_VAR** vars; /**< array of variable representing the start time of each job */ 145 SCIP_Bool* downlocks; /**< array to store if the variable has a down lock */ 146 SCIP_Bool* uplocks; /**< array to store if the variable has an uplock */ 147 SCIP_CONS** linkingconss; /**< array of linking constraints for the integer variables */ 148 SCIP_ROW** demandrows; /**< array of rows of linear relaxation of this problem */ 149 SCIP_ROW** scoverrows; /**< array of rows of small cover cuts of this problem */ 150 SCIP_ROW** bcoverrows; /**< array of rows of big cover cuts of this problem */ 151 int* demands; /**< array containing corresponding demands */ 152 int* durations; /**< array containing corresponding durations */ 153 SCIP_Real resstrength1; /**< stores the resource strength 1*/ 154 SCIP_Real resstrength2; /**< stores the resource strength 2 */ 155 SCIP_Real cumfactor1; /**< stroes the cumulativeness of the constraint */ 156 SCIP_Real disjfactor1; /**< stores the disjunctiveness of the constraint */ 157 SCIP_Real disjfactor2; /**< stores the disjunctiveness of the constraint */ 158 SCIP_Real estimatedstrength; 159 int nvars; /**< number of variables */ 160 int varssize; /**< size of the arrays */ 161 int ndemandrows; /**< number of rows of cumulative constrint for linear relaxation */ 162 int demandrowssize; /**< size of array rows of demand rows */ 163 int nscoverrows; /**< number of rows of small cover cuts */ 164 int scoverrowssize; /**< size of array of small cover cuts */ 165 int nbcoverrows; /**< number of rows of big cover cuts */ 166 int bcoverrowssize; /**< size of array of big cover cuts */ 167 int capacity; /**< available cumulative capacity */ 168 169 int hmin; /**< left bound of time axis to be considered (including hmin) */ 170 int hmax; /**< right bound of time axis to be considered (not including hmax) */ 171 172 unsigned int signature; /**< constraint signature which is need for pairwise comparison */ 173 174 unsigned int validsignature:1; /**< is the signature valid */ 175 unsigned int normalized:1; /**< is the constraint normalized */ 176 unsigned int covercuts:1; /**< cover cuts are created? */ 177 unsigned int propagated:1; /**< is constraint propagted */ 178 unsigned int varbounds:1; /**< bool to store if variable bound strengthening was already preformed */ 179 unsigned int triedsolving:1; /**< bool to store if we tried already to solve that constraint as independent subproblem */ 180 181 #ifdef SCIP_STATISTIC 182 int maxpeak; 183 #endif 184 }; 185 186 /** constraint handler data */ 187 struct SCIP_ConshdlrData 188 { 189 SCIP_EVENTHDLR* eventhdlr; /**< event handler for bound change events */ 190 191 SCIP_Bool usebinvars; /**< should the binary variables be used? */ 192 SCIP_Bool cutsasconss; /**< should the cumulative constraint create cuts as knapsack constraints? */ 193 SCIP_Bool ttinfer; /**< should time-table (core-times) propagator be used to infer bounds? */ 194 SCIP_Bool efcheck; /**< should edge-finding be used to detect an overload? */ 195 SCIP_Bool efinfer; /**< should edge-finding be used to infer bounds? */ 196 SCIP_Bool useadjustedjobs; /**< should during edge-finding jobs be adusted which run on the border of the effective time horizon? */ 197 SCIP_Bool ttefcheck; /**< should time-table edge-finding be used to detect an overload? */ 198 SCIP_Bool ttefinfer; /**< should time-table edge-finding be used to infer bounds? */ 199 SCIP_Bool localcuts; /**< should cuts be added only locally? */ 200 SCIP_Bool usecovercuts; /**< should covering cuts be added? */ 201 SCIP_Bool sepaold; /**< shall old sepa algo be applied? */ 202 203 SCIP_Bool fillbranchcands; /**< should branching candidates be added to storage? */ 204 205 SCIP_Bool dualpresolve; /**< should dual presolving be applied? */ 206 SCIP_Bool coeftightening; /**< should coeffisient tightening be applied? */ 207 SCIP_Bool normalize; /**< should demands and capacity be normalized? */ 208 SCIP_Bool disjunctive; /**< extract disjunctive constraints? */ 209 SCIP_Bool detectdisjunctive; /**< search for conflict set via maximal cliques to detect disjunctive constraints */ 210 SCIP_Bool detectvarbounds; /**< search for conflict set via maximal cliques to detect variable bound constraints */ 211 SCIP_Bool usebdwidening; /**< should bound widening be used during conflict analysis? */ 212 SCIP_Bool presolpairwise; /**< should pairwise constraint comparison be performed in presolving? */ 213 SCIP_Bool detectedredundant; /**< was detection of redundant constraints already performed? */ 214 215 SCIP_Longint maxnodes; /**< number of branch-and-bound nodes to solve an independent cumulative constraint (-1: no limit) */ 216 217 SCIP_DECL_SOLVECUMULATIVE((*solveCumulative)); /**< method to use a single cumulative condition */ 218 219 /* statistic values which are collected if SCIP_STATISTIC is defined */ 220 #ifdef SCIP_STATISTIC 221 SCIP_Longint nlbtimetable; /**< number of times the lower bound was tightened by the time-table propagator */ 222 SCIP_Longint nubtimetable; /**< number of times the upper bound was tightened by the time-table propagator */ 223 SCIP_Longint ncutofftimetable; /**< number of times the a cutoff was detected due to time-table propagator */ 224 SCIP_Longint nlbedgefinder; /**< number of times the lower bound was tightened by the edge-finder propagator */ 225 SCIP_Longint nubedgefinder; /**< number of times the upper bound was tightened by the edge-finder propagator */ 226 SCIP_Longint ncutoffedgefinder; /**< number of times the a cutoff was detected due to edge-finder propagator */ 227 SCIP_Longint ncutoffoverload; /**< number of times the a cutoff was detected due to overload checking via edge-finding */ 228 SCIP_Longint nlbTTEF; /**< number of times the lower bound was tightened by time-table edge-finding */ 229 SCIP_Longint nubTTEF; /**< number of times the upper bound was tightened by time-table edge-finding */ 230 SCIP_Longint ncutoffoverloadTTEF;/**< number of times the a cutoff was detected due to overload checking via time-table edge-finding */ 231 232 int nirrelevantjobs; /**< number of time a irrelevant/redundant jobs was removed form a constraint */ 233 int nalwaysruns; /**< number of time a job removed form a constraint which run completely during the effective horizon */ 234 int nremovedlocks; /**< number of times a up or down lock was removed */ 235 int ndualfixs; /**< number of times a dual fix was performed by a single constraint */ 236 int ndecomps; /**< number of times a constraint was decomposed */ 237 int ndualbranchs; /**< number of times a dual branch was discoverd and applicable via probing */ 238 int nallconsdualfixs; /**< number of times a dual fix was performed due to knowledge of all cumulative constraints */ 239 int naddedvarbounds; /**< number of added variable bounds constraints */ 240 int naddeddisjunctives; /**< number of added disjunctive constraints */ 241 242 SCIP_Bool iscopy; /**< Boolean to store if constraint handler is part of a copy */ 243 #endif 244 }; 245 246 /**@name Inference Information Methods 247 * 248 * An inference information can be passed with each domain reduction to SCIP. This information is passed back to the 249 * constraint handler if the corresponding bound change has to be explained. It can be used to store information which 250 * help to construct a reason/explanation for a bound change. The inference information is limited to size of integer. 251 * 252 * In case of the cumulative constraint handler we store the used propagation algorithms for that particular bound 253 * change and the earliest start and latest completion time of all jobs in the conflict set. 254 * 255 * @{ 256 */ 257 258 /** Propagation rules */ 259 enum Proprule 260 { 261 PROPRULE_0_INVALID = 0, /**< invalid inference information */ 262 PROPRULE_1_CORETIMES = 1, /**< core-time propagator */ 263 PROPRULE_2_EDGEFINDING = 2, /**< edge-finder */ 264 PROPRULE_3_TTEF = 3 /**< time-table edeg-finding */ 265 }; 266 typedef enum Proprule PROPRULE; 267 268 /** inference information */ 269 struct InferInfo 270 { 271 union 272 { 273 /** struct to use the inference information */ 274 struct 275 { 276 unsigned int proprule:2; /**< propagation rule that was applied */ 277 unsigned int data1:15; /**< data field one */ 278 unsigned int data2:15; /**< data field two */ 279 } asbits; 280 int asint; /**< inference information as a single int value */ 281 } val; 282 }; 283 typedef struct InferInfo INFERINFO; 284 285 /** converts an integer into an inference information */ 286 static 287 INFERINFO intToInferInfo( 288 int i /**< integer to convert */ 289 ) 290 { 291 INFERINFO inferinfo; 292 293 inferinfo.val.asint = i; 294 295 return inferinfo; 296 } 297 298 /** converts an inference information into an int */ 299 static 300 int inferInfoToInt( 301 INFERINFO inferinfo /**< inference information to convert */ 302 ) 303 { 304 return inferinfo.val.asint; 305 } 306 307 /** returns the propagation rule stored in the inference information */ 308 static 309 PROPRULE inferInfoGetProprule( 310 INFERINFO inferinfo /**< inference information to convert */ 311 ) 312 { 313 return (PROPRULE) inferinfo.val.asbits.proprule; 314 } 315 316 /** returns data field one of the inference information */ 317 static 318 int inferInfoGetData1( 319 INFERINFO inferinfo /**< inference information to convert */ 320 ) 321 { 322 return (int) inferinfo.val.asbits.data1; 323 } 324 325 /** returns data field two of the inference information */ 326 static 327 int inferInfoGetData2( 328 INFERINFO inferinfo /**< inference information to convert */ 329 ) 330 { 331 return (int) inferinfo.val.asbits.data2; 332 } 333 334 /** returns whether the inference information is valid */ 335 static 336 SCIP_Bool inferInfoIsValid( 337 INFERINFO inferinfo /**< inference information to convert */ 338 ) 339 { 340 return (inferinfo.val.asint != 0); 341 } 342 343 344 /** constructs an inference information out of a propagation rule, an earliest start and a latest completion time */ 345 static 346 INFERINFO getInferInfo( 347 PROPRULE proprule, /**< propagation rule that deduced the value */ 348 int data1, /**< data field one */ 349 int data2 /**< data field two */ 350 ) 351 { 352 INFERINFO inferinfo; 353 354 /* check that the data members are in the range of the available bits */ 355 if( proprule == PROPRULE_0_INVALID || data1 < 0 || data1 >= (1<<15) || data2 < 0 || data2 >= (1<<15) ) 356 { 357 inferinfo.val.asint = 0; 358 assert(inferInfoGetProprule(inferinfo) == PROPRULE_0_INVALID); 359 assert(inferInfoIsValid(inferinfo) == FALSE); 360 } 361 else 362 { 363 inferinfo.val.asbits.proprule = proprule; /*lint !e641*/ 364 inferinfo.val.asbits.data1 = (unsigned int) data1; /*lint !e732*/ 365 inferinfo.val.asbits.data2 = (unsigned int) data2; /*lint !e732*/ 366 assert(inferInfoIsValid(inferinfo) == TRUE); 367 } 368 369 return inferinfo; 370 } 371 372 /**@} */ 373 374 /* 375 * Local methods 376 */ 377 378 /**@name Miscellaneous Methods 379 * 380 * @{ 381 */ 382 383 #ifndef NDEBUG 384 385 /** compute the core of a job which lies in certain interval [begin, end) */ 386 static 387 SCIP_Longint computeCoreWithInterval( 388 int begin, /**< begin of the interval */ 389 int end, /**< end of the interval */ 390 int ect, /**< earliest completion time */ 391 int lst /**< latest start time */ 392 ) 393 { 394 int core; 395 396 core = MAX(0, MIN(end, ect) - MAX(lst, begin)); 397 398 return core; 399 } 400 #else 401 #define computeCoreWithInterval(begin, end, ect, lst) (MAX(0, MIN((end), (ect)) - MAX((lst), (begin)))) 402 #endif 403 404 /** returns the implied earliest start time */ /*lint -e{715}*/ 405 static 406 SCIP_RETCODE computeImpliedEst( 407 SCIP* scip, /**< SCIP data structure */ 408 SCIP_VAR* var, /**< variable for which the implied est should be returned */ 409 SCIP_HASHMAP* addedvars, /**< hash map containig the variable which are already added */ 410 int* est /**< pointer to store the implied earliest start time */ 411 ) 412 { /*lint --e{715}*/ 413 #if 0 414 SCIP_VAR** vbdvars; 415 SCIP_VAR* vbdvar; 416 SCIP_Real* vbdcoefs; 417 SCIP_Real* vbdconsts; 418 void* image; 419 int nvbdvars; 420 int v; 421 #endif 422 423 (*est) = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(var)); 424 425 #if 0 426 /* the code contains a bug; we need to check if an implication forces that the jobs do not run in parallel */ 427 428 nvbdvars = SCIPvarGetNVlbs(var); 429 vbdvars = SCIPvarGetVlbVars(var); 430 vbdcoefs = SCIPvarGetVlbCoefs(var); 431 vbdconsts = SCIPvarGetVlbConstants(var); 432 433 for( v = 0; v < nvbdvars; ++v ) 434 { 435 vbdvar = vbdvars[v]; 436 assert(vbdvar != NULL); 437 438 image = SCIPhashmapGetImage(addedvars, (void*)vbdvar); 439 440 if( image != NULL && SCIPisEQ(scip, vbdcoefs[v], 1.0 ) ) 441 { 442 int duration; 443 int vbdconst; 444 445 duration = (int)(size_t)image; 446 vbdconst = SCIPconvertRealToInt(scip, vbdconsts[v]); 447 448 SCIPdebugMsg(scip, "check implication <%s>[%g,%g] >= <%s>[%g,%g] + <%g>\n", 449 SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var), 450 SCIPvarGetName(vbdvar), SCIPvarGetLbLocal(vbdvar), SCIPvarGetUbLocal(vbdvar), vbdconsts[v]); 451 452 if( duration >= vbdconst ) 453 { 454 int impliedest; 455 456 impliedest = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(vbdvar)) + duration; 457 458 if( (*est) < impliedest ) 459 { 460 (*est) = impliedest; 461 462 SCIP_CALL( SCIPhashmapRemove(addedvars, (void*)vbdvar) ); 463 } 464 } 465 } 466 } 467 #endif 468 469 return SCIP_OKAY; 470 } 471 472 /** returns the implied latest completion time */ /*lint -e{715}*/ 473 static 474 SCIP_RETCODE computeImpliedLct( 475 SCIP* scip, /**< SCIP data structure */ 476 SCIP_VAR* var, /**< variable for which the implied est should be returned */ 477 int duration, /**< duration of the given job */ 478 SCIP_HASHMAP* addedvars, /**< hash map containig the variable which are already added */ 479 int* lct /**< pointer to store the implied latest completion time */ 480 ) 481 { /*lint --e{715}*/ 482 #if 0 483 SCIP_VAR** vbdvars; 484 SCIP_VAR* vbdvar; 485 SCIP_Real* vbdcoefs; 486 SCIP_Real* vbdconsts; 487 int nvbdvars; 488 int v; 489 #endif 490 491 (*lct) = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(var)) + duration; 492 493 #if 0 494 /* the code contains a bug; we need to check if an implication forces that the jobs do not run in parallel */ 495 496 nvbdvars = SCIPvarGetNVubs(var); 497 vbdvars = SCIPvarGetVubVars(var); 498 vbdcoefs = SCIPvarGetVubCoefs(var); 499 vbdconsts = SCIPvarGetVubConstants(var); 500 501 for( v = 0; v < nvbdvars; ++v ) 502 { 503 vbdvar = vbdvars[v]; 504 assert(vbdvar != NULL); 505 506 if( SCIPhashmapExists(addedvars, (void*)vbdvar) && SCIPisEQ(scip, vbdcoefs[v], 1.0 ) ) 507 { 508 int vbdconst; 509 510 vbdconst = SCIPconvertRealToInt(scip, -vbdconsts[v]); 511 512 SCIPdebugMsg(scip, "check implication <%s>[%g,%g] <= <%s>[%g,%g] + <%g>\n", 513 SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var), 514 SCIPvarGetName(vbdvar), SCIPvarGetLbLocal(vbdvar), SCIPvarGetUbLocal(vbdvar), vbdconsts[v]); 515 516 if( duration >= -vbdconst ) 517 { 518 int impliedlct; 519 520 impliedlct = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(vbdvar)); 521 522 if( (*lct) > impliedlct ) 523 { 524 (*lct) = impliedlct; 525 526 SCIP_CALL( SCIPhashmapRemove(addedvars, (void*)vbdvar) ); 527 } 528 } 529 } 530 } 531 #endif 532 533 return SCIP_OKAY; 534 } 535 536 /** collects all necessary binary variables to represent the jobs which can be active at time point of interest */ 537 static 538 SCIP_RETCODE collectBinaryVars( 539 SCIP* scip, /**< SCIP data structure */ 540 SCIP_CONSDATA* consdata, /**< constraint data */ 541 SCIP_VAR*** vars, /**< pointer to the array to store the binary variables */ 542 int** coefs, /**< pointer to store the coefficients */ 543 int* nvars, /**< number if collect binary variables */ 544 int* startindices, /**< permutation with rspect to the start times */ 545 int curtime, /**< current point in time */ 546 int nstarted, /**< number of jobs that start before the curtime or at curtime */ 547 int nfinished /**< number of jobs that finished before curtime or at curtime */ 548 ) 549 { 550 int nrowvars; 551 int startindex; 552 int size; 553 554 size = 10; 555 nrowvars = 0; 556 startindex = nstarted - 1; 557 558 SCIP_CALL( SCIPallocBufferArray(scip, vars, size) ); 559 SCIP_CALL( SCIPallocBufferArray(scip, coefs, size) ); 560 561 /* search for the (nstarted - nfinished) jobs which are active at curtime */ 562 while( nstarted - nfinished > nrowvars ) 563 { 564 SCIP_VAR* var; 565 int endtime; 566 int duration; 567 int demand; 568 int varidx; 569 570 /* collect job information */ 571 varidx = startindices[startindex]; 572 assert(varidx >= 0 && varidx < consdata->nvars); 573 574 var = consdata->vars[varidx]; 575 duration = consdata->durations[varidx]; 576 demand = consdata->demands[varidx]; 577 assert(var != NULL); 578 579 endtime = SCIPconvertRealToInt(scip, SCIPvarGetUbGlobal(var)) + duration; 580 581 /* check the end time of this job is larger than the curtime; in this case the job is still running */ 582 if( endtime > curtime ) 583 { 584 SCIP_VAR** binvars; 585 SCIP_Real* vals; 586 int nbinvars; 587 int start; 588 int end; 589 int b; 590 591 /* check if the linking constraints exists */ 592 assert(SCIPexistsConsLinking(scip, var)); 593 assert(SCIPgetConsLinking(scip, var) != NULL); 594 assert(SCIPgetConsLinking(scip, var) == consdata->linkingconss[varidx]); 595 596 /* collect linking constraint information */ 597 SCIP_CALL( SCIPgetBinvarsLinking(scip, consdata->linkingconss[varidx], &binvars, &nbinvars) ); 598 vals = SCIPgetValsLinking(scip, consdata->linkingconss[varidx]); 599 600 start = curtime - duration + 1; 601 end = MIN(curtime, endtime - duration); 602 603 for( b = 0; b < nbinvars; ++b ) 604 { 605 if( vals[b] < start ) 606 continue; 607 608 if( vals[b] > end ) 609 break; 610 611 assert(binvars[b] != NULL); 612 613 /* ensure array proper array size */ 614 if( size == *nvars ) 615 { 616 size *= 2; 617 SCIP_CALL( SCIPreallocBufferArray(scip, vars, size) ); 618 SCIP_CALL( SCIPreallocBufferArray(scip, coefs, size) ); 619 } 620 621 (*vars)[*nvars] = binvars[b]; 622 (*coefs)[*nvars] = demand; 623 (*nvars)++; 624 } 625 nrowvars++; 626 } 627 628 startindex--; 629 } 630 631 return SCIP_OKAY; 632 } 633 634 /** collect all integer variable which belong to jobs which can run at the point of interest */ 635 static 636 SCIP_RETCODE collectIntVars( 637 SCIP* scip, /**< SCIP data structure */ 638 SCIP_CONSDATA* consdata, /**< constraint data */ 639 SCIP_VAR*** activevars, /**< jobs that are currently running */ 640 int* startindices, /**< permutation with rspect to the start times */ 641 int curtime, /**< current point in time */ 642 int nstarted, /**< number of jobs that start before the curtime or at curtime */ 643 int nfinished, /**< number of jobs that finished before curtime or at curtime */ 644 SCIP_Bool lower, /**< shall cuts be created due to lower or upper bounds? */ 645 int* lhs /**< lhs for the new row sum of lbs + minoffset */ 646 ) 647 { 648 SCIP_VAR* var; 649 int startindex; 650 int endtime; 651 int duration; 652 int starttime; 653 654 int varidx; 655 int sumofstarts; 656 int mindelta; 657 int counter; 658 659 assert(curtime >= consdata->hmin); 660 assert(curtime < consdata->hmax); 661 662 counter = 0; 663 sumofstarts = 0; 664 665 mindelta = INT_MAX; 666 667 startindex = nstarted - 1; 668 669 /* search for the (nstarted - nfinished) jobs which are active at curtime */ 670 while( nstarted - nfinished > counter ) 671 { 672 assert(startindex >= 0); 673 674 /* collect job information */ 675 varidx = startindices[startindex]; 676 assert(varidx >= 0 && varidx < consdata->nvars); 677 678 var = consdata->vars[varidx]; 679 duration = consdata->durations[varidx]; 680 assert(duration > 0); 681 assert(var != NULL); 682 683 if( lower ) 684 starttime = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(var)); 685 else 686 starttime = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(var)); 687 688 endtime = MIN(starttime + duration, consdata->hmax); 689 690 /* check the end time of this job is larger than the curtime; in this case the job is still running */ 691 if( endtime > curtime ) 692 { 693 (*activevars)[counter] = var; 694 sumofstarts += starttime; 695 mindelta = MIN(mindelta, endtime - curtime); /* this amount of schifting holds for lb and ub */ 696 counter++; 697 } 698 699 startindex--; 700 } 701 702 assert(mindelta > 0); 703 *lhs = lower ? sumofstarts + mindelta : sumofstarts - mindelta; 704 705 return SCIP_OKAY; 706 } 707 708 /** initialize the sorted event point arrays */ 709 static 710 void createSortedEventpoints( 711 SCIP* scip, /**< SCIP data structure */ 712 int nvars, /**< number of start time variables (activities) */ 713 SCIP_VAR** vars, /**< array of start time variables */ 714 int* durations, /**< array of durations per start time variable */ 715 int* starttimes, /**< array to store sorted start events */ 716 int* endtimes, /**< array to store sorted end events */ 717 int* startindices, /**< permutation with rspect to the start times */ 718 int* endindices, /**< permutation with rspect to the end times */ 719 SCIP_Bool local /**< shall local bounds be used */ 720 ) 721 { 722 SCIP_VAR* var; 723 int j; 724 725 assert(vars != NULL || nvars == 0); 726 727 /* assign variables, start and endpoints to arrays */ 728 for ( j = 0; j < nvars; ++j ) 729 { 730 assert(vars != NULL); 731 732 var = vars[j]; 733 assert(var != NULL); 734 735 if( local ) 736 starttimes[j] = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(var)); 737 else 738 starttimes[j] = SCIPconvertRealToInt(scip, SCIPvarGetLbGlobal(var)); 739 740 startindices[j] = j; 741 742 if( local ) 743 endtimes[j] = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(var)) + durations[j]; 744 else 745 endtimes[j] = SCIPconvertRealToInt(scip, SCIPvarGetUbGlobal(var)) + durations[j]; 746 747 endindices[j] = j; 748 } 749 750 /* sort the arrays not-decreasing according to startsolvalues and endsolvalues (and sort the indices in the same way) */ 751 SCIPsortIntInt(starttimes, startindices, j); 752 SCIPsortIntInt(endtimes, endindices, j); 753 } 754 755 /** initialize the sorted event point arrays w.r.t. the given primal solutions */ 756 static 757 void createSortedEventpointsSol( 758 SCIP* scip, /**< SCIP data structure */ 759 SCIP_SOL* sol, /**< solution */ 760 int nvars, /**< number of start time variables (activities) */ 761 SCIP_VAR** vars, /**< array of start time variables */ 762 int* durations, /**< array of durations per start time variable */ 763 int* starttimes, /**< array to store sorted start events */ 764 int* endtimes, /**< array to store sorted end events */ 765 int* startindices, /**< permutation with rspect to the start times */ 766 int* endindices /**< permutation with rspect to the end times */ 767 ) 768 { 769 SCIP_VAR* var; 770 int j; 771 772 assert(vars != NULL || nvars == 0); 773 774 /* assign variables, start and endpoints to arrays */ 775 for ( j = 0; j < nvars; ++j ) 776 { 777 assert(vars != NULL); 778 779 var = vars[j]; 780 assert(var != NULL); 781 782 starttimes[j] = SCIPconvertRealToInt(scip, SCIPgetSolVal(scip, sol, var)); 783 startindices[j] = j; 784 785 endtimes[j] = SCIPconvertRealToInt(scip, SCIPgetSolVal(scip, sol, var)) + durations[j]; 786 endindices[j] = j; 787 } 788 789 /* sort the arrays not-decreasing according to startsolvalues and endsolvalues (and sort the indices in the same way) */ 790 SCIPsortIntInt(starttimes, startindices, j); 791 SCIPsortIntInt(endtimes, endindices, j); 792 } 793 794 /** initialize the sorted event point arrays 795 * 796 * @todo Check the separation process! 797 */ 798 static 799 void createSelectedSortedEventpointsSol( 800 SCIP* scip, /**< SCIP data structure */ 801 SCIP_CONSDATA* consdata, /**< constraint data */ 802 SCIP_SOL* sol, /**< primal CIP solution, NULL for current LP solution */ 803 int* starttimes, /**< array to store sorted start events */ 804 int* endtimes, /**< array to store sorted end events */ 805 int* startindices, /**< permutation with rspect to the start times */ 806 int* endindices, /**< permutation with rspect to the end times */ 807 int* nvars, /**< number of variables that are integral */ 808 SCIP_Bool lower /**< shall the constraints be derived for lower or upper bounds? */ 809 ) 810 { 811 SCIP_VAR* var; 812 int tmpnvars; 813 int j; 814 815 tmpnvars = consdata->nvars; 816 *nvars = 0; 817 818 /* assign variables, start and endpoints to arrays */ 819 for ( j = 0; j < tmpnvars; ++j ) 820 { 821 var = consdata->vars[j]; 822 assert(var != NULL); 823 assert(consdata->durations[j] > 0); 824 assert(consdata->demands[j] > 0); 825 826 if( lower ) 827 { 828 /* only consider jobs that are at their lower or upper bound */ 829 if( !SCIPisFeasIntegral(scip, SCIPgetSolVal(scip, sol, var)) 830 || !SCIPisFeasEQ(scip, SCIPgetSolVal(scip, sol, var), SCIPvarGetLbLocal(var)) ) 831 continue; 832 833 starttimes[*nvars] = SCIPconvertRealToInt(scip, SCIPgetSolVal(scip, sol, var)); 834 startindices[*nvars] = j; 835 836 endtimes[*nvars] = starttimes[*nvars] + consdata->durations[j]; 837 endindices[*nvars] = j; 838 839 SCIPdebugMsg(scip, "%d: variable <%s>[%g,%g] (sol %g, duration %d) starttime %d, endtime = %d, demand = %d\n", 840 *nvars, SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var), SCIPgetSolVal(scip, sol, var), 841 consdata->durations[j], 842 starttimes[*nvars], starttimes[*nvars] + consdata->durations[startindices[*nvars]], 843 consdata->demands[startindices[*nvars]]); 844 845 (*nvars)++; 846 } 847 else 848 { 849 if( !SCIPisFeasIntegral(scip, SCIPgetSolVal(scip, sol, var)) 850 || !SCIPisFeasEQ(scip, SCIPgetSolVal(scip, sol, var), SCIPvarGetUbLocal(var)) ) 851 continue; 852 853 starttimes[*nvars] = SCIPconvertRealToInt(scip, SCIPgetSolVal(scip, sol, var)); 854 startindices[*nvars] = j; 855 856 endtimes[*nvars] = starttimes[*nvars] + consdata->durations[j]; 857 endindices[*nvars] = j; 858 859 SCIPdebugMsg(scip, "%d: variable <%s>[%g,%g] (sol %g, duration %d) starttime %d, endtime = %d, demand = %d\n", 860 *nvars, SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var), SCIPgetSolVal(scip, sol, var), 861 consdata->durations[j], 862 starttimes[*nvars], starttimes[*nvars] + consdata->durations[startindices[*nvars]], 863 consdata->demands[startindices[*nvars]]); 864 865 (*nvars)++; 866 } 867 } 868 869 /* sort the arrays not-decreasing according to startsolvalues and endsolvalues (and sort the indices in the same way) */ 870 SCIPsortIntInt(starttimes, startindices, *nvars); 871 SCIPsortIntInt(endtimes, endindices, *nvars); 872 873 #ifdef SCIP_DEBUG 874 SCIPdebugMsg(scip, "sorted output %d\n", *nvars); 875 876 for ( j = 0; j < *nvars; ++j ) 877 { 878 SCIPdebugMsg(scip, "%d: job[%d] starttime %d, endtime = %d, demand = %d\n", j, 879 startindices[j], starttimes[j], starttimes[j] + consdata->durations[startindices[j]], 880 consdata->demands[startindices[j]]); 881 } 882 883 for ( j = 0; j < *nvars; ++j ) 884 { 885 SCIPdebugMsg(scip, "%d: job[%d] endtime %d, demand = %d\n", j, endindices[j], endtimes[j], 886 consdata->demands[endindices[j]]); 887 } 888 #endif 889 } 890 891 #ifdef SCIP_STATISTIC 892 /** this method checks for relevant intervals for energetic reasoning */ 893 static 894 SCIP_RETCODE computeRelevantEnergyIntervals( 895 SCIP* scip, /**< SCIP data structure */ 896 int nvars, /**< number of start time variables (activities) */ 897 SCIP_VAR** vars, /**< array of start time variables */ 898 int* durations, /**< array of durations */ 899 int* demands, /**< array of demands */ 900 int capacity, /**< cumulative capacity */ 901 int hmin, /**< left bound of time axis to be considered (including hmin) */ 902 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 903 int** timepoints, /**< array to store relevant points in time */ 904 SCIP_Real** cumulativedemands, /**< array to store the estimated cumulative demand for each point in time */ 905 int* ntimepoints, /**< pointer to store the number of timepoints */ 906 int* maxdemand, /**< pointer to store maximum over all demands */ 907 SCIP_Real* minfreecapacity /**< pointer to store the minimum free capacity */ 908 ) 909 { 910 int* starttimes; /* stores when each job is starting */ 911 int* endtimes; /* stores when each job ends */ 912 int* startindices; /* we will sort the startsolvalues, thus we need to know wich index of a job it corresponds to */ 913 int* endindices; /* we will sort the endsolvalues, thus we need to know wich index of a job it corresponds to */ 914 915 SCIP_Real totaldemand; 916 int curtime; /* point in time which we are just checking */ 917 int endindex; /* index of endsolvalues with: endsolvalues[endindex] > curtime */ 918 919 int j; 920 921 assert( scip != NULL ); 922 assert(durations != NULL); 923 assert(demands != NULL); 924 assert(capacity >= 0); 925 926 /* if no activities are associated with this cumulative then this constraint is redundant */ 927 if( nvars == 0 ) 928 return SCIP_OKAY; 929 930 assert(vars != NULL); 931 932 SCIP_CALL( SCIPallocBufferArray(scip, &starttimes, nvars) ); 933 SCIP_CALL( SCIPallocBufferArray(scip, &endtimes, nvars) ); 934 SCIP_CALL( SCIPallocBufferArray(scip, &startindices, nvars) ); 935 SCIP_CALL( SCIPallocBufferArray(scip, &endindices, nvars) ); 936 937 /* create event point arrays */ 938 createSortedEventpoints(scip, nvars, vars, durations, starttimes, endtimes, startindices, endindices, TRUE); 939 940 endindex = 0; 941 totaldemand = 0.0; 942 943 *ntimepoints = 0; 944 (*timepoints)[0] = starttimes[0]; 945 (*cumulativedemands)[0] = 0; 946 *maxdemand = 0; 947 948 /* check each startpoint of a job whether the capacity is kept or not */ 949 for( j = 0; j < nvars; ++j ) 950 { 951 int lct; 952 int idx; 953 954 curtime = starttimes[j]; 955 956 if( curtime >= hmax ) 957 break; 958 959 /* free all capacity usages of jobs the are no longer running */ 960 while( endindex < nvars && endtimes[endindex] <= curtime ) 961 { 962 int est; 963 964 if( (*timepoints)[*ntimepoints] < endtimes[endindex] ) 965 { 966 (*ntimepoints)++; 967 (*timepoints)[*ntimepoints] = endtimes[endindex]; 968 (*cumulativedemands)[*ntimepoints] = 0; 969 } 970 971 idx = endindices[endindex]; 972 est = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(vars[idx])); 973 totaldemand -= (SCIP_Real) demands[idx] * durations[idx] / (endtimes[endindex] - est); 974 endindex++; 975 976 (*cumulativedemands)[*ntimepoints] = totaldemand; 977 } 978 979 idx = startindices[j]; 980 lct = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(vars[idx]) + durations[idx]); 981 totaldemand += (SCIP_Real) demands[idx] * durations[idx] / (lct - starttimes[j]); 982 983 if( (*timepoints)[*ntimepoints] < curtime ) 984 { 985 (*ntimepoints)++; 986 (*timepoints)[*ntimepoints] = curtime; 987 (*cumulativedemands)[*ntimepoints] = 0; 988 } 989 990 (*cumulativedemands)[*ntimepoints] = totaldemand; 991 992 /* add the relative capacity requirements for all job which start at the curtime */ 993 while( j+1 < nvars && starttimes[j+1] == curtime ) 994 { 995 ++j; 996 idx = startindices[j]; 997 lct = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(vars[idx]) + durations[idx]); 998 totaldemand += (SCIP_Real) demands[idx] * durations[idx] / (lct - starttimes[j]); 999 1000 (*cumulativedemands)[*ntimepoints] = totaldemand; 1001 } 1002 } /*lint --e{850}*/ 1003 1004 /* free all capacity usages of jobs that are no longer running */ 1005 while( endindex < nvars/* && endtimes[endindex] < hmax*/) 1006 { 1007 int est; 1008 int idx; 1009 1010 if( (*timepoints)[*ntimepoints] < endtimes[endindex] ) 1011 { 1012 (*ntimepoints)++; 1013 (*timepoints)[*ntimepoints] = endtimes[endindex]; 1014 (*cumulativedemands)[*ntimepoints] = 0; 1015 } 1016 1017 idx = endindices[endindex]; 1018 est = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(vars[idx])); 1019 totaldemand -= (SCIP_Real) demands[idx] * durations[idx] / (endtimes[endindex] - est); 1020 (*cumulativedemands)[*ntimepoints] = totaldemand; 1021 1022 ++endindex; 1023 } 1024 1025 (*ntimepoints)++; 1026 /* compute minimum free capacity */ 1027 (*minfreecapacity) = INT_MAX; 1028 for( j = 0; j < *ntimepoints; ++j ) 1029 { 1030 if( (*timepoints)[j] >= hmin && (*timepoints)[j] < hmax ) 1031 *minfreecapacity = MIN( *minfreecapacity, (SCIP_Real)capacity - (*cumulativedemands)[j] ); 1032 } 1033 1034 /* free buffer arrays */ 1035 SCIPfreeBufferArray(scip, &endindices); 1036 SCIPfreeBufferArray(scip, &startindices); 1037 SCIPfreeBufferArray(scip, &endtimes); 1038 SCIPfreeBufferArray(scip, &starttimes); 1039 1040 return SCIP_OKAY; 1041 } 1042 1043 /** evaluates the cumulativeness and disjointness factor of a cumulative constraint */ 1044 static 1045 SCIP_RETCODE evaluateCumulativeness( 1046 SCIP* scip, /**< pointer to scip */ 1047 SCIP_CONS* cons /**< cumulative constraint */ 1048 ) 1049 { 1050 SCIP_CONSDATA* consdata; 1051 int nvars; 1052 int v; 1053 int capacity; 1054 1055 /* output values: */ 1056 SCIP_Real disjfactor2; /* (peak-capacity)/capacity * (large demands/nvars_t) */ 1057 SCIP_Real cumfactor1; 1058 SCIP_Real resstrength1; /* overall strength */ 1059 SCIP_Real resstrength2; /* timepoint wise maximum */ 1060 1061 /* helpful variables: */ 1062 SCIP_Real globalpeak; 1063 SCIP_Real globalmaxdemand; 1064 1065 /* get constraint data structure */ 1066 consdata = SCIPconsGetData(cons); 1067 assert(consdata != NULL); 1068 1069 nvars = consdata->nvars; 1070 capacity = consdata->capacity; 1071 globalpeak = 0.0; 1072 globalmaxdemand = 0.0; 1073 1074 disjfactor2 = 0.0; 1075 cumfactor1 = 0.0; 1076 resstrength2 = 0.0; 1077 1078 /* check each starting time (==each job, but inefficient) */ 1079 for( v = 0; v < nvars; ++v ) 1080 { 1081 SCIP_Real peak; 1082 SCIP_Real maxdemand; 1083 SCIP_Real deltademand; 1084 int ndemands; 1085 int nlarge; 1086 1087 int timepoint; 1088 int j; 1089 timepoint = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(consdata->vars[v])); 1090 peak = consdata->demands[v]; 1091 ndemands = 1; 1092 maxdemand = 0; 1093 nlarge = 0; 1094 1095 if( consdata->demands[v] > capacity / 3 ) 1096 nlarge++; 1097 1098 for( j = 0; j < nvars; ++j ) 1099 { 1100 int lb; 1101 1102 if( j == v ) 1103 continue; 1104 1105 maxdemand = 0.0; 1106 lb = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(consdata->vars[j])); 1107 1108 if( lb <= timepoint && lb + consdata->durations[j] > timepoint ) 1109 { 1110 peak += consdata->demands[j]; 1111 ndemands++; 1112 1113 if( consdata->demands[j] > consdata->capacity / 3 ) 1114 nlarge++; 1115 } 1116 } 1117 1118 deltademand = (SCIP_Real)peak / (SCIP_Real)ndemands; 1119 globalpeak = MAX(globalpeak, peak); 1120 globalmaxdemand = MAX(globalmaxdemand, maxdemand); 1121 1122 if( peak > capacity ) 1123 { 1124 disjfactor2 = MAX( disjfactor2, (peak-(SCIP_Real)capacity)/peak * (nlarge/(SCIP_Real)ndemands) ); 1125 cumfactor1 = MAX( cumfactor1, (peak-capacity)/peak * (capacity-deltademand)/(SCIP_Real)capacity ); 1126 resstrength2 = MAX(resstrength2, (capacity-maxdemand)/(peak-maxdemand) ); 1127 } 1128 } 1129 1130 resstrength1 = (capacity-globalmaxdemand) / (globalpeak-globalmaxdemand); 1131 1132 consdata->maxpeak = SCIPconvertRealToInt(scip, globalpeak); 1133 consdata->disjfactor2 = disjfactor2; 1134 consdata->cumfactor1 = cumfactor1; 1135 consdata->resstrength2 = resstrength2; 1136 consdata->resstrength1 = resstrength1; 1137 1138 /* get estimated res strength */ 1139 { 1140 int* timepoints; 1141 SCIP_Real* estimateddemands; 1142 int ntimepoints; 1143 int maxdemand; 1144 SCIP_Real minfreecapacity; 1145 1146 SCIP_CALL( SCIPallocBufferArray(scip, &timepoints, 2*nvars) ); 1147 SCIP_CALL( SCIPallocBufferArray(scip, &estimateddemands, 2*nvars) ); 1148 1149 ntimepoints = 0; 1150 minfreecapacity = INT_MAX; 1151 1152 SCIP_CALL( computeRelevantEnergyIntervals(scip, nvars, consdata->vars, 1153 consdata->durations, consdata->demands, 1154 capacity, consdata->hmin, consdata->hmax, &timepoints, &estimateddemands, 1155 &ntimepoints, &maxdemand, &minfreecapacity) ); 1156 1157 /* free buffer arrays */ 1158 SCIPfreeBufferArray(scip, &estimateddemands); 1159 SCIPfreeBufferArray(scip, &timepoints); 1160 1161 consdata->estimatedstrength = (SCIP_Real)(capacity - minfreecapacity) / (SCIP_Real) capacity; 1162 } 1163 1164 SCIPstatisticPrintf("cumulative constraint<%s>: DISJ1=%g, DISJ2=%g, CUM=%g, RS1 = %g, RS2 = %g, EST = %g\n", 1165 SCIPconsGetName(cons), consdata->disjfactor1, disjfactor2, cumfactor1, resstrength1, resstrength2, 1166 consdata->estimatedstrength); 1167 1168 return SCIP_OKAY; 1169 } 1170 #endif 1171 1172 /** gets the active variables together with the constant */ 1173 static 1174 SCIP_RETCODE getActiveVar( 1175 SCIP* scip, /**< SCIP data structure */ 1176 SCIP_VAR** var, /**< pointer to store the active variable */ 1177 int* scalar, /**< pointer to store the scalar */ 1178 int* constant /**< pointer to store the constant */ 1179 ) 1180 { 1181 if( !SCIPvarIsActive(*var) ) 1182 { 1183 SCIP_Real realscalar; 1184 SCIP_Real realconstant; 1185 1186 realscalar = 1.0; 1187 realconstant = 0.0; 1188 1189 assert(SCIPvarGetStatus(*var) == SCIP_VARSTATUS_AGGREGATED); 1190 1191 /* transform variable to active variable */ 1192 SCIP_CALL( SCIPgetProbvarSum(scip, var, &realscalar, &realconstant) ); 1193 assert(!SCIPisZero(scip, realscalar)); 1194 assert(SCIPvarIsActive(*var)); 1195 1196 if( realconstant < 0.0 ) 1197 (*constant) = -SCIPconvertRealToInt(scip, -realconstant); 1198 else 1199 (*constant) = SCIPconvertRealToInt(scip, realconstant); 1200 1201 if( realscalar < 0.0 ) 1202 (*scalar) = -SCIPconvertRealToInt(scip, -realscalar); 1203 else 1204 (*scalar) = SCIPconvertRealToInt(scip, realscalar); 1205 } 1206 else 1207 { 1208 (*scalar) = 1; 1209 (*constant) = 0; 1210 } 1211 1212 assert(*scalar != 0); 1213 1214 return SCIP_OKAY; 1215 } 1216 1217 /** computes the total energy of all jobs */ 1218 static 1219 SCIP_Longint computeTotalEnergy( 1220 int* durations, /**< array of job durations */ 1221 int* demands, /**< array of job demands */ 1222 int njobs /**< number of jobs */ 1223 ) 1224 { 1225 SCIP_Longint energy; 1226 int j; 1227 1228 energy = 0; 1229 1230 for( j = 0; j < njobs; ++j ) 1231 energy += (SCIP_Longint) durations[j] * demands[j]; 1232 1233 return energy; 1234 } 1235 1236 /**@} */ 1237 1238 /**@name Default method to solve a cumulative condition 1239 * 1240 * @{ 1241 */ 1242 1243 /** setup and solve subscip to solve single cumulative condition */ 1244 static 1245 SCIP_RETCODE setupAndSolveCumulativeSubscip( 1246 SCIP* subscip, /**< subscip data structure */ 1247 SCIP_Real* objvals, /**< array of objective coefficients for each job (linear objective function), or NULL if none */ 1248 int* durations, /**< array of durations */ 1249 int* demands, /**< array of demands */ 1250 int njobs, /**< number of jobs (activities) */ 1251 int capacity, /**< cumulative capacity */ 1252 int hmin, /**< left bound of time axis to be considered (including hmin) */ 1253 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 1254 SCIP_Longint maxnodes, /**< maximum number of branch-and-bound nodes (-1: no limit) */ 1255 SCIP_Real timelimit, /**< time limit for solving in seconds */ 1256 SCIP_Real memorylimit, /**< memory limit for solving in mega bytes (MB) */ 1257 SCIP_Real* ests, /**< array of earliest start times for each job */ 1258 SCIP_Real* lsts, /**< array of latest start times for each job */ 1259 SCIP_Bool* infeasible, /**< pointer to store if the subproblem was infeasible */ 1260 SCIP_Bool* unbounded, /**< pointer to store if the problem is unbounded */ 1261 SCIP_Bool* solved, /**< pointer to store if the problem is solved (to optimality) */ 1262 SCIP_Bool* error /**< pointer to store if an error occurred */ 1263 ) 1264 { 1265 SCIP_VAR** subvars; 1266 SCIP_CONS* cons; 1267 1268 char name[SCIP_MAXSTRLEN]; 1269 int v; 1270 SCIP_RETCODE retcode; 1271 1272 assert(subscip != NULL); 1273 1274 /* copy all plugins */ 1275 SCIP_CALL( SCIPincludeDefaultPlugins(subscip) ); 1276 1277 /* create the subproblem */ 1278 SCIP_CALL( SCIPcreateProbBasic(subscip, "cumulative") ); 1279 1280 SCIP_CALL( SCIPallocBlockMemoryArray(subscip, &subvars, njobs) ); 1281 1282 /* create for each job a start time variable */ 1283 for( v = 0; v < njobs; ++v ) 1284 { 1285 SCIP_Real objval; 1286 1287 /* construct variable name */ 1288 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "job%d", v); 1289 1290 if( objvals == NULL ) 1291 objval = 0.0; 1292 else 1293 objval = objvals[v]; 1294 1295 SCIP_CALL( SCIPcreateVarBasic(subscip, &subvars[v], name, ests[v], lsts[v], objval, SCIP_VARTYPE_INTEGER) ); 1296 SCIP_CALL( SCIPaddVar(subscip, subvars[v]) ); 1297 } 1298 1299 /* create cumulative constraint */ 1300 SCIP_CALL( SCIPcreateConsBasicCumulative(subscip, &cons, "cumulative", 1301 njobs, subvars, durations, demands, capacity) ); 1302 1303 /* set effective horizon */ 1304 SCIP_CALL( SCIPsetHminCumulative(subscip, cons, hmin) ); 1305 SCIP_CALL( SCIPsetHmaxCumulative(subscip, cons, hmax) ); 1306 1307 /* add cumulative constraint */ 1308 SCIP_CALL( SCIPaddCons(subscip, cons) ); 1309 SCIP_CALL( SCIPreleaseCons(subscip, &cons) ); 1310 1311 /* set CP solver settings 1312 * 1313 * @note This "meta" setting has to be set first since this call overwrite all parameters including for example the 1314 * time limit. 1315 */ 1316 SCIP_CALL( SCIPsetEmphasis(subscip, SCIP_PARAMEMPHASIS_CPSOLVER, TRUE) ); 1317 1318 /* do not abort subproblem on CTRL-C */ 1319 SCIP_CALL( SCIPsetBoolParam(subscip, "misc/catchctrlc", FALSE) ); 1320 1321 /* disable output to console */ 1322 SCIP_CALL( SCIPsetIntParam(subscip, "display/verblevel", 0) ); 1323 1324 /* set limits for the subproblem */ 1325 SCIP_CALL( SCIPsetLongintParam(subscip, "limits/nodes", maxnodes) ); 1326 SCIP_CALL( SCIPsetRealParam(subscip, "limits/time", timelimit) ); 1327 SCIP_CALL( SCIPsetRealParam(subscip, "limits/memory", memorylimit) ); 1328 1329 /* forbid recursive call of heuristics and separators solving subMIPs */ 1330 SCIP_CALL( SCIPsetSubscipsOff(subscip, TRUE) ); 1331 1332 /* solve single cumulative constraint by branch and bound */ 1333 retcode = SCIPsolve(subscip); 1334 1335 if( retcode != SCIP_OKAY ) 1336 (*error) = TRUE; 1337 else 1338 { 1339 SCIPdebugMsg(subscip, "solved single cumulative condition with status %d\n", SCIPgetStatus(subscip)); 1340 1341 /* evaluated solution status */ 1342 switch( SCIPgetStatus(subscip) ) 1343 { 1344 case SCIP_STATUS_INFORUNBD: 1345 case SCIP_STATUS_INFEASIBLE: 1346 (*infeasible) = TRUE; 1347 (*solved) = TRUE; 1348 break; 1349 case SCIP_STATUS_UNBOUNDED: 1350 (*unbounded) = TRUE; 1351 (*solved) = TRUE; 1352 break; 1353 case SCIP_STATUS_OPTIMAL: 1354 { 1355 SCIP_SOL* sol; 1356 SCIP_Real solval; 1357 1358 sol = SCIPgetBestSol(subscip); 1359 assert(sol != NULL); 1360 1361 for( v = 0; v < njobs; ++v ) 1362 { 1363 solval = SCIPgetSolVal(subscip, sol, subvars[v]); 1364 1365 ests[v] = solval; 1366 lsts[v] = solval; 1367 } 1368 (*solved) = TRUE; 1369 break; 1370 } 1371 case SCIP_STATUS_NODELIMIT: 1372 case SCIP_STATUS_TOTALNODELIMIT: 1373 case SCIP_STATUS_TIMELIMIT: 1374 case SCIP_STATUS_MEMLIMIT: 1375 case SCIP_STATUS_USERINTERRUPT: 1376 case SCIP_STATUS_TERMINATE: 1377 /* transfer the global bound changes */ 1378 for( v = 0; v < njobs; ++v ) 1379 { 1380 ests[v] = SCIPvarGetLbGlobal(subvars[v]); 1381 lsts[v] = SCIPvarGetUbGlobal(subvars[v]); 1382 } 1383 (*solved) = FALSE; 1384 break; 1385 1386 case SCIP_STATUS_UNKNOWN: 1387 case SCIP_STATUS_STALLNODELIMIT: 1388 case SCIP_STATUS_GAPLIMIT: 1389 case SCIP_STATUS_SOLLIMIT: 1390 case SCIP_STATUS_BESTSOLLIMIT: 1391 case SCIP_STATUS_RESTARTLIMIT: 1392 SCIPerrorMessage("invalid status code <%d>\n", SCIPgetStatus(subscip)); 1393 return SCIP_INVALIDDATA; 1394 } 1395 } 1396 1397 /* release all variables */ 1398 for( v = 0; v < njobs; ++v ) 1399 { 1400 SCIP_CALL( SCIPreleaseVar(subscip, &subvars[v]) ); 1401 } 1402 1403 SCIPfreeBlockMemoryArray(subscip, &subvars, njobs); 1404 1405 return SCIP_OKAY; 1406 } 1407 1408 /** solve single cumulative condition using SCIP and a single cumulative constraint */ 1409 static 1410 SCIP_DECL_SOLVECUMULATIVE(solveCumulativeViaScipCp) 1411 { 1412 SCIP* subscip; 1413 1414 SCIP_RETCODE retcode; 1415 1416 assert(njobs > 0); 1417 1418 (*solved) = FALSE; 1419 (*infeasible) = FALSE; 1420 (*unbounded) = FALSE; 1421 (*error) = FALSE; 1422 1423 SCIPdebugMessage("solve independent cumulative condition with %d variables\n", njobs); 1424 1425 /* initialize the sub-problem */ 1426 SCIP_CALL( SCIPcreate(&subscip) ); 1427 1428 /* create and solve the subproblem. catch possible errors */ 1429 retcode = setupAndSolveCumulativeSubscip(subscip, objvals, durations, demands, 1430 njobs, capacity, hmin, hmax, 1431 maxnodes, timelimit, memorylimit, 1432 ests, lsts, 1433 infeasible, unbounded, solved, error); 1434 1435 /* free the subscip in any case */ 1436 SCIP_CALL( SCIPfree(&subscip) ); 1437 1438 SCIP_CALL( retcode ); 1439 1440 return SCIP_OKAY; 1441 } 1442 1443 #if 0 1444 /** solve single cumulative condition using SCIP and the time indexed formulation */ 1445 static 1446 SCIP_DECL_SOLVECUMULATIVE(solveCumulativeViaScipMip) 1447 { 1448 SCIP* subscip; 1449 SCIP_VAR*** binvars; 1450 SCIP_RETCODE retcode; 1451 char name[SCIP_MAXSTRLEN]; 1452 int minest; 1453 int maxlct; 1454 int t; 1455 int v; 1456 1457 assert(njobs > 0); 1458 1459 (*solved) = FALSE; 1460 (*infeasible) = FALSE; 1461 (*unbounded) = FALSE; 1462 (*error) = FALSE; 1463 1464 SCIPdebugMsg(scip, "solve independent cumulative condition with %d variables\n", njobs); 1465 1466 /* initialize the sub-problem */ 1467 SCIP_CALL( SCIPcreate(&subscip) ); 1468 1469 /* copy all plugins */ 1470 SCIP_CALL( SCIPincludeDefaultPlugins(subscip) ); 1471 1472 /* create the subproblem */ 1473 SCIP_CALL( SCIPcreateProbBasic(subscip, "cumulative") ); 1474 1475 SCIP_CALL( SCIPallocBufferArray(subscip, &binvars, njobs) ); 1476 1477 minest = INT_MAX; 1478 maxlct = INT_MIN; 1479 1480 /* create for each job and time step a binary variable which is one if this jobs starts at this time point and a set 1481 * partitioning constrain which forces that job starts 1482 */ 1483 for( v = 0; v < njobs; ++v ) 1484 { 1485 SCIP_CONS* cons; 1486 SCIP_Real objval; 1487 int timeinterval; 1488 int est; 1489 int lst; 1490 1491 if( objvals == NULL ) 1492 objval = 0.0; 1493 else 1494 objval = objvals[v]; 1495 1496 est = ests[v]; 1497 lst = lsts[v]; 1498 1499 /* compute number of possible start points */ 1500 timeinterval = lst - est + 1; 1501 assert(timeinterval > 0); 1502 1503 /* compute the smallest earliest start time and largest latest completion time */ 1504 minest = MIN(minest, est); 1505 maxlct = MAX(maxlct, lst + durations[v]); 1506 1507 /* construct constraint name */ 1508 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "job_%d", v); 1509 1510 SCIP_CALL( SCIPcreateConsBasicSetpart(subscip, &cons, name, 0, NULL) ); 1511 1512 SCIP_CALL( SCIPallocBufferArray(subscip, &binvars[v], timeinterval) ); 1513 1514 for( t = 0; t < timeinterval; ++t ) 1515 { 1516 SCIP_VAR* binvar; 1517 1518 /* construct varibale name */ 1519 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "job_%d_time_%d", v, t + est); 1520 1521 SCIP_CALL( SCIPcreateVarBasic(subscip, &binvar, name, 0.0, 1.0, objval, SCIP_VARTYPE_BINARY) ); 1522 SCIP_CALL( SCIPaddVar(subscip, binvar) ); 1523 1524 /* add binary varibale to the set partitioning constraint which ensures that the job is started */ 1525 SCIP_CALL( SCIPaddCoefSetppc(subscip, cons, binvar) ); 1526 1527 binvars[v][t] = binvar; 1528 } 1529 1530 /* add and release the set partitioning constraint */ 1531 SCIP_CALL( SCIPaddCons(subscip, cons) ); 1532 SCIP_CALL( SCIPreleaseCons(subscip, &cons) ); 1533 } 1534 1535 /* adjusted the smallest earliest start time and the largest latest completion time with the effective horizon */ 1536 hmin = MAX(hmin, minest); 1537 hmax = MIN(hmax, maxlct); 1538 assert(hmin > INT_MIN); 1539 assert(hmax < INT_MAX); 1540 assert(hmin < hmax); 1541 1542 /* create for each time a knapsack constraint which ensures that the resource capacity is not exceeded */ 1543 for( t = hmin; t < hmax; ++t ) 1544 { 1545 SCIP_CONS* cons; 1546 1547 /* construct constraint name */ 1548 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "time_%d", t); 1549 1550 /* create an empty knapsack constraint */ 1551 SCIP_CALL( SCIPcreateConsBasicKnapsack(subscip, &cons, name, 0, NULL, NULL, (SCIP_Longint)capacity) ); 1552 1553 /* add all jobs which potentially can be processed at that time point */ 1554 for( v = 0; v < njobs; ++v ) 1555 { 1556 int duration; 1557 int demand; 1558 int start; 1559 int end; 1560 int est; 1561 int lst; 1562 int k; 1563 1564 est = ests[v]; 1565 lst = lsts[v] ; 1566 1567 duration = durations[v]; 1568 assert(duration > 0); 1569 1570 /* check if the varibale is processed potentially at time point t */ 1571 if( t < est || t >= lst + duration ) 1572 continue; 1573 1574 demand = demands[v]; 1575 assert(demand >= 0); 1576 1577 start = MAX(t - duration + 1, est); 1578 end = MIN(t, lst); 1579 1580 assert(start <= end); 1581 1582 for( k = start; k <= end; ++k ) 1583 { 1584 assert(binvars[v][k] != NULL); 1585 SCIP_CALL( SCIPaddCoefKnapsack(subscip, cons, binvars[v][k], (SCIP_Longint) demand) ); 1586 } 1587 } 1588 1589 /* add and release the knapsack constraint */ 1590 SCIP_CALL( SCIPaddCons(subscip, cons) ); 1591 SCIP_CALL( SCIPreleaseCons(subscip, &cons) ); 1592 } 1593 1594 /* do not abort subproblem on CTRL-C */ 1595 SCIP_CALL( SCIPsetBoolParam(subscip, "misc/catchctrlc", FALSE) ); 1596 1597 /* disable output to console */ 1598 SCIP_CALL( SCIPsetIntParam(subscip, "display/verblevel", 0) ); 1599 1600 /* set limits for the subproblem */ 1601 SCIP_CALL( SCIPsetLongintParam(subscip, "limits/nodes", maxnodes) ); 1602 SCIP_CALL( SCIPsetRealParam(subscip, "limits/time", timelimit) ); 1603 SCIP_CALL( SCIPsetRealParam(subscip, "limits/memory", memorylimit) ); 1604 1605 /* solve single cumulative constraint by branch and bound */ 1606 retcode = SCIPsolve(subscip); 1607 1608 if( retcode != SCIP_OKAY ) 1609 (*error) = TRUE; 1610 else 1611 { 1612 SCIPdebugMsg(scip, "solved single cumulative condition with status %d\n", SCIPgetStatus(subscip)); 1613 1614 /* evaluated solution status */ 1615 switch( SCIPgetStatus(subscip) ) 1616 { 1617 case SCIP_STATUS_INFORUNBD: 1618 case SCIP_STATUS_INFEASIBLE: 1619 (*infeasible) = TRUE; 1620 (*solved) = TRUE; 1621 break; 1622 case SCIP_STATUS_UNBOUNDED: 1623 (*unbounded) = TRUE; 1624 (*solved) = TRUE; 1625 break; 1626 case SCIP_STATUS_OPTIMAL: 1627 { 1628 SCIP_SOL* sol; 1629 1630 sol = SCIPgetBestSol(subscip); 1631 assert(sol != NULL); 1632 1633 for( v = 0; v < njobs; ++v ) 1634 { 1635 int timeinterval; 1636 int est; 1637 int lst; 1638 1639 est = ests[v]; 1640 lst = lsts[v]; 1641 1642 /* compute number of possible start points */ 1643 timeinterval = lst - est + 1; 1644 1645 /* check which binary varibale is set to one */ 1646 for( t = 0; t < timeinterval; ++t ) 1647 { 1648 if( SCIPgetSolVal(subscip, sol, binvars[v][t]) > 0.5 ) 1649 { 1650 ests[v] = est + t; 1651 lsts[v] = est + t; 1652 break; 1653 } 1654 } 1655 } 1656 1657 (*solved) = TRUE; 1658 break; 1659 } 1660 case SCIP_STATUS_NODELIMIT: 1661 case SCIP_STATUS_TOTALNODELIMIT: 1662 case SCIP_STATUS_TIMELIMIT: 1663 case SCIP_STATUS_MEMLIMIT: 1664 case SCIP_STATUS_USERINTERRUPT: 1665 /* transfer the global bound changes */ 1666 for( v = 0; v < njobs; ++v ) 1667 { 1668 int timeinterval; 1669 int est; 1670 int lst; 1671 1672 est = ests[v]; 1673 lst = lsts[v]; 1674 1675 /* compute number of possible start points */ 1676 timeinterval = lst - est + 1; 1677 1678 /* check which binary varibale is the first binary varibale which is not globally fixed to zero */ 1679 for( t = 0; t < timeinterval; ++t ) 1680 { 1681 if( SCIPvarGetUbGlobal(binvars[v][t]) > 0.5 ) 1682 { 1683 ests[v] = est + t; 1684 break; 1685 } 1686 } 1687 1688 /* check which binary varibale is the last binary varibale which is not globally fixed to zero */ 1689 for( t = timeinterval - 1; t >= 0; --t ) 1690 { 1691 if( SCIPvarGetUbGlobal(binvars[v][t]) > 0.5 ) 1692 { 1693 lsts[v] = est + t; 1694 break; 1695 } 1696 } 1697 } 1698 (*solved) = FALSE; 1699 break; 1700 1701 case SCIP_STATUS_UNKNOWN: 1702 case SCIP_STATUS_STALLNODELIMIT: 1703 case SCIP_STATUS_GAPLIMIT: 1704 case SCIP_STATUS_SOLLIMIT: 1705 case SCIP_STATUS_BESTSOLLIMIT: 1706 SCIPerrorMessage("invalid status code <%d>\n", SCIPgetStatus(subscip)); 1707 return SCIP_INVALIDDATA; 1708 } 1709 } 1710 1711 /* release all variables */ 1712 for( v = 0; v < njobs; ++v ) 1713 { 1714 int timeinterval; 1715 int est; 1716 int lst; 1717 1718 est = ests[v]; 1719 lst = lsts[v]; 1720 1721 /* compute number of possible start points */ 1722 timeinterval = lst - est + 1; 1723 1724 for( t = 0; t < timeinterval; ++t ) 1725 { 1726 SCIP_CALL( SCIPreleaseVar(subscip, &binvars[v][t]) ); 1727 } 1728 SCIPfreeBufferArray(subscip, &binvars[v]); 1729 } 1730 1731 SCIPfreeBufferArray(subscip, &binvars); 1732 1733 SCIP_CALL( SCIPfree(&subscip) ); 1734 1735 return SCIP_OKAY; 1736 } 1737 #endif 1738 1739 /**@} */ 1740 1741 /**@name Constraint handler data 1742 * 1743 * Method used to create and free the constraint handler data when including and removing the cumulative constraint 1744 * handler. 1745 * 1746 * @{ 1747 */ 1748 1749 /** creates constaint handler data for cumulative constraint handler */ 1750 static 1751 SCIP_RETCODE conshdlrdataCreate( 1752 SCIP* scip, /**< SCIP data structure */ 1753 SCIP_CONSHDLRDATA** conshdlrdata, /**< pointer to store the constraint handler data */ 1754 SCIP_EVENTHDLR* eventhdlr /**< event handler */ 1755 ) 1756 { 1757 /* create precedence constraint handler data */ 1758 assert(scip != NULL); 1759 assert(conshdlrdata != NULL); 1760 assert(eventhdlr != NULL); 1761 1762 SCIP_CALL( SCIPallocBlockMemory(scip, conshdlrdata) ); 1763 1764 /* set event handler for checking if bounds of start time variables are tighten */ 1765 (*conshdlrdata)->eventhdlr = eventhdlr; 1766 1767 /* set default methed for solving single cumulative conditions using SCIP and a CP model */ 1768 (*conshdlrdata)->solveCumulative = solveCumulativeViaScipCp; 1769 1770 #ifdef SCIP_STATISTIC 1771 (*conshdlrdata)->nlbtimetable = 0; 1772 (*conshdlrdata)->nubtimetable = 0; 1773 (*conshdlrdata)->ncutofftimetable = 0; 1774 (*conshdlrdata)->nlbedgefinder = 0; 1775 (*conshdlrdata)->nubedgefinder = 0; 1776 (*conshdlrdata)->ncutoffedgefinder = 0; 1777 (*conshdlrdata)->ncutoffoverload = 0; 1778 (*conshdlrdata)->ncutoffoverloadTTEF = 0; 1779 1780 (*conshdlrdata)->nirrelevantjobs = 0; 1781 (*conshdlrdata)->nalwaysruns = 0; 1782 (*conshdlrdata)->nremovedlocks = 0; 1783 (*conshdlrdata)->ndualfixs = 0; 1784 (*conshdlrdata)->ndecomps = 0; 1785 (*conshdlrdata)->ndualbranchs = 0; 1786 (*conshdlrdata)->nallconsdualfixs = 0; 1787 (*conshdlrdata)->naddedvarbounds = 0; 1788 (*conshdlrdata)->naddeddisjunctives = 0; 1789 #endif 1790 1791 return SCIP_OKAY; 1792 } 1793 1794 /** frees constraint handler data for logic or constraint handler */ 1795 static 1796 void conshdlrdataFree( 1797 SCIP* scip, /**< SCIP data structure */ 1798 SCIP_CONSHDLRDATA** conshdlrdata /**< pointer to the constraint handler data */ 1799 ) 1800 { 1801 assert(conshdlrdata != NULL); 1802 assert(*conshdlrdata != NULL); 1803 1804 SCIPfreeBlockMemory(scip, conshdlrdata); 1805 } 1806 1807 /**@} */ 1808 1809 1810 /**@name Constraint data methods 1811 * 1812 * @{ 1813 */ 1814 1815 /** catches bound change events for all variables in transformed cumulative constraint */ 1816 static 1817 SCIP_RETCODE consdataCatchEvents( 1818 SCIP* scip, /**< SCIP data structure */ 1819 SCIP_CONSDATA* consdata, /**< cumulative constraint data */ 1820 SCIP_EVENTHDLR* eventhdlr /**< event handler to call for the event processing */ 1821 ) 1822 { 1823 int v; 1824 1825 assert(scip != NULL); 1826 assert(consdata != NULL); 1827 assert(eventhdlr != NULL); 1828 1829 /* catch event for every single variable */ 1830 for( v = 0; v < consdata->nvars; ++v ) 1831 { 1832 SCIP_CALL( SCIPcatchVarEvent(scip, consdata->vars[v], 1833 SCIP_EVENTTYPE_BOUNDTIGHTENED, eventhdlr, (SCIP_EVENTDATA*)consdata, NULL) ); 1834 } 1835 1836 return SCIP_OKAY; 1837 } 1838 1839 /** drops events for variable at given position */ 1840 static 1841 SCIP_RETCODE consdataDropEvents( 1842 SCIP* scip, /**< SCIP data structure */ 1843 SCIP_CONSDATA* consdata, /**< cumulative constraint data */ 1844 SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */ 1845 int pos /**< array position of variable to catch bound change events for */ 1846 ) 1847 { 1848 assert(scip != NULL); 1849 assert(consdata != NULL); 1850 assert(eventhdlr != NULL); 1851 assert(0 <= pos && pos < consdata->nvars); 1852 assert(consdata->vars[pos] != NULL); 1853 1854 SCIP_CALL( SCIPdropVarEvent(scip, consdata->vars[pos], 1855 SCIP_EVENTTYPE_BOUNDTIGHTENED, eventhdlr, (SCIP_EVENTDATA*)consdata, -1) ); 1856 1857 return SCIP_OKAY; 1858 } 1859 1860 /** drops bound change events for all variables in transformed linear constraint */ 1861 static 1862 SCIP_RETCODE consdataDropAllEvents( 1863 SCIP* scip, /**< SCIP data structure */ 1864 SCIP_CONSDATA* consdata, /**< linear constraint data */ 1865 SCIP_EVENTHDLR* eventhdlr /**< event handler to call for the event processing */ 1866 ) 1867 { 1868 int v; 1869 1870 assert(scip != NULL); 1871 assert(consdata != NULL); 1872 1873 /* drop event of every single variable */ 1874 for( v = 0; v < consdata->nvars; ++v ) 1875 { 1876 SCIP_CALL( consdataDropEvents(scip, consdata, eventhdlr, v) ); 1877 } 1878 1879 return SCIP_OKAY; 1880 } 1881 1882 /** initialize variable lock data structure */ 1883 static 1884 void initializeLocks( 1885 SCIP_CONSDATA* consdata, /**< constraint data */ 1886 SCIP_Bool locked /**< should the variable be locked? */ 1887 ) 1888 { 1889 int nvars; 1890 int v; 1891 1892 nvars = consdata->nvars; 1893 1894 /* initialize locking arrays */ 1895 for( v = 0; v < nvars; ++v ) 1896 { 1897 consdata->downlocks[v] = locked; 1898 consdata->uplocks[v] = locked; 1899 } 1900 } 1901 1902 /** creates constraint data of cumulative constraint */ 1903 static 1904 SCIP_RETCODE consdataCreate( 1905 SCIP* scip, /**< SCIP data structure */ 1906 SCIP_CONSDATA** consdata, /**< pointer to consdata */ 1907 SCIP_VAR** vars, /**< array of integer variables */ 1908 SCIP_CONS** linkingconss, /**< array of linking constraints for the integer variables, or NULL */ 1909 int* durations, /**< array containing corresponding durations */ 1910 int* demands, /**< array containing corresponding demands */ 1911 int nvars, /**< number of variables */ 1912 int capacity, /**< available cumulative capacity */ 1913 int hmin, /**< left bound of time axis to be considered (including hmin) */ 1914 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 1915 SCIP_Bool check /**< is the corresponding constraint a check constraint */ 1916 ) 1917 { 1918 int v; 1919 1920 assert(scip != NULL); 1921 assert(consdata != NULL); 1922 assert(vars != NULL || nvars > 0); 1923 assert(demands != NULL); 1924 assert(durations != NULL); 1925 assert(capacity >= 0); 1926 assert(hmin >= 0); 1927 assert(hmin < hmax); 1928 1929 /* create constraint data */ 1930 SCIP_CALL( SCIPallocBlockMemory(scip, consdata) ); 1931 1932 (*consdata)->hmin = hmin; 1933 (*consdata)->hmax = hmax; 1934 1935 (*consdata)->capacity = capacity; 1936 (*consdata)->demandrows = NULL; 1937 (*consdata)->demandrowssize = 0; 1938 (*consdata)->ndemandrows = 0; 1939 (*consdata)->scoverrows = NULL; 1940 (*consdata)->nscoverrows = 0; 1941 (*consdata)->scoverrowssize = 0; 1942 (*consdata)->bcoverrows = NULL; 1943 (*consdata)->nbcoverrows = 0; 1944 (*consdata)->bcoverrowssize = 0; 1945 (*consdata)->nvars = nvars; 1946 (*consdata)->varssize = nvars; 1947 (*consdata)->signature = 0; 1948 (*consdata)->validsignature = FALSE; 1949 (*consdata)->normalized = FALSE; 1950 (*consdata)->covercuts = FALSE; 1951 (*consdata)->propagated = FALSE; 1952 (*consdata)->varbounds = FALSE; 1953 (*consdata)->triedsolving = FALSE; 1954 1955 if( nvars > 0 ) 1956 { 1957 assert(vars != NULL); /* for flexelint */ 1958 1959 SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*consdata)->vars, vars, nvars) ); 1960 SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*consdata)->demands, demands, nvars) ); 1961 SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*consdata)->durations, durations, nvars) ); 1962 (*consdata)->linkingconss = NULL; 1963 1964 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(*consdata)->downlocks, nvars) ); 1965 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(*consdata)->uplocks, nvars) ); 1966 1967 /* initialize variable lock data structure; the locks are only used if the constraint is a check constraint */ 1968 initializeLocks(*consdata, check); 1969 1970 if( linkingconss != NULL ) 1971 { 1972 SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*consdata)->linkingconss, linkingconss, nvars) ); 1973 } 1974 1975 /* transform variables, if they are not yet transformed */ 1976 if( SCIPisTransformed(scip) ) 1977 { 1978 SCIPdebugMsg(scip, "get tranformed variables and constraints\n"); 1979 1980 /* get transformed variables and do NOT captures these */ 1981 SCIP_CALL( SCIPgetTransformedVars(scip, (*consdata)->nvars, (*consdata)->vars, (*consdata)->vars) ); 1982 1983 /* multi-aggregated variables cannot be replaced by active variable; therefore we mark all variables for not 1984 * been multi-aggregated 1985 */ 1986 for( v = 0; v < nvars; ++v ) 1987 { 1988 SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, (*consdata)->vars[v]) ); 1989 } 1990 1991 if( linkingconss != NULL ) 1992 { 1993 /* get transformed constraints and captures these */ 1994 SCIP_CALL( SCIPtransformConss(scip, (*consdata)->nvars, (*consdata)->linkingconss, (*consdata)->linkingconss) ); 1995 1996 for( v = 0; v < nvars; ++v ) 1997 assert(SCIPgetConsLinking(scip, (*consdata)->vars[v]) == (*consdata)->linkingconss[v]); 1998 } 1999 } 2000 2001 #ifndef NDEBUG 2002 /* only binary and integer variables can be used in cumulative constraints 2003 * for fractional variable values, the constraint cannot be checked 2004 */ 2005 for( v = 0; v < (*consdata)->nvars; ++v ) 2006 assert(SCIPvarGetType((*consdata)->vars[v]) <= SCIP_VARTYPE_INTEGER); 2007 #endif 2008 } 2009 else 2010 { 2011 (*consdata)->vars = NULL; 2012 (*consdata)->downlocks = NULL; 2013 (*consdata)->uplocks = NULL; 2014 (*consdata)->demands = NULL; 2015 (*consdata)->durations = NULL; 2016 (*consdata)->linkingconss = NULL; 2017 } 2018 2019 /* initialize values for running propagation algorithms efficiently */ 2020 (*consdata)->resstrength1 = -1.0; 2021 (*consdata)->resstrength2 = -1.0; 2022 (*consdata)->cumfactor1 = -1.0; 2023 (*consdata)->disjfactor1 = -1.0; 2024 (*consdata)->disjfactor2 = -1.0; 2025 (*consdata)->estimatedstrength = -1.0; 2026 2027 SCIPstatistic( (*consdata)->maxpeak = -1 ); 2028 2029 return SCIP_OKAY; 2030 } 2031 2032 /** releases LP rows of constraint data and frees rows array */ 2033 static 2034 SCIP_RETCODE consdataFreeRows( 2035 SCIP* scip, /**< SCIP data structure */ 2036 SCIP_CONSDATA** consdata /**< constraint data */ 2037 ) 2038 { 2039 int r; 2040 2041 assert(consdata != NULL); 2042 assert(*consdata != NULL); 2043 2044 for( r = 0; r < (*consdata)->ndemandrows; ++r ) 2045 { 2046 assert((*consdata)->demandrows[r] != NULL); 2047 SCIP_CALL( SCIPreleaseRow(scip, &(*consdata)->demandrows[r]) ); 2048 } 2049 2050 SCIPfreeBlockMemoryArrayNull(scip, &(*consdata)->demandrows, (*consdata)->demandrowssize); 2051 2052 (*consdata)->ndemandrows = 0; 2053 (*consdata)->demandrowssize = 0; 2054 2055 /* free rows of cover cuts */ 2056 for( r = 0; r < (*consdata)->nscoverrows; ++r ) 2057 { 2058 assert((*consdata)->scoverrows[r] != NULL); 2059 SCIP_CALL( SCIPreleaseRow(scip, &(*consdata)->scoverrows[r]) ); 2060 } 2061 2062 SCIPfreeBlockMemoryArrayNull(scip, &(*consdata)->scoverrows, (*consdata)->scoverrowssize); 2063 2064 (*consdata)->nscoverrows = 0; 2065 (*consdata)->scoverrowssize = 0; 2066 2067 for( r = 0; r < (*consdata)->nbcoverrows; ++r ) 2068 { 2069 assert((*consdata)->bcoverrows[r] != NULL); 2070 SCIP_CALL( SCIPreleaseRow(scip, &(*consdata)->bcoverrows[r]) ); 2071 } 2072 2073 SCIPfreeBlockMemoryArrayNull(scip, &(*consdata)->bcoverrows, (*consdata)->bcoverrowssize); 2074 2075 (*consdata)->nbcoverrows = 0; 2076 (*consdata)->bcoverrowssize = 0; 2077 2078 (*consdata)->covercuts = FALSE; 2079 2080 return SCIP_OKAY; 2081 } 2082 2083 /** frees a cumulative constraint data */ 2084 static 2085 SCIP_RETCODE consdataFree( 2086 SCIP* scip, /**< SCIP data structure */ 2087 SCIP_CONSDATA** consdata /**< pointer to linear constraint data */ 2088 ) 2089 { 2090 int varssize; 2091 int nvars; 2092 2093 assert(consdata != NULL); 2094 assert(*consdata != NULL); 2095 2096 nvars = (*consdata)->nvars; 2097 varssize = (*consdata)->varssize; 2098 2099 if( varssize > 0 ) 2100 { 2101 int v; 2102 2103 /* release and free the rows */ 2104 SCIP_CALL( consdataFreeRows(scip, consdata) ); 2105 2106 /* release the linking constraints if they were generated */ 2107 if( (*consdata)->linkingconss != NULL ) 2108 { 2109 for( v = nvars-1; v >= 0; --v ) 2110 { 2111 assert((*consdata)->linkingconss[v] != NULL ); 2112 SCIP_CALL( SCIPreleaseCons(scip, &(*consdata)->linkingconss[v]) ); 2113 } 2114 2115 SCIPfreeBlockMemoryArray(scip, &(*consdata)->linkingconss, varssize); 2116 } 2117 2118 /* free arrays */ 2119 SCIPfreeBlockMemoryArray(scip, &(*consdata)->downlocks, varssize); 2120 SCIPfreeBlockMemoryArray(scip, &(*consdata)->uplocks, varssize); 2121 SCIPfreeBlockMemoryArray(scip, &(*consdata)->durations, varssize); 2122 SCIPfreeBlockMemoryArray(scip, &(*consdata)->demands, varssize); 2123 SCIPfreeBlockMemoryArray(scip, &(*consdata)->vars, varssize); 2124 } 2125 2126 /* free memory */ 2127 SCIPfreeBlockMemory(scip, consdata); 2128 2129 return SCIP_OKAY; 2130 } 2131 2132 /** prints cumulative constraint to file stream */ 2133 static 2134 void consdataPrint( 2135 SCIP* scip, /**< SCIP data structure */ 2136 SCIP_CONSDATA* consdata, /**< cumulative constraint data */ 2137 FILE* file /**< output file (or NULL for standard output) */ 2138 ) 2139 { 2140 int v; 2141 2142 assert(consdata != NULL); 2143 2144 /* print coefficients */ 2145 SCIPinfoMessage( scip, file, "cumulative("); 2146 2147 for( v = 0; v < consdata->nvars; ++v ) 2148 { 2149 assert(consdata->vars[v] != NULL); 2150 if( v > 0 ) 2151 SCIPinfoMessage(scip, file, ", "); 2152 SCIPinfoMessage(scip, file, "<%s>[%g,%g](%d)[%d]", SCIPvarGetName(consdata->vars[v]), 2153 SCIPvarGetLbGlobal(consdata->vars[v]), SCIPvarGetUbGlobal(consdata->vars[v]), 2154 consdata->durations[v], consdata->demands[v]); 2155 } 2156 SCIPinfoMessage(scip, file, ")[%d,%d) <= %d", consdata->hmin, consdata->hmax, consdata->capacity); 2157 } 2158 2159 /** deletes coefficient at given position from constraint data */ 2160 static 2161 SCIP_RETCODE consdataDeletePos( 2162 SCIP* scip, /**< SCIP data structure */ 2163 SCIP_CONSDATA* consdata, /**< cumulative constraint data */ 2164 SCIP_CONS* cons, /**< knapsack constraint */ 2165 int pos /**< position of coefficient to delete */ 2166 ) 2167 { 2168 SCIP_CONSHDLR* conshdlr; 2169 SCIP_CONSHDLRDATA* conshdlrdata; 2170 2171 assert(scip != NULL); 2172 assert(consdata != NULL); 2173 assert(cons != NULL); 2174 assert(SCIPconsIsTransformed(cons)); 2175 assert(!SCIPinProbing(scip)); 2176 2177 SCIPdebugMsg(scip, "cumulative constraint <%s>: remove variable <%s>\n", 2178 SCIPconsGetName(cons), SCIPvarGetName(consdata->vars[pos])); 2179 2180 /* remove the rounding locks for the deleted variable */ 2181 SCIP_CALL( SCIPunlockVarCons(scip, consdata->vars[pos], cons, consdata->downlocks[pos], consdata->uplocks[pos]) ); 2182 2183 consdata->downlocks[pos] = FALSE; 2184 consdata->uplocks[pos] = FALSE; 2185 2186 if( consdata->linkingconss != NULL ) 2187 { 2188 SCIP_CALL( SCIPreleaseCons(scip, &consdata->linkingconss[pos]) ); 2189 } 2190 2191 /* get event handler */ 2192 conshdlr = SCIPconsGetHdlr(cons); 2193 assert(conshdlr != NULL); 2194 conshdlrdata = SCIPconshdlrGetData(conshdlr); 2195 assert(conshdlrdata != NULL); 2196 assert(conshdlrdata->eventhdlr != NULL); 2197 2198 /* drop events */ 2199 SCIP_CALL( consdataDropEvents(scip, consdata, conshdlrdata->eventhdlr, pos) ); 2200 2201 SCIPdebugMsg(scip, "remove variable <%s>[%g,%g] from cumulative constraint <%s>\n", 2202 SCIPvarGetName(consdata->vars[pos]), SCIPvarGetLbGlobal(consdata->vars[pos]), SCIPvarGetUbGlobal(consdata->vars[pos]), SCIPconsGetName(cons)); 2203 2204 /* in case the we did not remove the variable in the last slot of the arrays we move the current last to this 2205 * position 2206 */ 2207 if( pos != consdata->nvars - 1 ) 2208 { 2209 consdata->vars[pos] = consdata->vars[consdata->nvars-1]; 2210 consdata->downlocks[pos] = consdata->downlocks[consdata->nvars-1]; 2211 consdata->uplocks[pos] = consdata->uplocks[consdata->nvars-1]; 2212 consdata->demands[pos] = consdata->demands[consdata->nvars-1]; 2213 consdata->durations[pos] = consdata->durations[consdata->nvars-1]; 2214 2215 if( consdata->linkingconss != NULL ) 2216 { 2217 consdata->linkingconss[pos]= consdata->linkingconss[consdata->nvars-1]; 2218 } 2219 } 2220 2221 consdata->nvars--; 2222 consdata->validsignature = FALSE; 2223 consdata->normalized = FALSE; 2224 2225 return SCIP_OKAY; 2226 } 2227 2228 /** collect linking constraints for each integer variable */ 2229 static 2230 SCIP_RETCODE consdataCollectLinkingCons( 2231 SCIP* scip, /**< SCIP data structure */ 2232 SCIP_CONSDATA* consdata /**< pointer to consdata */ 2233 ) 2234 { 2235 int nvars; 2236 int v; 2237 2238 assert(scip != NULL); 2239 assert(consdata != NULL); 2240 2241 nvars = consdata->nvars; 2242 assert(nvars > 0); 2243 assert(consdata->linkingconss == NULL); 2244 2245 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->linkingconss, consdata->varssize) ); 2246 2247 for( v = 0; v < nvars; ++v ) 2248 { 2249 SCIP_CONS* cons; 2250 SCIP_VAR* var; 2251 2252 var = consdata->vars[v]; 2253 assert(var != NULL); 2254 2255 SCIPdebugMsg(scip, "linking constraint (%d of %d) for variable <%s>\n", v+1, nvars, SCIPvarGetName(var)); 2256 2257 /* create linking constraint if it does not exist yet */ 2258 if( !SCIPexistsConsLinking(scip, var) ) 2259 { 2260 char name[SCIP_MAXSTRLEN]; 2261 2262 (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "link(%s)", SCIPvarGetName(var)); 2263 2264 /* creates and captures an linking constraint */ 2265 SCIP_CALL( SCIPcreateConsLinking(scip, &cons, name, var, NULL, 0, 0, 2266 TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE /*TRUE*/, FALSE) ); 2267 SCIP_CALL( SCIPaddCons(scip, cons) ); 2268 consdata->linkingconss[v] = cons; 2269 } 2270 else 2271 { 2272 consdata->linkingconss[v] = SCIPgetConsLinking(scip, var); 2273 SCIP_CALL( SCIPcaptureCons(scip, consdata->linkingconss[v]) ); 2274 } 2275 2276 assert(SCIPexistsConsLinking(scip, var)); 2277 assert(consdata->linkingconss[v] != NULL); 2278 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(consdata->linkingconss[v])), "linking") == 0 ); 2279 assert(SCIPgetConsLinking(scip, var) == consdata->linkingconss[v]); 2280 } 2281 2282 return SCIP_OKAY; 2283 } 2284 2285 /**@} */ 2286 2287 2288 /**@name Check methods 2289 * 2290 * @{ 2291 */ 2292 2293 /** check for the given starting time variables with their demands and durations if the cumulative conditions for the 2294 * given solution is satisfied 2295 */ 2296 static 2297 SCIP_RETCODE checkCumulativeCondition( 2298 SCIP* scip, /**< SCIP data structure */ 2299 SCIP_SOL* sol, /**< primal solution, or NULL for current LP/pseudo solution */ 2300 int nvars, /**< number of variables (jobs) */ 2301 SCIP_VAR** vars, /**< array of integer variable which corresponds to starting times for a job */ 2302 int* durations, /**< array containing corresponding durations */ 2303 int* demands, /**< array containing corresponding demands */ 2304 int capacity, /**< available cumulative capacity */ 2305 int hmin, /**< left bound of time axis to be considered (including hmin) */ 2306 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 2307 SCIP_Bool* violated, /**< pointer to store if the cumulative condition is violated */ 2308 SCIP_CONS* cons, /**< constraint which is checked */ 2309 SCIP_Bool printreason /**< should the reason for the violation be printed? */ 2310 ) 2311 { 2312 int* startsolvalues; /* stores when each job is starting */ 2313 int* endsolvalues; /* stores when each job ends */ 2314 int* startindices; /* we will sort the startsolvalues, thus we need to know which index of a job it corresponds to */ 2315 int* endindices; /* we will sort the endsolvalues, thus we need to know which index of a job it corresponds to */ 2316 2317 int freecapacity; 2318 int curtime; /* point in time which we are just checking */ 2319 int endindex; /* index of endsolvalues with: endsolvalues[endindex] > curtime */ 2320 int j; 2321 2322 SCIP_Real absviol; 2323 SCIP_Real relviol; 2324 2325 assert(scip != NULL); 2326 assert(violated != NULL); 2327 2328 (*violated) = FALSE; 2329 2330 if( nvars == 0 ) 2331 return SCIP_OKAY; 2332 2333 assert(vars != NULL); 2334 assert(demands != NULL); 2335 assert(durations != NULL); 2336 2337 /* compute time points where we have to check whether capacity constraint is infeasible or not */ 2338 SCIP_CALL( SCIPallocBufferArray(scip, &startsolvalues, nvars) ); 2339 SCIP_CALL( SCIPallocBufferArray(scip, &endsolvalues, nvars) ); 2340 SCIP_CALL( SCIPallocBufferArray(scip, &startindices, nvars) ); 2341 SCIP_CALL( SCIPallocBufferArray(scip, &endindices, nvars) ); 2342 2343 /* assign variables, start and endpoints to arrays */ 2344 for ( j = 0; j < nvars; ++j ) 2345 { 2346 int solvalue; 2347 2348 /* the constraint of the cumulative constraint handler should be called after the integrality check */ 2349 assert(SCIPisFeasIntegral(scip, SCIPgetSolVal(scip, sol, vars[j]))); 2350 2351 solvalue = SCIPconvertRealToInt(scip, SCIPgetSolVal(scip, sol, vars[j])); 2352 2353 /* we need to ensure that we check at least one time point during the effective horizon; therefore we project all 2354 * jobs which start before hmin to hmin 2355 */ 2356 startsolvalues[j] = MAX(solvalue, hmin); 2357 startindices[j] = j; 2358 2359 endsolvalues[j] = MAX(solvalue + durations[j], hmin); 2360 endindices[j] = j; 2361 } 2362 2363 /* sort the arrays not-decreasing according to start solution values and end solution values (and sort the 2364 * corresponding indices in the same way) 2365 */ 2366 SCIPsortIntInt(startsolvalues, startindices, nvars); 2367 SCIPsortIntInt(endsolvalues, endindices, nvars); 2368 2369 endindex = 0; 2370 freecapacity = capacity; 2371 absviol = 0.0; 2372 relviol = 0.0; 2373 2374 /* check each start point of a job whether the capacity is kept or not */ 2375 for( j = 0; j < nvars; ++j ) 2376 { 2377 /* only check intervals [hmin,hmax) */ 2378 curtime = startsolvalues[j]; 2379 2380 if( curtime >= hmax ) 2381 break; 2382 2383 /* subtract all capacity needed up to this point */ 2384 freecapacity -= demands[startindices[j]]; 2385 while( j+1 < nvars && startsolvalues[j+1] == curtime ) 2386 { 2387 j++; 2388 freecapacity -= demands[startindices[j]]; 2389 } 2390 2391 /* free all capacity usages of jobs that are no longer running */ 2392 while( endindex < nvars && curtime >= endsolvalues[endindex] ) 2393 { 2394 freecapacity += demands[endindices[endindex]]; 2395 ++endindex; 2396 } 2397 assert(freecapacity <= capacity); 2398 2399 /* update absolute and relative violation */ 2400 if( absviol < (SCIP_Real) (-freecapacity) ) 2401 { 2402 absviol = -freecapacity; 2403 relviol = SCIPrelDiff((SCIP_Real)(capacity - freecapacity), (SCIP_Real)capacity); 2404 } 2405 2406 /* check freecapacity to be smaller than zero */ 2407 if( freecapacity < 0 && curtime >= hmin ) 2408 { 2409 SCIPdebugMsg(scip, "freecapacity = %3d\n", freecapacity); 2410 (*violated) = TRUE; 2411 2412 if( printreason ) 2413 { 2414 int i; 2415 2416 /* first state the violated constraints */ 2417 SCIP_CALL( SCIPprintCons(scip, cons, NULL) ); 2418 2419 /* second state the reason */ 2420 SCIPinfoMessage(scip, NULL, 2421 ";\nviolation: at time point %d available capacity = %d, needed capacity = %d\n", 2422 curtime, capacity, capacity - freecapacity); 2423 2424 for( i = 0; i <= j; ++i ) 2425 { 2426 if( startsolvalues[i] + durations[startindices[i]] > curtime ) 2427 { 2428 SCIPinfoMessage(scip, NULL, "activity %s, start = %i, duration = %d, demand = %d \n", 2429 SCIPvarGetName(vars[startindices[i]]), startsolvalues[i], durations[startindices[i]], 2430 demands[startindices[i]]); 2431 } 2432 } 2433 } 2434 break; 2435 } 2436 } /*lint --e{850}*/ 2437 2438 /* update constraint violation in solution */ 2439 if( sol != NULL ) 2440 SCIPupdateSolConsViolation(scip, sol, absviol, relviol); 2441 2442 /* free all buffer arrays */ 2443 SCIPfreeBufferArray(scip, &endindices); 2444 SCIPfreeBufferArray(scip, &startindices); 2445 SCIPfreeBufferArray(scip, &endsolvalues); 2446 SCIPfreeBufferArray(scip, &startsolvalues); 2447 2448 return SCIP_OKAY; 2449 } 2450 2451 /** check if the given constrait is valid; checks each starting point of a job whether the remaining capacity is at 2452 * least zero or not. If not (*violated) is set to TRUE 2453 */ 2454 static 2455 SCIP_RETCODE checkCons( 2456 SCIP* scip, /**< SCIP data structure */ 2457 SCIP_CONS* cons, /**< constraint to be checked */ 2458 SCIP_SOL* sol, /**< primal solution, or NULL for current LP/pseudo solution */ 2459 SCIP_Bool* violated, /**< pointer to store if the constraint is violated */ 2460 SCIP_Bool printreason /**< should the reason for the violation be printed? */ 2461 ) 2462 { 2463 SCIP_CONSDATA* consdata; 2464 2465 assert(scip != NULL); 2466 assert(cons != NULL); 2467 assert(violated != NULL); 2468 2469 SCIPdebugMsg(scip, "check cumulative constraints <%s>\n", SCIPconsGetName(cons)); 2470 2471 consdata = SCIPconsGetData(cons); 2472 assert(consdata != NULL); 2473 2474 /* check the cumulative condition */ 2475 SCIP_CALL( checkCumulativeCondition(scip, sol, consdata->nvars, consdata->vars, 2476 consdata->durations, consdata->demands, consdata->capacity, consdata->hmin, consdata->hmax, 2477 violated, cons, printreason) ); 2478 2479 return SCIP_OKAY; 2480 } 2481 2482 /**@} */ 2483 2484 /**@name Conflict analysis 2485 * 2486 * @{ 2487 */ 2488 2489 /** resolves the propagation of the core time algorithm */ 2490 static 2491 SCIP_RETCODE resolvePropagationCoretimes( 2492 SCIP* scip, /**< SCIP data structure */ 2493 int nvars, /**< number of start time variables (activities) */ 2494 SCIP_VAR** vars, /**< array of start time variables */ 2495 int* durations, /**< array of durations */ 2496 int* demands, /**< array of demands */ 2497 int capacity, /**< cumulative capacity */ 2498 int hmin, /**< left bound of time axis to be considered (including hmin) */ 2499 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 2500 SCIP_VAR* infervar, /**< inference variable */ 2501 int inferdemand, /**< demand of the inference variable */ 2502 int inferpeak, /**< time point which causes the propagation */ 2503 int relaxedpeak, /**< relaxed time point which would be sufficient to be proved */ 2504 SCIP_BDCHGIDX* bdchgidx, /**< the index of the bound change, representing the point of time where the change took place */ 2505 SCIP_Bool usebdwidening, /**< should bound widening be used during conflict analysis? */ 2506 int* provedpeak, /**< pointer to store the actually proved peak, or NULL */ 2507 SCIP_Bool* explanation /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */ 2508 ) 2509 { 2510 SCIP_VAR* var; 2511 SCIP_Bool* reported; 2512 int duration; 2513 int maxlst; 2514 int minect; 2515 int ect; 2516 int lst; 2517 int j; 2518 2519 assert(SCIPgetStage(scip) == SCIP_STAGE_SOLVING || SCIPinProbing(scip)); 2520 2521 SCIPdebugMsg(scip, "variable <%s>: (demand %d) resolve propagation of core time algorithm (peak %d)\n", 2522 SCIPvarGetName(infervar), inferdemand, inferpeak); 2523 assert(nvars > 0); 2524 2525 /* adjusted capacity */ 2526 capacity -= inferdemand; 2527 maxlst = INT_MIN; 2528 minect = INT_MAX; 2529 2530 SCIP_CALL( SCIPallocBufferArray(scip, &reported, nvars) ); 2531 BMSclearMemoryArray(reported, nvars); 2532 2533 /* first we loop over all variables and adjust the capacity with those jobs which provide a global core at the 2534 * inference peak and those where the current conflict bounds provide a core at the inference peak 2535 */ 2536 for( j = 0; j < nvars && capacity >= 0; ++j ) 2537 { 2538 var = vars[j]; 2539 assert(var != NULL); 2540 2541 /* skip inference variable */ 2542 if( var == infervar ) 2543 continue; 2544 2545 duration = durations[j]; 2546 assert(duration > 0); 2547 2548 /* compute cores of jobs; if core overlaps interval of inference variable add this job to the array */ 2549 assert(!SCIPvarIsActive(var) || SCIPisFeasEQ(scip, SCIPgetVarUbAtIndex(scip, var, bdchgidx, TRUE), SCIPgetVarUbAtIndex(scip, var, bdchgidx, FALSE))); 2550 assert(SCIPisFeasIntegral(scip, SCIPgetVarUbAtIndex(scip, var, bdchgidx, TRUE))); 2551 assert(!SCIPvarIsActive(var) || SCIPisFeasEQ(scip, SCIPgetVarLbAtIndex(scip, var, bdchgidx, TRUE), SCIPgetVarLbAtIndex(scip, var, bdchgidx, FALSE))); 2552 assert(SCIPisFeasIntegral(scip, SCIPgetVarLbAtIndex(scip, var, bdchgidx, TRUE))); 2553 2554 SCIPdebugMsg(scip, "variable <%s>: glb=[%g,%g] conflict=[%g,%g] (duration %d, demand %d)\n", 2555 SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var), 2556 SCIPgetConflictVarLb(scip, var), SCIPgetConflictVarUb(scip, var), duration, demands[j]); 2557 2558 ect = SCIPconvertRealToInt(scip, SCIPvarGetLbGlobal(var)) + duration; 2559 lst = SCIPconvertRealToInt(scip, SCIPvarGetUbGlobal(var)); 2560 2561 /* check if the inference peak is part of the global bound core; if so we decreasing the capacity by the demand of 2562 * that job without adding it the explanation 2563 */ 2564 if( inferpeak < ect && lst <= inferpeak ) 2565 { 2566 capacity -= demands[j]; 2567 reported[j] = TRUE; 2568 2569 maxlst = MAX(maxlst, lst); 2570 minect = MIN(minect, ect); 2571 assert(maxlst < minect); 2572 2573 if( explanation != NULL ) 2574 explanation[j] = TRUE; 2575 2576 continue; 2577 } 2578 2579 /* collect the conflict bound core (the conflict bounds are those bounds which are already part of the conflict) 2580 * hence these bound are already reported by other resolve propation steps. In case a bound (lower or upper) is 2581 * not part of the conflict yet we get the global bounds back. 2582 */ 2583 ect = SCIPconvertRealToInt(scip, SCIPgetConflictVarLb(scip, var)) + duration; 2584 lst = SCIPconvertRealToInt(scip, SCIPgetConflictVarUb(scip, var)); 2585 2586 /* check if the inference peak is part of the conflict bound core; if so we decreasing the capacity by the demand 2587 * of that job without and collect the job as part of the explanation 2588 * 2589 * @note we do not need to reported that job to SCIP since the required bounds are already reported 2590 */ 2591 if( inferpeak < ect && lst <= inferpeak ) 2592 { 2593 capacity -= demands[j]; 2594 reported[j] = TRUE; 2595 2596 maxlst = MAX(maxlst, lst); 2597 minect = MIN(minect, ect); 2598 assert(maxlst < minect); 2599 2600 if( explanation != NULL ) 2601 explanation[j] = TRUE; 2602 } 2603 } 2604 2605 if( capacity >= 0 ) 2606 { 2607 int* cands; 2608 int* canddemands; 2609 int ncands; 2610 int c; 2611 2612 SCIP_CALL( SCIPallocBufferArray(scip, &cands, nvars) ); 2613 SCIP_CALL( SCIPallocBufferArray(scip, &canddemands, nvars) ); 2614 ncands = 0; 2615 2616 /* collect all cores of the variables which lay in the considered time window except the inference variable */ 2617 for( j = 0; j < nvars; ++j ) 2618 { 2619 var = vars[j]; 2620 assert(var != NULL); 2621 2622 /* skip inference variable */ 2623 if( var == infervar || reported[j] ) 2624 continue; 2625 2626 duration = durations[j]; 2627 assert(duration > 0); 2628 2629 /* compute cores of jobs; if core overlaps interval of inference variable add this job to the array */ 2630 assert(!SCIPvarIsActive(var) || SCIPisFeasEQ(scip, SCIPgetVarUbAtIndex(scip, var, bdchgidx, TRUE), SCIPgetVarUbAtIndex(scip, var, bdchgidx, FALSE))); 2631 assert(SCIPisFeasIntegral(scip, SCIPgetVarUbAtIndex(scip, var, bdchgidx, TRUE))); 2632 assert(!SCIPvarIsActive(var) || SCIPisFeasEQ(scip, SCIPgetVarLbAtIndex(scip, var, bdchgidx, TRUE), SCIPgetVarLbAtIndex(scip, var, bdchgidx, FALSE))); 2633 assert(SCIPisFeasIntegral(scip, SCIPgetVarLbAtIndex(scip, var, bdchgidx, TRUE))); 2634 2635 /* collect local core information */ 2636 ect = SCIPconvertRealToInt(scip, SCIPgetVarLbAtIndex(scip, var, bdchgidx, FALSE)) + duration; 2637 lst = SCIPconvertRealToInt(scip, SCIPgetVarUbAtIndex(scip, var, bdchgidx, FALSE)); 2638 2639 SCIPdebugMsg(scip, "variable <%s>: loc=[%g,%g] glb=[%g,%g] (duration %d, demand %d)\n", 2640 SCIPvarGetName(var), SCIPgetVarLbAtIndex(scip, var, bdchgidx, FALSE), SCIPgetVarUbAtIndex(scip, var, bdchgidx, FALSE), 2641 SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var), duration, demands[j]); 2642 2643 /* check if the inference peak is part of the core */ 2644 if( inferpeak < ect && lst <= inferpeak ) 2645 { 2646 cands[ncands] = j; 2647 canddemands[ncands] = demands[j]; 2648 ncands++; 2649 2650 capacity -= demands[j]; 2651 } 2652 } 2653 2654 /* sort candidates indices w.r.t. their demands */ 2655 SCIPsortDownIntInt(canddemands, cands, ncands); 2656 2657 assert(capacity < 0); 2658 assert(ncands > 0); 2659 2660 /* greedily remove candidates form the list such that the needed capacity is still exceeded */ 2661 while( capacity + canddemands[ncands-1] < 0 ) 2662 { 2663 ncands--; 2664 capacity += canddemands[ncands]; 2665 assert(ncands > 0); 2666 } 2667 2668 /* compute the size (number of time steps) of the job cores */ 2669 for( c = 0; c < ncands; ++c ) 2670 { 2671 var = vars[cands[c]]; 2672 assert(var != NULL); 2673 2674 duration = durations[cands[c]]; 2675 2676 ect = SCIPconvertRealToInt(scip, SCIPgetVarLbAtIndex(scip, var, bdchgidx, FALSE)) + duration; 2677 lst = SCIPconvertRealToInt(scip, SCIPgetVarUbAtIndex(scip, var, bdchgidx, FALSE)); 2678 2679 maxlst = MAX(maxlst, lst); 2680 minect = MIN(minect, ect); 2681 assert(maxlst < minect); 2682 } 2683 2684 SCIPdebugMsg(scip, "infer peak %d, relaxed peak %d, lst %d, ect %d\n", inferpeak, relaxedpeak, maxlst, minect); 2685 assert(inferpeak >= maxlst); 2686 assert(inferpeak < minect); 2687 2688 /* check if the collect variable are sufficient to prove the relaxed bound (relaxedpeak) */ 2689 if( relaxedpeak < inferpeak ) 2690 { 2691 inferpeak = MAX(maxlst, relaxedpeak); 2692 } 2693 else if( relaxedpeak > inferpeak ) 2694 { 2695 inferpeak = MIN(minect-1, relaxedpeak); 2696 } 2697 assert(inferpeak >= hmin); 2698 assert(inferpeak < hmax); 2699 assert(inferpeak >= maxlst); 2700 assert(inferpeak < minect); 2701 2702 /* post all necessary bound changes */ 2703 for( c = 0; c < ncands; ++c ) 2704 { 2705 var = vars[cands[c]]; 2706 assert(var != NULL); 2707 2708 if( usebdwidening ) 2709 { 2710 duration = durations[cands[c]]; 2711 2712 SCIP_CALL( SCIPaddConflictRelaxedLb(scip, var, bdchgidx, (SCIP_Real)(inferpeak - duration + 1)) ); 2713 SCIP_CALL( SCIPaddConflictRelaxedUb(scip, var, bdchgidx, (SCIP_Real)inferpeak) ); 2714 } 2715 else 2716 { 2717 SCIP_CALL( SCIPaddConflictLb(scip, var, bdchgidx) ); 2718 SCIP_CALL( SCIPaddConflictUb(scip, var, bdchgidx) ); 2719 } 2720 2721 if( explanation != NULL ) 2722 explanation[cands[c]] = TRUE; 2723 } 2724 2725 SCIPfreeBufferArray(scip, &canddemands); 2726 SCIPfreeBufferArray(scip, &cands); 2727 } 2728 2729 SCIPfreeBufferArray(scip, &reported); 2730 2731 if( provedpeak != NULL ) 2732 *provedpeak = inferpeak; 2733 2734 return SCIP_OKAY; 2735 } 2736 2737 #if 0 2738 /** repropagation of edge finding algorithm simplified version from Petr Vilim only a small subset is reported such that 2739 * energy in total and for bound change is enough 2740 */ 2741 static 2742 SCIP_RETCODE resolvePropagationEdgeFinding( 2743 SCIP* scip, /**< SCIP data structure */ 2744 int nvars, /**< number of start time variables (activities) */ 2745 SCIP_VAR** vars, /**< array of start time variables */ 2746 int* durations, /**< array of durations */ 2747 int hmin, /**< left bound of time axis to be considered (including hmin) */ 2748 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 2749 SCIP_VAR* infervar, /**< variable whose bound change is to be explained */ 2750 INFERINFO inferinfo, /**< inference info containing position of correct bdchgids */ 2751 SCIP_BOUNDTYPE boundtype, /**< the type of the changed bound (lower or upper bound) */ 2752 SCIP_BDCHGIDX* bdchgidx, /**< the index of the bound change, representing the point of time where the change took place */ 2753 SCIP_Bool usebdwidening, /**< should bound widening be used during conflict analysis? */ 2754 SCIP_Bool* explanation /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */ 2755 ) 2756 { 2757 int est; 2758 int lct; 2759 int j; 2760 2761 /* ???????????????????? do bound widening */ 2762 2763 assert(scip != NULL); 2764 assert(nvars > 0); 2765 assert(inferInfoGetProprule(inferinfo) == PROPRULE_2_EDGEFINDING); 2766 2767 SCIPdebugMsg(scip, "repropagate edge-finding with short reasons for variable <%s>\n", SCIPvarGetName(infervar)); 2768 2769 if( boundtype == SCIP_BOUNDTYPE_LOWER ) 2770 { 2771 SCIP_CALL( SCIPaddConflictLb(scip, infervar, bdchgidx) ); 2772 } 2773 else 2774 { 2775 SCIP_CALL( SCIPaddConflictUb(scip, infervar, bdchgidx) ); 2776 } 2777 2778 est = inferInfoGetData1(inferinfo); 2779 lct = inferInfoGetData2(inferinfo); 2780 assert(est < lct); 2781 2782 /* collect the energies of all variables in [est_omega, lct_omega] */ 2783 for( j = 0; j < nvars; ++j ) 2784 { 2785 SCIP_VAR* var; 2786 SCIP_Bool left; 2787 SCIP_Bool right; 2788 int duration; 2789 int lb; 2790 int ub; 2791 2792 var = vars[j]; 2793 assert(var != NULL); 2794 2795 if( var == infervar ) 2796 { 2797 if( explanation != NULL ) 2798 explanation[j] = TRUE; 2799 2800 continue; 2801 } 2802 2803 lb = SCIPconvertRealToInt(scip, SCIPgetVarLbAtIndex(scip, var, bdchgidx, FALSE)); 2804 ub = SCIPconvertRealToInt(scip, SCIPgetVarUbAtIndex(scip, var, bdchgidx, FALSE)); 2805 2806 duration = durations[j]; 2807 assert(duration > 0); 2808 2809 /* in case the earliest start time is equal to hmin we have to also consider the jobs which run in that region 2810 * since we use adjusted jobs during the propagation 2811 */ 2812 left = (est == hmin && lb + duration > hmin) || lb >= est; 2813 2814 /* in case the latest completion time is equal to hmax we have to also consider the jobs which run in that region 2815 * since we use adjusted jobs during the propagation 2816 */ 2817 right = (lct == hmax && ub < hmax) || ub + duration <= lct; 2818 2819 /* store all jobs running in [est_omega; lct_omega] */ 2820 if( left && right ) 2821 { 2822 /* check if bound widening should be used */ 2823 if( usebdwidening ) 2824 { 2825 SCIP_CALL( SCIPaddConflictRelaxedLb(scip, var, bdchgidx, (SCIP_Real)(lct - duration)) ); 2826 SCIP_CALL( SCIPaddConflictRelaxedUb(scip, var, bdchgidx, (SCIP_Real)(est)) ); 2827 } 2828 else 2829 { 2830 SCIP_CALL( SCIPaddConflictLb(scip, var, bdchgidx) ); 2831 SCIP_CALL( SCIPaddConflictUb(scip, var, bdchgidx) ); 2832 } 2833 2834 if( explanation != NULL ) 2835 explanation[j] = TRUE; 2836 } 2837 } 2838 2839 return SCIP_OKAY; 2840 } 2841 #endif 2842 2843 /** compute the minimum overlaps w.r.t. the duration of the job and the time window [begin,end) */ 2844 static 2845 int computeOverlap( 2846 int begin, /**< begin of the times interval */ 2847 int end, /**< end of time interval */ 2848 int est, /**< earliest start time */ 2849 int lst, /**< latest start time */ 2850 int duration /**< duration of the job */ 2851 ) 2852 { 2853 int left; 2854 int right; 2855 int ect; 2856 int lct; 2857 2858 ect = est + duration; 2859 lct = lst + duration; 2860 2861 /* check if job runs completely within [begin,end) */ 2862 if( lct <= end && est >= begin ) 2863 return duration; 2864 2865 assert(lst <= end && ect >= begin); 2866 2867 left = ect - begin; 2868 assert(left > 0); 2869 2870 right = end - lst; 2871 assert(right > 0); 2872 2873 return MIN3(left, right, end - begin); 2874 } 2875 2876 /** an overload was detected due to the time-time edge-finding propagate; initialized conflict analysis, add an initial 2877 * reason 2878 * 2879 * @note the conflict analysis is not performend, only the initialized SCIP_Bool pointer is set to TRUE 2880 */ 2881 static 2882 SCIP_RETCODE analyzeEnergyRequirement( 2883 SCIP* scip, /**< SCIP data structure */ 2884 int nvars, /**< number of start time variables (activities) */ 2885 SCIP_VAR** vars, /**< array of start time variables */ 2886 int* durations, /**< array of durations */ 2887 int* demands, /**< array of demands */ 2888 int capacity, /**< capacity of the cumulative condition */ 2889 int begin, /**< begin of the time window */ 2890 int end, /**< end of the time window */ 2891 SCIP_VAR* infervar, /**< variable which was propagate, or NULL */ 2892 SCIP_BOUNDTYPE boundtype, /**< the type of the changed bound (lower or upper bound) */ 2893 SCIP_BDCHGIDX* bdchgidx, /**< the index of the bound change, representing the point of time where the change took place */ 2894 SCIP_Real relaxedbd, /**< the relaxed bound which is sufficient to be explained */ 2895 SCIP_Bool usebdwidening, /**< should bound widening be used during conflict analysis? */ 2896 SCIP_Bool* explanation /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */ 2897 ) 2898 { 2899 int* locenergies; 2900 int* overlaps; 2901 int* idxs; 2902 2903 SCIP_Longint requiredenergy; 2904 int v; 2905 2906 SCIP_CALL( SCIPallocBufferArray(scip, &locenergies, nvars) ); 2907 SCIP_CALL( SCIPallocBufferArray(scip, &overlaps, nvars) ); 2908 SCIP_CALL( SCIPallocBufferArray(scip, &idxs, nvars) ); 2909 2910 /* energy which needs be explained */ 2911 requiredenergy = ((SCIP_Longint) end - begin) * capacity; 2912 2913 SCIPdebugMsg(scip, "analysis energy load in [%d,%d) (capacity %d, energy %" SCIP_LONGINT_FORMAT ")\n", begin, end, capacity, requiredenergy); 2914 2915 /* collect global contribution and adjusted the required energy by the amount of energy the inference variable 2916 * takes 2917 */ 2918 for( v = 0; v < nvars; ++v ) 2919 { 2920 SCIP_VAR* var; 2921 int glbenergy; 2922 int duration; 2923 int demand; 2924 int est; 2925 int lst; 2926 2927 var = vars[v]; 2928 assert(var != NULL); 2929 2930 locenergies[v] = 0; 2931 overlaps[v] = 0; 2932 idxs[v] = v; 2933 2934 demand = demands[v]; 2935 assert(demand > 0); 2936 2937 duration = durations[v]; 2938 assert(duration > 0); 2939 2940 /* check if the variable equals the inference variable (the one which was propagated) */ 2941 if( infervar == var ) 2942 { 2943 int overlap; 2944 int right; 2945 int left; 2946 2947 assert(relaxedbd != SCIP_UNKNOWN); /*lint !e777*/ 2948 2949 SCIPdebugMsg(scip, "inference variable <%s>[%g,%g] %s %g (duration %d, demand %d)\n", 2950 SCIPvarGetName(var), SCIPgetVarLbAtIndex(scip, var, bdchgidx, FALSE), SCIPgetVarUbAtIndex(scip, var, bdchgidx, FALSE), 2951 boundtype == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=", relaxedbd, duration, demand); 2952 2953 /* compute the amount of energy which needs to be available for enforcing the propagation and report the bound 2954 * which is necessary from the inference variable 2955 */ 2956 if( boundtype == SCIP_BOUNDTYPE_UPPER ) 2957 { 2958 int lct; 2959 2960 /* get the latest start time of the infer start time variable before the propagation took place */ 2961 lst = SCIPconvertRealToInt(scip, SCIPgetVarUbAtIndex(scip, var, bdchgidx, FALSE)); 2962 2963 /* the latest start time of the inference start time variable before the propagation needs to be smaller as 2964 * the end of the time interval; meaning the job needs be overlap with the time interval in case the job is 2965 * scheduled w.r.t. its latest start time 2966 */ 2967 assert(lst < end); 2968 2969 /* compute the overlap of the job in case it would be scheduled w.r.t. its latest start time and the time 2970 * interval (before the propagation) 2971 */ 2972 right = MIN3(end - lst, end - begin, duration); 2973 2974 /* the job needs to overlap with the interval; otherwise the propagation w.r.t. this time window is not valid */ 2975 assert(right > 0); 2976 2977 lct = SCIPconvertRealToInt(scip, relaxedbd) + duration; 2978 assert(begin <= lct); 2979 assert(bdchgidx == NULL || SCIPconvertRealToInt(scip, SCIPgetVarUbAtIndex(scip, var, bdchgidx, TRUE)) < begin); 2980 2981 /* compute the overlap of the job after the propagation but considering the relaxed bound */ 2982 left = MIN(lct - begin + 1, end - begin); 2983 assert(left > 0); 2984 2985 /* compute the minimum overlap; */ 2986 overlap = MIN(left, right); 2987 assert(overlap > 0); 2988 assert(overlap <= end - begin); 2989 assert(overlap <= duration); 2990 2991 if( usebdwidening ) 2992 { 2993 assert(SCIPconvertRealToInt(scip, SCIPgetVarUbAtIndex(scip, var, bdchgidx, FALSE)) <= (end - overlap)); 2994 SCIP_CALL( SCIPaddConflictRelaxedUb(scip, var, bdchgidx, (SCIP_Real)(end - overlap)) ); 2995 } 2996 else 2997 { 2998 SCIP_CALL( SCIPaddConflictUb(scip, var, bdchgidx) ); 2999 } 3000 } 3001 else 3002 { 3003 int ect; 3004 3005 assert(boundtype == SCIP_BOUNDTYPE_LOWER); 3006 3007 /* get the earliest completion time of the infer start time variable before the propagation took place */ 3008 ect = SCIPconvertRealToInt(scip, SCIPgetVarLbAtIndex(scip, var, bdchgidx, FALSE)) + duration; 3009 3010 /* the earliest start time of the inference start time variable before the propagation needs to be larger as 3011 * than the beginning of the time interval; meaning the job needs be overlap with the time interval in case 3012 * the job is scheduled w.r.t. its earliest start time 3013 */ 3014 assert(ect > begin); 3015 3016 /* compute the overlap of the job in case it would be scheduled w.r.t. its earliest start time and the time 3017 * interval (before the propagation) 3018 */ 3019 left = MIN3(ect - begin, end - begin, duration); 3020 3021 /* the job needs to overlap with the interval; otherwise the propagation w.r.t. this time window is not valid */ 3022 assert(left > 0); 3023 3024 est = SCIPconvertRealToInt(scip, relaxedbd); 3025 assert(end >= est); 3026 assert(bdchgidx == NULL || end - SCIPgetVarLbAtIndex(scip, var, bdchgidx, TRUE) < duration); 3027 3028 /* compute the overlap of the job after the propagation but considering the relaxed bound */ 3029 right = MIN(end - est + 1, end - begin); 3030 assert(right > 0); 3031 3032 /* compute the minimum overlap */ 3033 overlap = MIN(left, right); 3034 assert(overlap > 0); 3035 assert(overlap <= end - begin); 3036 assert(overlap <= duration); 3037 3038 if( usebdwidening ) 3039 { 3040 assert(SCIPconvertRealToInt(scip, SCIPgetVarLbAtIndex(scip, var, bdchgidx, FALSE)) >= (begin + overlap - duration)); 3041 SCIP_CALL( SCIPaddConflictRelaxedLb(scip, var, bdchgidx, (SCIP_Real)(begin + overlap - duration)) ); 3042 } 3043 else 3044 { 3045 SCIP_CALL( SCIPaddConflictLb(scip, var, bdchgidx) ); 3046 } 3047 } 3048 3049 /* subtract the amount of energy which is available due to the overlap of the inference start time */ 3050 requiredenergy -= (SCIP_Longint) overlap * demand; 3051 3052 if( explanation != NULL ) 3053 explanation[v] = TRUE; 3054 3055 continue; 3056 } 3057 3058 /* global time points */ 3059 est = SCIPconvertRealToInt(scip, SCIPvarGetLbGlobal(var)); 3060 lst = SCIPconvertRealToInt(scip, SCIPvarGetUbGlobal(var)); 3061 3062 glbenergy = 0; 3063 3064 /* check if the has any overlap w.r.t. global bound; meaning some parts of the job will run for sure within the 3065 * time window 3066 */ 3067 if( est + duration > begin && lst < end ) 3068 { 3069 /* evaluated global contribution */ 3070 glbenergy = computeOverlap(begin, end, est, lst, duration) * demand; 3071 3072 /* remove the globally available energy form the required energy */ 3073 requiredenergy -= glbenergy; 3074 3075 if( explanation != NULL ) 3076 explanation[v] = TRUE; 3077 } 3078 3079 /* local time points */ 3080 est = SCIPconvertRealToInt(scip, SCIPgetVarLbAtIndex(scip, var, bdchgidx, FALSE)); 3081 lst = SCIPconvertRealToInt(scip, SCIPgetVarUbAtIndex(scip, var, bdchgidx, FALSE)); 3082 3083 /* check if the job has any overlap w.r.t. local bound; meaning some parts of the job will run for sure within the 3084 * time window 3085 */ 3086 if( est + duration > begin && lst < end ) 3087 { 3088 overlaps[v] = computeOverlap(begin, end, est, lst, duration); 3089 3090 /* evaluated additionally local energy contribution */ 3091 locenergies[v] = overlaps[v] * demand - glbenergy; 3092 assert(locenergies[v] >= 0); 3093 } 3094 } 3095 3096 /* sort the variable contributions w.r.t. additional local energy contributions */ 3097 SCIPsortDownIntIntInt(locenergies, overlaps, idxs, nvars); 3098 3099 /* add local energy contributions until an overload is implied */ 3100 for( v = 0; v < nvars && requiredenergy >= 0; ++v ) 3101 { 3102 SCIP_VAR* var; 3103 int duration; 3104 int overlap; 3105 int relaxlb; 3106 int relaxub; 3107 int idx; 3108 3109 idx = idxs[v]; 3110 assert(idx >= 0 && idx < nvars); 3111 3112 var = vars[idx]; 3113 assert(var != NULL); 3114 assert(var != infervar); 3115 3116 duration = durations[idx]; 3117 assert(duration > 0); 3118 3119 overlap = overlaps[v]; 3120 assert(overlap > 0); 3121 3122 requiredenergy -= locenergies[v]; 3123 3124 if( requiredenergy < -1 ) 3125 { 3126 int demand; 3127 3128 demand = demands[idx]; 3129 assert(demand > 0); 3130 3131 overlap += (int)((requiredenergy + 1) / demand); 3132 3133 #ifndef NDEBUG 3134 requiredenergy += locenergies[v]; 3135 requiredenergy -= (SCIP_Longint) overlap * demand; 3136 assert(requiredenergy < 0); 3137 #endif 3138 } 3139 assert(overlap > 0); 3140 3141 relaxlb = begin - duration + overlap; 3142 relaxub = end - overlap; 3143 3144 SCIPdebugMsg(scip, "variable <%s> glb=[%g,%g] loc=[%g,%g], conf=[%g,%g], added=[%d,%d] (demand %d, duration %d)\n", 3145 SCIPvarGetName(var), 3146 SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var), 3147 SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var), 3148 SCIPgetConflictVarLb(scip, var), SCIPgetConflictVarUb(scip, var), 3149 relaxlb, relaxub, demands[idx], duration); 3150 3151 SCIP_CALL( SCIPaddConflictRelaxedLb(scip, var, bdchgidx, (SCIP_Real)relaxlb) ); 3152 SCIP_CALL( SCIPaddConflictRelaxedUb(scip, var, bdchgidx, (SCIP_Real)relaxub) ); 3153 3154 if( explanation != NULL ) 3155 explanation[idx] = TRUE; 3156 } 3157 3158 assert(requiredenergy < 0); 3159 3160 SCIPfreeBufferArray(scip, &idxs); 3161 SCIPfreeBufferArray(scip, &overlaps); 3162 SCIPfreeBufferArray(scip, &locenergies); 3163 3164 return SCIP_OKAY; 3165 } 3166 3167 /** resolve propagation w.r.t. the cumulative condition */ 3168 static 3169 SCIP_RETCODE respropCumulativeCondition( 3170 SCIP* scip, /**< SCIP data structure */ 3171 int nvars, /**< number of start time variables (activities) */ 3172 SCIP_VAR** vars, /**< array of start time variables */ 3173 int* durations, /**< array of durations */ 3174 int* demands, /**< array of demands */ 3175 int capacity, /**< cumulative capacity */ 3176 int hmin, /**< left bound of time axis to be considered (including hmin) */ 3177 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 3178 SCIP_VAR* infervar, /**< the conflict variable whose bound change has to be resolved */ 3179 INFERINFO inferinfo, /**< the user information */ 3180 SCIP_BOUNDTYPE boundtype, /**< the type of the changed bound (lower or upper bound) */ 3181 SCIP_BDCHGIDX* bdchgidx, /**< the index of the bound change, representing the point of time where the change took place */ 3182 SCIP_Real relaxedbd, /**< the relaxed bound which is sufficient to be explained */ 3183 SCIP_Bool usebdwidening, /**< should bound widening be used during conflict analysis? */ 3184 SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */ 3185 SCIP_RESULT* result /**< pointer to store the result of the propagation conflict resolving call */ 3186 ) 3187 { 3188 switch( inferInfoGetProprule(inferinfo) ) 3189 { 3190 case PROPRULE_1_CORETIMES: 3191 { 3192 int inferdemand; 3193 int inferduration; 3194 int inferpos; 3195 int inferpeak; 3196 int relaxedpeak; 3197 int provedpeak; 3198 3199 /* get the position of the inferred variable in the vars array */ 3200 inferpos = inferInfoGetData1(inferinfo); 3201 if( inferpos >= nvars || vars[inferpos] != infervar ) 3202 { 3203 /* find inference variable in constraint */ 3204 for( inferpos = 0; inferpos < nvars && vars[inferpos] != infervar; ++inferpos ) 3205 {} 3206 } 3207 assert(inferpos < nvars); 3208 assert(vars[inferpos] == infervar); 3209 3210 inferdemand = demands[inferpos]; 3211 inferduration = durations[inferpos]; 3212 3213 if( boundtype == SCIP_BOUNDTYPE_UPPER ) 3214 { 3215 /* we propagated the latest start time (upper bound) step wise with a step length of at most the duration of 3216 * the inference variable 3217 */ 3218 assert(SCIPgetVarUbAtIndex(scip, infervar, bdchgidx, FALSE) - SCIPgetVarUbAtIndex(scip, infervar, bdchgidx, TRUE) < inferduration + 0.5); 3219 3220 SCIPdebugMsg(scip, "variable <%s>: upper bound changed from %g to %g (relaxed %g)\n", 3221 SCIPvarGetName(infervar), SCIPgetVarUbAtIndex(scip, infervar, bdchgidx, FALSE), 3222 SCIPgetVarUbAtIndex(scip, infervar, bdchgidx, TRUE), relaxedbd); 3223 3224 /* get the inference peak that the time point which lead to the that propagtion */ 3225 inferpeak = inferInfoGetData2(inferinfo); 3226 /* the bound passed back to be resolved might be tighter as the bound propagted by the core time propagator; 3227 * this can happen if the variable is not activ and aggregated to an activ variable with a scale != 1.0 3228 */ 3229 assert(SCIPconvertRealToInt(scip, SCIPgetVarUbAtIndex(scip, infervar, bdchgidx, TRUE)) + inferduration <= inferpeak); 3230 relaxedpeak = SCIPconvertRealToInt(scip, relaxedbd) + inferduration; 3231 3232 /* make sure that the relaxed peak is part of the effective horizon */ 3233 relaxedpeak = MIN(relaxedpeak, hmax-1); 3234 3235 /* make sure that relaxed peak is not larger than the infer peak 3236 * 3237 * This can happen in case the variable is not an active variable! 3238 */ 3239 relaxedpeak = MAX(relaxedpeak, inferpeak); 3240 assert(relaxedpeak >= inferpeak); 3241 assert(relaxedpeak >= hmin); 3242 } 3243 else 3244 { 3245 assert(boundtype == SCIP_BOUNDTYPE_LOWER); 3246 3247 SCIPdebugMsg(scip, "variable <%s>: lower bound changed from %g to %g (relaxed %g)\n", 3248 SCIPvarGetName(infervar), SCIPgetVarLbAtIndex(scip, infervar, bdchgidx, FALSE), 3249 SCIPgetVarLbAtIndex(scip, infervar, bdchgidx, TRUE), relaxedbd); 3250 3251 /* get the time interval where the job could not be scheduled */ 3252 inferpeak = inferInfoGetData2(inferinfo); 3253 /* the bound passed back to be resolved might be tighter as the bound propagted by the core time propagator; 3254 * this can happen if the variable is not activ and aggregated to an activ variable with a scale != 1.0 3255 */ 3256 assert(SCIPconvertRealToInt(scip, SCIPgetVarLbAtIndex(scip, infervar, bdchgidx, TRUE)) - 1 >= inferpeak); 3257 relaxedpeak = SCIPconvertRealToInt(scip, relaxedbd) - 1; 3258 3259 /* make sure that the relaxed peak is part of the effective horizon */ 3260 relaxedpeak = MAX(relaxedpeak, hmin); 3261 3262 /* make sure that relaxed peak is not larger than the infer peak 3263 * 3264 * This can happen in case the variable is not an active variable! 3265 */ 3266 relaxedpeak = MIN(relaxedpeak, inferpeak); 3267 assert(relaxedpeak < hmax); 3268 } 3269 3270 /* resolves the propagation of the core time algorithm */ 3271 SCIP_CALL( resolvePropagationCoretimes(scip, nvars, vars, durations, demands, capacity, hmin, hmax, 3272 infervar, inferdemand, inferpeak, relaxedpeak, bdchgidx, usebdwidening, &provedpeak, explanation) ); 3273 3274 if( boundtype == SCIP_BOUNDTYPE_UPPER ) 3275 { 3276 if( usebdwidening ) 3277 { 3278 SCIP_CALL( SCIPaddConflictRelaxedUb(scip, infervar, NULL, (SCIP_Real)provedpeak) ); 3279 } 3280 else 3281 { 3282 /* old upper bound of variable itself is part of the explanation */ 3283 SCIP_CALL( SCIPaddConflictUb(scip, infervar, bdchgidx) ); 3284 } 3285 } 3286 else 3287 { 3288 assert(boundtype == SCIP_BOUNDTYPE_LOWER); 3289 3290 if( usebdwidening ) 3291 { 3292 SCIP_CALL( SCIPaddConflictRelaxedLb(scip, infervar, bdchgidx, (SCIP_Real)(provedpeak - inferduration + 1)) ); 3293 } 3294 else 3295 { 3296 /* old lower bound of variable itself is part of the explanation */ 3297 SCIP_CALL( SCIPaddConflictLb(scip, infervar, bdchgidx) ); 3298 } 3299 } 3300 3301 if( explanation != NULL ) 3302 explanation[inferpos] = TRUE; 3303 3304 break; 3305 } 3306 case PROPRULE_2_EDGEFINDING: 3307 case PROPRULE_3_TTEF: 3308 { 3309 int begin; 3310 int end; 3311 3312 begin = inferInfoGetData1(inferinfo); 3313 end = inferInfoGetData2(inferinfo); 3314 assert(begin < end); 3315 3316 begin = MAX(begin, hmin); 3317 end = MIN(end, hmax); 3318 3319 SCIP_CALL( analyzeEnergyRequirement(scip, nvars, vars, durations, demands, capacity, 3320 begin, end, infervar, boundtype, bdchgidx, relaxedbd, usebdwidening, explanation) ); 3321 3322 break; 3323 } 3324 3325 case PROPRULE_0_INVALID: 3326 default: 3327 SCIPerrorMessage("invalid inference information %d\n", inferInfoGetProprule(inferinfo)); 3328 SCIPABORT(); 3329 return SCIP_INVALIDDATA; /*lint !e527*/ 3330 } 3331 3332 (*result) = SCIP_SUCCESS; 3333 3334 return SCIP_OKAY; 3335 } 3336 3337 /**@} */ 3338 3339 3340 /**@name Enforcement methods 3341 * 3342 * @{ 3343 */ 3344 3345 /** apply all fixings which are given by the alternative bounds */ 3346 static 3347 SCIP_RETCODE applyAlternativeBoundsBranching( 3348 SCIP* scip, /**< SCIP data structure */ 3349 SCIP_VAR** vars, /**< array of active variables */ 3350 int nvars, /**< number of active variables */ 3351 int* alternativelbs, /**< alternative lower bounds */ 3352 int* alternativeubs, /**< alternative lower bounds */ 3353 int* downlocks, /**< number of constraints with down lock participating by the computation */ 3354 int* uplocks, /**< number of constraints with up lock participating by the computation */ 3355 SCIP_Bool* branched /**< pointer to store if a branching was applied */ 3356 ) 3357 { 3358 int v; 3359 3360 for( v = 0; v < nvars; ++v ) 3361 { 3362 SCIP_VAR* var; 3363 SCIP_Real objval; 3364 3365 var = vars[v]; 3366 assert(var != NULL); 3367 3368 objval = SCIPvarGetObj(var); 3369 3370 if( SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL) == downlocks[v] && !SCIPisNegative(scip, objval) ) 3371 { 3372 int ub; 3373 3374 ub = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(var)); 3375 3376 if( alternativelbs[v] <= ub ) 3377 { 3378 SCIP_CALL( SCIPbranchVarHole(scip, var, SCIPvarGetLbLocal(var), (SCIP_Real)alternativelbs[v], NULL, NULL) ); 3379 (*branched) = TRUE; 3380 3381 SCIPdebugMsg(scip, "variable <%s> branched domain hole (%g,%d)\n", SCIPvarGetName(var), 3382 SCIPvarGetLbLocal(var), alternativelbs[v]); 3383 3384 return SCIP_OKAY; 3385 } 3386 } 3387 3388 if( SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL) == uplocks[v] && !SCIPisPositive(scip, objval) ) 3389 { 3390 int lb; 3391 3392 lb = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(var)); 3393 3394 if( alternativeubs[v] >= lb ) 3395 { 3396 SCIP_CALL( SCIPbranchVarHole(scip, var, (SCIP_Real)alternativeubs[v], SCIPvarGetUbLocal(var), NULL, NULL) ); 3397 (*branched) = TRUE; 3398 3399 SCIPdebugMsg(scip, "variable <%s> branched domain hole (%d,%g)\n", SCIPvarGetName(var), 3400 alternativeubs[v], SCIPvarGetUbLocal(var)); 3401 3402 return SCIP_OKAY; 3403 } 3404 } 3405 } 3406 3407 return SCIP_OKAY; 3408 } 3409 3410 /** remove the capacity requirments for all job which start at the curtime */ 3411 static 3412 void subtractStartingJobDemands( 3413 SCIP_CONSDATA* consdata, /**< constraint data */ 3414 int curtime, /**< current point in time */ 3415 int* starttimes, /**< array of start times */ 3416 int* startindices, /**< permutation with respect to the start times */ 3417 int* freecapacity, /**< pointer to store the resulting free capacity */ 3418 int* idx, /**< pointer to index in start time array */ 3419 int nvars /**< number of vars in array of starttimes and startindices */ 3420 ) 3421 { 3422 #if defined SCIP_DEBUG && !defined NDEBUG 3423 int oldidx; 3424 3425 assert(idx != NULL); 3426 oldidx = *idx; 3427 #else 3428 assert(idx != NULL); 3429 #endif 3430 3431 assert(starttimes != NULL); 3432 assert(starttimes != NULL); 3433 assert(freecapacity != NULL); 3434 assert(starttimes[*idx] == curtime); 3435 assert(consdata->demands != NULL); 3436 assert(freecapacity != idx); 3437 3438 /* subtract all capacity needed up to this point */ 3439 (*freecapacity) -= consdata->demands[startindices[*idx]]; 3440 3441 while( (*idx)+1 < nvars && starttimes[(*idx)+1] == curtime ) 3442 { 3443 ++(*idx); 3444 (*freecapacity) -= consdata->demands[startindices[(*idx)]]; 3445 assert(freecapacity != idx); 3446 } 3447 #ifdef SCIP_DEBUG 3448 assert(oldidx <= *idx); 3449 #endif 3450 } 3451 3452 /** add the capacity requirments for all job which end at the curtime */ 3453 static 3454 void addEndingJobDemands( 3455 SCIP_CONSDATA* consdata, /**< constraint data */ 3456 int curtime, /**< current point in time */ 3457 int* endtimes, /**< array of end times */ 3458 int* endindices, /**< permutation with rspect to the end times */ 3459 int* freecapacity, /**< pointer to store the resulting free capacity */ 3460 int* idx, /**< pointer to index in end time array */ 3461 int nvars /**< number of vars in array of starttimes and startindices */ 3462 ) 3463 { 3464 #if defined SCIP_DEBUG && !defined NDEBUG 3465 int oldidx; 3466 oldidx = *idx; 3467 #endif 3468 3469 /* free all capacity usages of jobs the are no longer running */ 3470 while( endtimes[*idx] <= curtime && *idx < nvars) 3471 { 3472 (*freecapacity) += consdata->demands[endindices[*idx]]; 3473 ++(*idx); 3474 } 3475 3476 #ifdef SCIP_DEBUG 3477 assert(oldidx <= *idx); 3478 #endif 3479 } 3480 3481 /** computes a point in time when the capacity is exceeded returns hmax if this does not happen */ 3482 static 3483 SCIP_RETCODE computePeak( 3484 SCIP* scip, /**< SCIP data structure */ 3485 SCIP_CONSDATA* consdata, /**< constraint handler data */ 3486 SCIP_SOL* sol, /**< primal solution, or NULL for current LP/pseudo solution */ 3487 int* timepoint /**< pointer to store the time point of the peak */ 3488 ) 3489 { 3490 int* starttimes; /* stores when each job is starting */ 3491 int* endtimes; /* stores when each job ends */ 3492 int* startindices; /* we will sort the startsolvalues, thus we need to know wich index of a job it corresponds to */ 3493 int* endindices; /* we will sort the endsolvalues, thus we need to know wich index of a job it corresponds to */ 3494 3495 int nvars; /* number of activities for this constraint */ 3496 int freecapacity; /* remaining capacity */ 3497 int curtime; /* point in time which we are just checking */ 3498 int endindex; /* index of endsolvalues with: endsolvalues[endindex] > curtime */ 3499 3500 int hmin; 3501 int hmax; 3502 3503 int j; 3504 3505 assert(consdata != NULL); 3506 3507 nvars = consdata->nvars; 3508 assert(nvars > 0); 3509 3510 *timepoint = consdata->hmax; 3511 3512 assert(consdata->vars != NULL); 3513 3514 SCIP_CALL( SCIPallocBufferArray(scip, &starttimes, nvars) ); 3515 SCIP_CALL( SCIPallocBufferArray(scip, &endtimes, nvars) ); 3516 SCIP_CALL( SCIPallocBufferArray(scip, &startindices, nvars) ); 3517 SCIP_CALL( SCIPallocBufferArray(scip, &endindices, nvars) ); 3518 3519 /* create event point arrays */ 3520 createSortedEventpointsSol(scip, sol, consdata->nvars, consdata->vars, consdata->durations, 3521 starttimes, endtimes, startindices, endindices); 3522 3523 endindex = 0; 3524 freecapacity = consdata->capacity; 3525 hmin = consdata->hmin; 3526 hmax = consdata->hmax; 3527 3528 /* check each startpoint of a job whether the capacity is kept or not */ 3529 for( j = 0; j < nvars; ++j ) 3530 { 3531 curtime = starttimes[j]; 3532 SCIPdebugMsg(scip, "look at %d-th job with start %d\n", j, curtime); 3533 3534 if( curtime >= hmax ) 3535 break; 3536 3537 /* remove the capacity requirments for all job which start at the curtime */ 3538 subtractStartingJobDemands(consdata, curtime, starttimes, startindices, &freecapacity, &j, nvars); 3539 3540 /* add the capacity requirments for all job which end at the curtime */ 3541 addEndingJobDemands(consdata, curtime, endtimes, endindices, &freecapacity, &endindex, nvars); 3542 3543 assert(freecapacity <= consdata->capacity); 3544 assert(endindex <= nvars); 3545 3546 /* endindex - points to the next job which will finish */ 3547 /* j - points to the last job that has been released */ 3548 3549 /* if free capacity is smaller than zero, then add branching candidates */ 3550 if( freecapacity < 0 && curtime >= hmin ) 3551 { 3552 *timepoint = curtime; 3553 break; 3554 } 3555 } /*lint --e{850}*/ 3556 3557 /* free all buffer arrays */ 3558 SCIPfreeBufferArray(scip, &endindices); 3559 SCIPfreeBufferArray(scip, &startindices); 3560 SCIPfreeBufferArray(scip, &endtimes); 3561 SCIPfreeBufferArray(scip, &starttimes); 3562 3563 return SCIP_OKAY; 3564 } 3565 3566 /** checks all cumulative constraints for infeasibility and add branching candidates to storage */ 3567 static 3568 SCIP_RETCODE collectBranchingCands( 3569 SCIP* scip, /**< SCIP data structure */ 3570 SCIP_CONS** conss, /**< constraints to be processed */ 3571 int nconss, /**< number of constraints */ 3572 SCIP_SOL* sol, /**< primal solution, or NULL for current LP/pseudo solution */ 3573 int* nbranchcands /**< pointer to store the number of branching variables */ 3574 ) 3575 { 3576 SCIP_HASHTABLE* collectedvars; 3577 int c; 3578 3579 assert(scip != NULL); 3580 assert(conss != NULL); 3581 3582 /* create a hash table */ 3583 SCIP_CALL( SCIPhashtableCreate(&collectedvars, SCIPblkmem(scip), SCIPgetNVars(scip), 3584 SCIPvarGetHashkey, SCIPvarIsHashkeyEq, SCIPvarGetHashkeyVal, NULL) ); 3585 3586 assert(scip != NULL); 3587 assert(conss != NULL); 3588 3589 for( c = 0; c < nconss; ++c ) 3590 { 3591 SCIP_CONS* cons; 3592 SCIP_CONSDATA* consdata; 3593 3594 int curtime; 3595 int j; 3596 3597 cons = conss[c]; 3598 assert(cons != NULL); 3599 3600 if( !SCIPconsIsActive(cons) ) 3601 continue; 3602 3603 consdata = SCIPconsGetData(cons); 3604 assert(consdata != NULL); 3605 3606 /* get point in time when capacity is exceeded */ 3607 SCIP_CALL( computePeak(scip, consdata, sol, &curtime) ); 3608 3609 if( curtime < consdata->hmin || curtime >= consdata->hmax ) 3610 continue; 3611 3612 /* report all variables that are running at that point in time */ 3613 for( j = 0; j < consdata->nvars; ++j ) 3614 { 3615 SCIP_VAR* var; 3616 int lb; 3617 int ub; 3618 3619 var = consdata->vars[j]; 3620 assert(var != NULL); 3621 3622 /* check if the variable was already added */ 3623 if( SCIPhashtableExists(collectedvars, (void*)var) ) 3624 continue; 3625 3626 lb = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(var)); 3627 ub = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(var)); 3628 3629 if( lb <= curtime && ub + consdata->durations[j] > curtime && lb < ub ) 3630 { 3631 SCIP_Real solval; 3632 SCIP_Real score; 3633 3634 solval = SCIPgetSolVal(scip, sol, var); 3635 score = MIN(solval - lb, ub - solval) / ((SCIP_Real)ub-lb); 3636 3637 SCIPdebugMsg(scip, "add var <%s> to branch cand storage\n", SCIPvarGetName(var)); 3638 SCIP_CALL( SCIPaddExternBranchCand(scip, var, score, lb + (ub - lb) / 2.0 + 0.2) ); 3639 (*nbranchcands)++; 3640 3641 SCIP_CALL( SCIPhashtableInsert(collectedvars, var) ); 3642 } 3643 } 3644 } 3645 3646 SCIPhashtableFree(&collectedvars); 3647 3648 SCIPdebugMsg(scip, "found %d branching candidates\n", *nbranchcands); 3649 3650 return SCIP_OKAY; 3651 } 3652 3653 /** enforcement of an LP, pseudo, or relaxation solution */ 3654 static 3655 SCIP_RETCODE enforceSolution( 3656 SCIP* scip, /**< SCIP data structure */ 3657 SCIP_CONS** conss, /**< constraints to be processed */ 3658 int nconss, /**< number of constraints */ 3659 SCIP_SOL* sol, /**< solution to enforce (NULL for LP or pseudo solution) */ 3660 SCIP_Bool branch, /**< should branching candidates be collected */ 3661 SCIP_RESULT* result /**< pointer to store the result */ 3662 ) 3663 { 3664 if( branch ) 3665 { 3666 int nbranchcands; 3667 3668 nbranchcands = 0; 3669 SCIP_CALL( collectBranchingCands(scip, conss, nconss, sol, &nbranchcands) ); 3670 3671 if( nbranchcands > 0 ) 3672 (*result) = SCIP_INFEASIBLE; 3673 } 3674 else 3675 { 3676 SCIP_Bool violated; 3677 int c; 3678 3679 violated = FALSE; 3680 3681 /* first check if a constraints is violated */ 3682 for( c = 0; c < nconss && !violated; ++c ) 3683 { 3684 SCIP_CONS* cons; 3685 3686 cons = conss[c]; 3687 assert(cons != NULL); 3688 3689 SCIP_CALL( checkCons(scip, cons, sol, &violated, FALSE) ); 3690 } 3691 3692 if( violated ) 3693 (*result) = SCIP_INFEASIBLE; 3694 } 3695 3696 return SCIP_OKAY; 3697 } 3698 3699 /**@} */ 3700 3701 /**@name Propagation 3702 * 3703 * @{ 3704 */ 3705 3706 /** check if cumulative constraint is independently of all other constraints */ 3707 static 3708 SCIP_Bool isConsIndependently( 3709 SCIP_CONS* cons /**< cumulative constraint */ 3710 ) 3711 { 3712 SCIP_CONSDATA* consdata; 3713 SCIP_VAR** vars; 3714 SCIP_Bool* downlocks; 3715 SCIP_Bool* uplocks; 3716 int nvars; 3717 int v; 3718 3719 consdata = SCIPconsGetData(cons); 3720 assert(consdata != NULL); 3721 3722 nvars = consdata->nvars; 3723 vars = consdata->vars; 3724 downlocks = consdata->downlocks; 3725 uplocks = consdata->uplocks; 3726 3727 /* check if the cumulative constraint has the only locks on the involved variables */ 3728 for( v = 0; v < nvars; ++v ) 3729 { 3730 SCIP_VAR* var; 3731 3732 var = vars[v]; 3733 assert(var != NULL); 3734 3735 if( SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL) > (int)downlocks[v] 3736 || SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL) > (int)uplocks[v] ) 3737 return FALSE; 3738 } 3739 3740 return TRUE; 3741 } 3742 3743 /** in case the cumulative constraint is independent of every else, solve the cumulative problem and apply the fixings 3744 * (dual reductions) 3745 */ 3746 static 3747 SCIP_RETCODE solveIndependentCons( 3748 SCIP* scip, /**< SCIP data structure */ 3749 SCIP_CONS* cons, /**< cumulative constraint */ 3750 SCIP_Longint maxnodes, /**< number of branch-and-bound nodes to solve an independent cumulative constraint (-1: no limit) */ 3751 int* nchgbds, /**< pointer to store the number changed variable bounds */ 3752 int* nfixedvars, /**< pointer to count number of fixings */ 3753 int* ndelconss, /**< pointer to count number of deleted constraints */ 3754 SCIP_Bool* cutoff, /**< pointer to store if the constraint is infeasible */ 3755 SCIP_Bool* unbounded /**< pointer to store if the constraint is unbounded */ 3756 ) 3757 { 3758 SCIP_CONSDATA* consdata; 3759 SCIP_VAR** vars; 3760 SCIP_Real* objvals; 3761 SCIP_Real* lbs; 3762 SCIP_Real* ubs; 3763 SCIP_Real timelimit; 3764 SCIP_Real memorylimit; 3765 SCIP_Bool solved; 3766 SCIP_Bool error; 3767 3768 int ncheckconss; 3769 int nvars; 3770 int v; 3771 3772 assert(scip != NULL); 3773 assert(!SCIPconsIsModifiable(cons)); 3774 assert(SCIPgetNConss(scip) > 0); 3775 3776 /* if SCIP is in probing mode or repropagation we cannot perform this dual reductions since this dual reduction 3777 * would/could end in an implication which can lead to cutoff of the/all optimal solution 3778 */ 3779 if( SCIPinProbing(scip) || SCIPinRepropagation(scip) ) 3780 return SCIP_OKAY; 3781 3782 /* constraints for which the check flag is set to FALSE, did not contribute to the lock numbers; therefore, we cannot 3783 * use the locks to decide for a dual reduction using this constraint; 3784 */ 3785 if( !SCIPconsIsChecked(cons) ) 3786 return SCIP_OKAY; 3787 3788 ncheckconss = SCIPgetNCheckConss(scip); 3789 3790 /* if the cumulative constraint is the only constraint of the original problem or the only check constraint in the 3791 * presolved problem do nothing execpt to change the parameter settings 3792 */ 3793 if( ncheckconss == 1 ) 3794 { 3795 /* shrink the minimal maximum value for the conflict length */ 3796 SCIP_CALL( SCIPsetIntParam(scip, "conflict/minmaxvars", 10) ); 3797 3798 /* use only first unique implication point */ 3799 SCIP_CALL( SCIPsetIntParam(scip, "conflict/fuiplevels", 1) ); 3800 3801 /* do not use reconversion conflicts */ 3802 SCIP_CALL( SCIPsetIntParam(scip, "conflict/reconvlevels", 0) ); 3803 3804 /* after 250 conflict we force a restart since then the variable statistics are reasonable initialized */ 3805 SCIP_CALL( SCIPsetIntParam(scip, "conflict/restartnum", 250) ); 3806 3807 /* increase the number of conflicts which induce a restart */ 3808 SCIP_CALL( SCIPsetRealParam(scip, "conflict/restartfac", 2.0) ); 3809 3810 /* weight the variable which made into a conflict */ 3811 SCIP_CALL( SCIPsetRealParam(scip, "conflict/conflictweight", 1.0) ); 3812 3813 /* do not check pseudo solution (for performance reasons) */ 3814 SCIP_CALL( SCIPsetBoolParam(scip, "constraints/disableenfops", TRUE) ); 3815 3816 /* use value based history to detect a reasonable branching point */ 3817 SCIP_CALL( SCIPsetBoolParam(scip, "history/valuebased", TRUE) ); 3818 3819 /* turn of LP relaxation */ 3820 SCIP_CALL( SCIPsetIntParam(scip, "lp/solvefreq", -1) ); 3821 3822 /* prefer the down branch in case the value based history does not suggest something */ 3823 SCIP_CALL( SCIPsetCharParam(scip, "nodeselection/childsel", 'd') ); 3824 3825 /* accept any bound change */ 3826 SCIP_CALL( SCIPsetRealParam(scip, "numerics/boundstreps", 1e-6) ); 3827 3828 /* allow for at most 10 restart, after that the value based history should be reliable */ 3829 SCIP_CALL( SCIPsetIntParam(scip, "presolving/maxrestarts", 10) ); 3830 3831 /* set priority for depth first search to highest possible value */ 3832 SCIP_CALL( SCIPsetIntParam(scip, "nodeselection/dfs/stdpriority", INT_MAX/4) ); 3833 3834 return SCIP_OKAY; 3835 } 3836 3837 consdata = SCIPconsGetData(cons); 3838 assert(consdata != NULL); 3839 3840 /* check if already tried to solve that constraint as independent sub problem; we do not want to try it again if we 3841 * fail on the first place 3842 */ 3843 if( consdata->triedsolving ) 3844 return SCIP_OKAY; 3845 3846 /* check if constraint is independently */ 3847 if( !isConsIndependently(cons) ) 3848 return SCIP_OKAY; 3849 3850 /* mark the constraint to be tried of solving it as independent sub problem; in case that is successful the 3851 * constraint is deleted; otherwise, we want to ensure that we do not try that again 3852 */ 3853 consdata->triedsolving = TRUE; 3854 3855 SCIPdebugMsg(scip, "the cumulative constraint <%s> is independent from rest of the problem (%d variables, %d constraints)\n", 3856 SCIPconsGetName(cons), SCIPgetNVars(scip), SCIPgetNConss(scip)); 3857 SCIPdebugPrintCons(scip, cons, NULL); 3858 3859 nvars = consdata->nvars; 3860 vars = consdata->vars; 3861 3862 SCIP_CALL( SCIPallocBufferArray(scip, &lbs, nvars) ); 3863 SCIP_CALL( SCIPallocBufferArray(scip, &ubs, nvars) ); 3864 SCIP_CALL( SCIPallocBufferArray(scip, &objvals, nvars) ); 3865 3866 for( v = 0; v < nvars; ++v ) 3867 { 3868 SCIP_VAR* var; 3869 3870 /* if a variables array is given, use the variable bounds otherwise the default values stored in the ests and lsts 3871 * array 3872 */ 3873 var = vars[v]; 3874 assert(var != NULL); 3875 3876 lbs[v] = SCIPvarGetLbLocal(var); 3877 ubs[v] = SCIPvarGetUbLocal(var); 3878 3879 objvals[v] = SCIPvarGetObj(var); 3880 } 3881 3882 /* check whether there is enough time and memory left */ 3883 SCIP_CALL( SCIPgetRealParam(scip, "limits/time", &timelimit) ); 3884 if( !SCIPisInfinity(scip, timelimit) ) 3885 timelimit -= SCIPgetSolvingTime(scip); 3886 SCIP_CALL( SCIPgetRealParam(scip, "limits/memory", &memorylimit) ); 3887 3888 /* substract the memory already used by the main SCIP and the estimated memory usage of external software */ 3889 if( !SCIPisInfinity(scip, memorylimit) ) 3890 { 3891 memorylimit -= SCIPgetMemUsed(scip)/1048576.0; 3892 memorylimit -= SCIPgetMemExternEstim(scip)/1048576.0; 3893 } 3894 3895 /* solve the cumulative condition separately */ 3896 SCIP_CALL( SCIPsolveCumulative(scip, nvars, lbs, ubs, objvals, consdata->durations, consdata->demands, consdata->capacity, 3897 consdata->hmin, consdata->hmax, timelimit, memorylimit, maxnodes, &solved, cutoff, unbounded, &error) ); 3898 3899 if( !(*cutoff) && !(*unbounded) && !error ) 3900 { 3901 SCIP_Bool infeasible; 3902 SCIP_Bool tightened; 3903 SCIP_Bool allfixed; 3904 3905 allfixed = TRUE; 3906 3907 for( v = 0; v < nvars; ++v ) 3908 { 3909 /* check if variable is fixed */ 3910 if( lbs[v] + 0.5 > ubs[v] ) 3911 { 3912 SCIP_CALL( SCIPfixVar(scip, vars[v], lbs[v], &infeasible, &tightened) ); 3913 assert(!infeasible); 3914 3915 if( tightened ) 3916 { 3917 (*nfixedvars)++; 3918 consdata->triedsolving = FALSE; 3919 } 3920 } 3921 else 3922 { 3923 SCIP_CALL( SCIPtightenVarLb(scip, vars[v], lbs[v], TRUE, &infeasible, &tightened) ); 3924 assert(!infeasible); 3925 3926 if( tightened ) 3927 { 3928 (*nchgbds)++; 3929 consdata->triedsolving = FALSE; 3930 } 3931 3932 SCIP_CALL( SCIPtightenVarUb(scip, vars[v], ubs[v], TRUE, &infeasible, &tightened) ); 3933 assert(!infeasible); 3934 3935 if( tightened ) 3936 { 3937 (*nchgbds)++; 3938 consdata->triedsolving = FALSE; 3939 } 3940 3941 allfixed = FALSE; 3942 } 3943 } 3944 3945 /* if all variables are fixed, remove the cumulative constraint since it is redundant */ 3946 if( allfixed ) 3947 { 3948 SCIP_CALL( SCIPdelConsLocal(scip, cons) ); 3949 (*ndelconss)++; 3950 } 3951 } 3952 3953 SCIPfreeBufferArray(scip, &objvals); 3954 SCIPfreeBufferArray(scip, &ubs); 3955 SCIPfreeBufferArray(scip, &lbs); 3956 3957 return SCIP_OKAY; 3958 } 3959 3960 /** start conflict analysis to analysis the core insertion which is infeasible */ 3961 static 3962 SCIP_RETCODE analyseInfeasibelCoreInsertion( 3963 SCIP* scip, /**< SCIP data structure */ 3964 int nvars, /**< number of start time variables (activities) */ 3965 SCIP_VAR** vars, /**< array of start time variables */ 3966 int* durations, /**< array of durations */ 3967 int* demands, /**< array of demands */ 3968 int capacity, /**< cumulative capacity */ 3969 int hmin, /**< left bound of time axis to be considered (including hmin) */ 3970 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 3971 SCIP_VAR* infervar, /**< start time variable which lead to the infeasibilty */ 3972 int inferduration, /**< duration of the start time variable */ 3973 int inferdemand, /**< demand of the start time variable */ 3974 int inferpeak, /**< profile preak which causes the infeasibilty */ 3975 SCIP_Bool usebdwidening, /**< should bound widening be used during conflict analysis? */ 3976 SCIP_Bool* initialized, /**< pointer to store if the conflict analysis was initialized */ 3977 SCIP_Bool* explanation /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */ 3978 ) 3979 { 3980 SCIPdebugMsg(scip, "detected infeasibility due to adding a core to the core resource profile\n"); 3981 SCIPdebugMsg(scip, "variable <%s>[%g,%g] (demand %d, duration %d)\n", SCIPvarGetName(infervar), 3982 SCIPvarGetLbLocal(infervar), SCIPvarGetUbLocal(infervar), inferdemand, inferduration); 3983 3984 /* initialize conflict analysis if conflict analysis is applicable */ 3985 if( SCIPisConflictAnalysisApplicable(scip) ) 3986 { 3987 SCIP_CALL( SCIPinitConflictAnalysis(scip, SCIP_CONFTYPE_PROPAGATION, FALSE) ); 3988 3989 SCIP_CALL( resolvePropagationCoretimes(scip, nvars, vars, durations, demands, capacity, hmin, hmax, 3990 infervar, inferdemand, inferpeak, inferpeak, NULL, usebdwidening, NULL, explanation) ); 3991 3992 SCIPdebugMsg(scip, "add lower and upper bounds of variable <%s>\n", SCIPvarGetName(infervar)); 3993 3994 /* add both bound of the inference variable since these biuld the core which we could not inserted */ 3995 if( usebdwidening ) 3996 { 3997 SCIP_CALL( SCIPaddConflictRelaxedLb(scip, infervar, NULL, (SCIP_Real)(inferpeak - inferduration + 1)) ); 3998 SCIP_CALL( SCIPaddConflictRelaxedUb(scip, infervar, NULL, (SCIP_Real)inferpeak) ); 3999 } 4000 else 4001 { 4002 SCIP_CALL( SCIPaddConflictLb(scip, infervar, NULL) ); 4003 SCIP_CALL( SCIPaddConflictUb(scip, infervar, NULL) ); 4004 } 4005 4006 *initialized = TRUE; 4007 } 4008 4009 return SCIP_OKAY; 4010 } 4011 4012 /** We are using the core resource profile which contains all core except the one of the start time variable which we 4013 * want to propagate, to incease the earliest start time. This we are doing in steps of length at most the duration of 4014 * the job. The reason for that is, that this makes it later easier to resolve this propagation during the conflict 4015 * analysis 4016 */ 4017 static 4018 SCIP_RETCODE coretimesUpdateLb( 4019 SCIP* scip, /**< SCIP data structure */ 4020 int nvars, /**< number of start time variables (activities) */ 4021 SCIP_VAR** vars, /**< array of start time variables */ 4022 int* durations, /**< array of durations */ 4023 int* demands, /**< array of demands */ 4024 int capacity, /**< cumulative capacity */ 4025 int hmin, /**< left bound of time axis to be considered (including hmin) */ 4026 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 4027 SCIP_CONS* cons, /**< constraint which is propagated */ 4028 SCIP_PROFILE* profile, /**< resource profile */ 4029 int idx, /**< position of the variable to propagate */ 4030 int* nchgbds, /**< pointer to store the number of bound changes */ 4031 SCIP_Bool usebdwidening, /**< should bound widening be used during conflict analysis? */ 4032 SCIP_Bool* initialized, /**< was conflict analysis initialized */ 4033 SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */ 4034 SCIP_Bool* infeasible /**< pointer to store if the constraint is infeasible */ 4035 ) 4036 { 4037 SCIP_VAR* var; 4038 int ntimepoints; 4039 int duration; 4040 int demand; 4041 int peak; 4042 int newlb; 4043 int est; 4044 int lst; 4045 int pos; 4046 4047 var = vars[idx]; 4048 assert(var != NULL); 4049 4050 duration = durations[idx]; 4051 assert(duration > 0); 4052 4053 demand = demands[idx]; 4054 assert(demand > 0); 4055 4056 est = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(var)); 4057 lst = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(var)); 4058 ntimepoints = SCIPprofileGetNTimepoints(profile); 4059 4060 /* first we find left position of earliest start time (lower bound) in resource profile; this position gives us the 4061 * load which we have at the earliest start time (lower bound) 4062 */ 4063 (void) SCIPprofileFindLeft(profile, est, &pos); 4064 4065 SCIPdebugMsg(scip, "propagate earliest start time (lower bound) (pos %d)\n", pos); 4066 4067 /* we now trying to move the earliest start time in steps of at most "duration" length */ 4068 do 4069 { 4070 INFERINFO inferinfo; 4071 SCIP_Bool tightened; 4072 int ect; 4073 4074 #ifndef NDEBUG 4075 { 4076 /* in debug mode we check that we adjust the search position correctly */ 4077 int tmppos; 4078 4079 (void)SCIPprofileFindLeft(profile, est, &tmppos); 4080 assert(pos == tmppos); 4081 } 4082 #endif 4083 ect = est + duration; 4084 peak = -1; 4085 4086 /* we search for a peak within the core profile which conflicts with the demand of the start time variable; we 4087 * want a peak which is closest to the earliest completion time 4088 */ 4089 do 4090 { 4091 /* check if the profile load conflicts with the demand of the start time variable */ 4092 if( SCIPprofileGetLoad(profile, pos) + demand > capacity ) 4093 peak = pos; 4094 4095 pos++; 4096 } 4097 while( pos < ntimepoints && SCIPprofileGetTime(profile, pos) < ect ); 4098 4099 /* if we found no peak that means current the job could be scheduled at its earliest start time without 4100 * conflicting to the core resource profile 4101 */ 4102 /* coverity[check_after_sink] */ 4103 if( peak == -1 ) 4104 break; 4105 4106 /* the peak position gives us a time point where the start time variable is in conflict with the resource 4107 * profile. That means we have to move it to the next time point in the resource profile but at most to the 4108 * earliest completion time (the remaining move will done in the next loop) 4109 */ 4110 newlb = SCIPprofileGetTime(profile, peak+1); 4111 newlb = MIN(newlb, ect); 4112 4113 /* if the earliest start time is greater than the lst we detected an infeasibilty */ 4114 if( newlb > lst ) 4115 { 4116 SCIPdebugMsg(scip, "variable <%s>: cannot be scheduled\n", SCIPvarGetName(var)); 4117 4118 /* use conflict analysis to analysis the core insertion which was infeasible */ 4119 SCIP_CALL( analyseInfeasibelCoreInsertion(scip, nvars, vars, durations, demands, capacity, hmin, hmax, 4120 var, duration, demand, newlb-1, usebdwidening, initialized, explanation) ); 4121 4122 if( explanation != NULL ) 4123 explanation[idx] = TRUE; 4124 4125 *infeasible = TRUE; 4126 4127 break; 4128 } 4129 4130 /* construct the inference information which we are using with the conflict analysis to resolve that particular 4131 * bound change 4132 */ 4133 inferinfo = getInferInfo(PROPRULE_1_CORETIMES, idx, newlb-1); 4134 4135 /* perform the bound lower bound change */ 4136 if( inferInfoIsValid(inferinfo) ) 4137 { 4138 SCIP_CALL( SCIPinferVarLbCons(scip, var, (SCIP_Real)newlb, cons, inferInfoToInt(inferinfo), TRUE, infeasible, &tightened) ); 4139 } 4140 else 4141 { 4142 SCIP_CALL( SCIPtightenVarLb(scip, var, (SCIP_Real)newlb, TRUE, infeasible, &tightened) ); 4143 } 4144 assert(tightened); 4145 assert(!(*infeasible)); 4146 4147 SCIPdebugMsg(scip, "variable <%s> new lower bound <%d> -> <%d>\n", SCIPvarGetName(var), est, newlb); 4148 (*nchgbds)++; 4149 4150 /* for the statistic we count the number of times a lower bound was tightened due the the time-table algorithm */ 4151 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->nlbtimetable++ ); 4152 4153 /* adjust the earliest start time 4154 * 4155 * @note We are taking the lower of the start time variable on purpose instead of newlb. This is due the fact that 4156 * the proposed lower bound might be even strength by be the core which can be the case if aggregations are 4157 * involved. 4158 */ 4159 est = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(var)); 4160 assert(est >= newlb); 4161 4162 /* adjust the search position for the resource profile for the next step */ 4163 if( est == SCIPprofileGetTime(profile, peak+1) ) 4164 pos = peak + 1; 4165 else 4166 pos = peak; 4167 } 4168 while( est < lst ); 4169 4170 return SCIP_OKAY; 4171 } 4172 4173 /** We are using the core resource profile which contains all core except the one of the start time variable which we 4174 * want to propagate, to decrease the latest start time. This we are doing in steps of length at most the duration of 4175 * the job. The reason for that is, that this makes it later easier to resolve this propagation during the conflict 4176 * analysis 4177 */ 4178 static 4179 SCIP_RETCODE coretimesUpdateUb( 4180 SCIP* scip, /**< SCIP data structure */ 4181 SCIP_VAR* var, /**< start time variable to propagate */ 4182 int duration, /**< duration of the job */ 4183 int demand, /**< demand of the job */ 4184 int capacity, /**< cumulative capacity */ 4185 SCIP_CONS* cons, /**< constraint which is propagated */ 4186 SCIP_PROFILE* profile, /**< resource profile */ 4187 int idx, /**< position of the variable to propagate */ 4188 int* nchgbds /**< pointer to store the number of bound changes */ 4189 ) 4190 { 4191 int ntimepoints; 4192 int newub; 4193 int peak; 4194 int pos; 4195 int est; 4196 int lst; 4197 int lct; 4198 4199 assert(var != NULL); 4200 assert(duration > 0); 4201 assert(demand > 0); 4202 4203 est = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(var)); 4204 lst = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(var)); 4205 4206 /* in case the start time variable is fixed do nothing */ 4207 if( est == lst ) 4208 return SCIP_OKAY; 4209 4210 ntimepoints = SCIPprofileGetNTimepoints(profile); 4211 4212 lct = lst + duration; 4213 4214 /* first we find left position of latest completion time minus 1 (upper bound + duration) in resource profile; That 4215 * is the last time point where the job would run if schedule it at its latest start time (upper bound). This 4216 * position gives us the load which we have at the latest completion time minus one 4217 */ 4218 (void) SCIPprofileFindLeft(profile, lct - 1, &pos); 4219 4220 SCIPdebugMsg(scip, "propagate upper bound (pos %d)\n", pos); 4221 SCIPdebug( SCIPprofilePrint(profile, SCIPgetMessagehdlr(scip), NULL) ); 4222 4223 if( pos == ntimepoints-1 && SCIPprofileGetTime(profile, pos) == lst ) 4224 return SCIP_OKAY; 4225 4226 /* we now trying to move the latest start time in steps of at most "duration" length */ 4227 do 4228 { 4229 INFERINFO inferinfo; 4230 SCIP_Bool tightened; 4231 SCIP_Bool infeasible; 4232 4233 peak = -1; 4234 4235 #ifndef NDEBUG 4236 { 4237 /* in debug mode we check that we adjust the search position correctly */ 4238 int tmppos; 4239 4240 (void)SCIPprofileFindLeft(profile, lct - 1, &tmppos); 4241 assert(pos == tmppos); 4242 } 4243 #endif 4244 4245 /* we search for a peak within the core profile which conflicts with the demand of the start time variable; we 4246 * want a peak which is closest to the latest start time 4247 */ 4248 do 4249 { 4250 if( SCIPprofileGetLoad(profile, pos) + demand > capacity ) 4251 peak = pos; 4252 4253 pos--; 4254 } 4255 while( pos >= 0 && SCIPprofileGetTime(profile, pos+1) > lst); 4256 4257 /* if we found no peak that means the current job could be scheduled at its latest start time without conflicting 4258 * to the core resource profile 4259 */ 4260 /* coverity[check_after_sink] */ 4261 if( peak == -1 ) 4262 break; 4263 4264 /* the peak position gives us a time point where the start time variable is in conflict with the resource 4265 * profile. That means the job has be done until that point. Hence that gives us the latest completion 4266 * time. Note that that we want to move the bound by at most the duration length (the remaining move we are 4267 * doing in the next loop) 4268 */ 4269 newub = SCIPprofileGetTime(profile, peak); 4270 newub = MAX(newub, lst) - duration; 4271 assert(newub >= est); 4272 4273 /* construct the inference information which we are using with the conflict analysis to resolve that particular 4274 * bound change 4275 */ 4276 inferinfo = getInferInfo(PROPRULE_1_CORETIMES, idx, newub+duration); 4277 4278 /* perform the bound upper bound change */ 4279 if( inferInfoIsValid(inferinfo) ) 4280 { 4281 SCIP_CALL( SCIPinferVarUbCons(scip, var, (SCIP_Real)newub, cons, inferInfoToInt(inferinfo), TRUE, &infeasible, &tightened) ); 4282 } 4283 else 4284 { 4285 SCIP_CALL( SCIPtightenVarUb(scip, var, (SCIP_Real)newub, TRUE, &infeasible, &tightened) ); 4286 } 4287 assert(tightened); 4288 assert(!infeasible); 4289 4290 SCIPdebugMsg(scip, "variable <%s>: new upper bound <%d> -> <%d>\n", SCIPvarGetName(var), lst, newub); 4291 (*nchgbds)++; 4292 4293 /* for the statistic we count the number of times a upper bound was tightened due the the time-table algorithm */ 4294 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->nubtimetable++ ); 4295 4296 /* adjust the latest start and completion time 4297 * 4298 * @note We are taking the upper of the start time variable on purpose instead of newub. This is due the fact that 4299 * the proposed upper bound might be even strength by be the core which can be the case if aggregations are 4300 * involved. 4301 */ 4302 lst = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(var)); 4303 assert(lst <= newub); 4304 lct = lst + duration; 4305 4306 /* adjust the search position for the resource profile for the next step */ 4307 if( SCIPprofileGetTime(profile, peak) == lct ) 4308 pos = peak - 1; 4309 else 4310 pos = peak; 4311 } 4312 while( est < lst ); 4313 4314 return SCIP_OKAY; 4315 } 4316 4317 /** compute for the different earliest start and latest completion time the core energy of the corresponding time 4318 * points 4319 */ 4320 static 4321 void computeCoreEnergyAfter( 4322 SCIP_PROFILE* profile, /**< core profile */ 4323 int nvars, /**< number of start time variables (activities) */ 4324 int* ests, /**< array of sorted earliest start times */ 4325 int* lcts, /**< array of sorted latest completion times */ 4326 int* coreEnergyAfterEst, /**< array to store the core energy after the earliest start time of each job */ 4327 int* coreEnergyAfterLct /**< array to store the core energy after the latest completion time of each job */ 4328 ) 4329 { 4330 int ntimepoints; 4331 int energy; 4332 int t; 4333 int v; 4334 4335 ntimepoints = SCIPprofileGetNTimepoints(profile); 4336 t = ntimepoints - 1; 4337 energy = 0; 4338 4339 /* compute core energy after the earliest start time of each job */ 4340 for( v = nvars-1; v >= 0; --v ) 4341 { 4342 while( t > 0 && SCIPprofileGetTime(profile, t-1) >= ests[v] ) 4343 { 4344 assert(SCIPprofileGetLoad(profile, t-1) >= 0); 4345 assert(SCIPprofileGetTime(profile, t) - SCIPprofileGetTime(profile, t-1)>= 0); 4346 energy += SCIPprofileGetLoad(profile, t-1) * (SCIPprofileGetTime(profile, t) - SCIPprofileGetTime(profile, t-1)); 4347 t--; 4348 } 4349 assert(SCIPprofileGetTime(profile, t) >= ests[v] || t == ntimepoints-1); 4350 4351 /* maybe ests[j] is in-between two timepoints */ 4352 if( SCIPprofileGetTime(profile, t) - ests[v] > 0 ) 4353 { 4354 assert(t > 0); 4355 coreEnergyAfterEst[v] = energy + SCIPprofileGetLoad(profile, t-1) * (SCIPprofileGetTime(profile, t) - ests[v]); 4356 } 4357 else 4358 coreEnergyAfterEst[v] = energy; 4359 } 4360 4361 t = ntimepoints - 1; 4362 energy = 0; 4363 4364 /* compute core energy after the latest completion time of each job */ 4365 for( v = nvars-1; v >= 0; --v ) 4366 { 4367 while( t > 0 && SCIPprofileGetTime(profile, t-1) >= lcts[v] ) 4368 { 4369 assert(SCIPprofileGetLoad(profile, t-1) >= 0); 4370 assert(SCIPprofileGetTime(profile, t) - SCIPprofileGetTime(profile, t-1)>= 0); 4371 energy += SCIPprofileGetLoad(profile, t-1) * (SCIPprofileGetTime(profile, t) - SCIPprofileGetTime(profile, t-1)); 4372 t--; 4373 } 4374 assert(SCIPprofileGetTime(profile, t) >= lcts[v] || t == ntimepoints-1); 4375 4376 /* maybe lcts[j] is in-between two timepoints */ 4377 if( SCIPprofileGetTime(profile, t) - lcts[v] > 0 ) 4378 { 4379 assert(t > 0); 4380 coreEnergyAfterLct[v] = energy + SCIPprofileGetLoad(profile, t-1) * (SCIPprofileGetTime(profile, t) - lcts[v]); 4381 } 4382 else 4383 coreEnergyAfterLct[v] = energy; 4384 } 4385 } 4386 4387 /** collect earliest start times, latest completion time, and free energy contributions */ 4388 static 4389 void collectDataTTEF( 4390 SCIP* scip, /**< SCIP data structure */ 4391 int nvars, /**< number of start time variables (activities) */ 4392 SCIP_VAR** vars, /**< array of start time variables */ 4393 int* durations, /**< array of durations */ 4394 int* demands, /**< array of demands */ 4395 int hmin, /**< left bound of time axis to be considered (including hmin) */ 4396 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 4397 int* permests, /**< array to store the variable positions */ 4398 int* ests, /**< array to store earliest start times */ 4399 int* permlcts, /**< array to store the variable positions */ 4400 int* lcts, /**< array to store latest completion times */ 4401 int* ects, /**< array to store earliest completion times of the flexible part of the job */ 4402 int* lsts, /**< array to store latest start times of the flexible part of the job */ 4403 int* flexenergies /**< array to store the flexible energies of each job */ 4404 ) 4405 { 4406 int v; 4407 4408 for( v = 0; v < nvars; ++ v) 4409 { 4410 int duration; 4411 int leftadjust; 4412 int rightadjust; 4413 int core; 4414 int est; 4415 int lct; 4416 int ect; 4417 int lst; 4418 4419 duration = durations[v]; 4420 assert(duration > 0); 4421 4422 est = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(vars[v])); 4423 lst = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(vars[v])); 4424 ect = est + duration; 4425 lct = lst + duration; 4426 4427 ests[v] = est; 4428 lcts[v] = lct; 4429 permests[v] = v; 4430 permlcts[v] = v; 4431 4432 /* compute core time window which lies within the effective horizon */ 4433 core = (int) computeCoreWithInterval(hmin, hmax, ect, lst); 4434 4435 /* compute the number of time steps the job could run before the effective horizon */ 4436 leftadjust = MAX(0, hmin - est); 4437 4438 /* compute the number of time steps the job could run after the effective horizon */ 4439 rightadjust = MAX(0, lct - hmax); 4440 4441 /* compute for each job the energy which is flexible; meaning not part of the core */ 4442 flexenergies[v] = duration - leftadjust - rightadjust - core; 4443 flexenergies[v] = MAX(0, flexenergies[v]); 4444 flexenergies[v] *= demands[v]; 4445 assert(flexenergies[v] >= 0); 4446 4447 /* the earliest completion time of the flexible energy */ 4448 ects[v] = MIN(ect, lst); 4449 4450 /* the latest start time of the flexible energy */ 4451 lsts[v] = MAX(ect, lst); 4452 } 4453 } 4454 4455 /** try to tighten the lower bound of the given variable */ 4456 static 4457 SCIP_RETCODE tightenLbTTEF( 4458 SCIP* scip, /**< SCIP data structure */ 4459 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */ 4460 int nvars, /**< number of start time variables (activities) */ 4461 SCIP_VAR** vars, /**< array of start time variables */ 4462 int* durations, /**< array of durations */ 4463 int* demands, /**< array of demands */ 4464 int capacity, /**< cumulative capacity */ 4465 int hmin, /**< left bound of time axis to be considered (including hmin) */ 4466 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 4467 SCIP_VAR* var, /**< variable to be considered for upper bound tightening */ 4468 int duration, /**< duration of the job */ 4469 int demand, /**< demand of the job */ 4470 int est, /**< earliest start time of the job */ 4471 int ect, /**< earliest completion time of the flexible part of the job */ 4472 int lct, /**< latest completion time of the job */ 4473 int begin, /**< begin of the time window under investigation */ 4474 int end, /**< end of the time window under investigation */ 4475 SCIP_Longint energy, /**< available energy for the flexible part of the hob within the time window */ 4476 int* bestlb, /**< pointer to strope the best lower bound change */ 4477 int* inferinfos, /**< pointer to store the inference information which is need for the (best) lower bound change */ 4478 SCIP_Bool* initialized, /**< was conflict analysis initialized */ 4479 SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */ 4480 SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */ 4481 ) 4482 { 4483 int newlb; 4484 4485 assert(begin >= hmin); 4486 assert(end <= hmax); 4487 4488 /* check if the time-table edge-finding should infer bounds */ 4489 if( !conshdlrdata->ttefinfer ) 4490 return SCIP_OKAY; 4491 4492 /* if the job can be processed completely before or after the time window, nothing can be tightened */ 4493 if( est >= end || ect <= begin ) 4494 return SCIP_OKAY; 4495 4496 /* if flexible part runs completely within the time window (assuming it is scheduled on its earliest start time), we 4497 * skip since the overload check will do the job 4498 */ 4499 if( est >= begin && ect <= end ) 4500 return SCIP_OKAY; 4501 4502 /* check if the available energy in the time window is to small to handle the flexible part if it is schedule on its 4503 * earliest start time 4504 */ 4505 if( energy >= demand * ((SCIP_Longint) MAX(begin, est) - MIN(end, ect)) ) 4506 return SCIP_OKAY; 4507 4508 /* adjust the available energy for the job; the given available energy assumes that the core of the considered job is 4509 * present; therefore, we need to add the core; 4510 * 4511 * @note the variable ect define the earliest completion time of the flexible part of the job; hence we need to 4512 * compute the earliest completion time of the (whole) job 4513 */ 4514 energy += computeCoreWithInterval(begin, end, est + duration, lct - duration) * demand; 4515 4516 /* compute a latest start time (upper bound) such that the job consums at most the available energy 4517 * 4518 * @note we can round down the compute duration w.r.t. the available energy 4519 */ 4520 newlb = end - (int) (energy / demand); 4521 4522 /* check if we detected an infeasibility which is the case if the new lower bound is larger than the current upper 4523 * bound (latest start time); meaning it is not possible to schedule the job 4524 */ 4525 if( newlb > lct - duration ) 4526 { 4527 /* initialize conflict analysis if conflict analysis is applicable */ 4528 if( SCIPisConflictAnalysisApplicable(scip) ) 4529 { 4530 SCIP_Real relaxedbd; 4531 4532 assert(SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(var)) < newlb); 4533 4534 /* it is enough to overshoot the upper bound of the variable by one */ 4535 relaxedbd = SCIPvarGetUbLocal(var) + 1.0; 4536 4537 /* initialize conflict analysis */ 4538 SCIP_CALL( SCIPinitConflictAnalysis(scip, SCIP_CONFTYPE_PROPAGATION, FALSE) ); 4539 4540 /* added to upper bound (which was overcut be new lower bound) of the variable */ 4541 SCIP_CALL( SCIPaddConflictUb(scip, var, NULL) ); 4542 4543 /* analyze the infeasible */ 4544 SCIP_CALL( analyzeEnergyRequirement(scip, nvars, vars, durations, demands, capacity, 4545 begin, end, var, SCIP_BOUNDTYPE_LOWER, NULL, relaxedbd, conshdlrdata->usebdwidening, explanation) ); 4546 4547 (*initialized) = TRUE; 4548 } 4549 4550 (*cutoff) = TRUE; 4551 } 4552 else if( newlb > (*bestlb) ) 4553 { 4554 INFERINFO inferinfo; 4555 4556 assert(newlb > begin); 4557 4558 inferinfo = getInferInfo(PROPRULE_3_TTEF, begin, end); 4559 4560 /* construct inference information */ 4561 (*inferinfos) = inferInfoToInt(inferinfo); 4562 (*bestlb) = newlb; 4563 } 4564 4565 return SCIP_OKAY; 4566 } 4567 4568 /** try to tighten the upper bound of the given variable */ 4569 static 4570 SCIP_RETCODE tightenUbTTEF( 4571 SCIP* scip, /**< SCIP data structure */ 4572 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */ 4573 int nvars, /**< number of start time variables (activities) */ 4574 SCIP_VAR** vars, /**< array of start time variables */ 4575 int* durations, /**< array of durations */ 4576 int* demands, /**< array of demands */ 4577 int capacity, /**< cumulative capacity */ 4578 int hmin, /**< left bound of time axis to be considered (including hmin) */ 4579 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 4580 SCIP_VAR* var, /**< variable to be considered for upper bound tightening */ 4581 int duration, /**< duration of the job */ 4582 int demand, /**< demand of the job */ 4583 int est, /**< earliest start time of the job */ 4584 int lst, /**< latest start time of the flexible part of the job */ 4585 int lct, /**< latest completion time of the job */ 4586 int begin, /**< begin of the time window under investigation */ 4587 int end, /**< end of the time window under investigation */ 4588 SCIP_Longint energy, /**< available energy for the flexible part of the hob within the time window */ 4589 int* bestub, /**< pointer to strope the best upper bound change */ 4590 int* inferinfos, /**< pointer to store the inference information which is need for the (best) upper bound change */ 4591 SCIP_Bool* initialized, /**< was conflict analysis initialized */ 4592 SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */ 4593 SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */ 4594 ) 4595 { 4596 int newub; 4597 4598 assert(begin >= hmin); 4599 assert(end <= hmax); 4600 assert(est < begin); 4601 4602 /* check if the time-table edge-finding should infer bounds */ 4603 if( !conshdlrdata->ttefinfer ) 4604 return SCIP_OKAY; 4605 4606 /* if flexible part of the job can be processed completely before or after the time window, nothing can be tightened */ 4607 if( lst >= end || lct <= begin ) 4608 return SCIP_OKAY; 4609 4610 /* if flexible part runs completely within the time window (assuming it is scheduled on its latest start time), we 4611 * skip since the overload check will do the job 4612 */ 4613 if( lst >= begin && lct <= end ) 4614 return SCIP_OKAY; 4615 4616 /* check if the available energy in the time window is to small to handle the flexible part of the job */ 4617 if( energy >= demand * ((SCIP_Longint) MIN(end, lct) - MAX(begin, lst)) ) 4618 return SCIP_OKAY; 4619 4620 /* adjust the available energy for the job; the given available energy assumes that the core of the considered job is 4621 * present; therefore, we need to add the core; 4622 * 4623 * @note the variable lst define the latest start time of the flexible part of the job; hence we need to compute the 4624 * latest start of the (whole) job 4625 */ 4626 energy += computeCoreWithInterval(begin, end, est + duration, lct - duration) * demand; 4627 assert(energy >= 0); 4628 4629 /* compute a latest start time (upper bound) such that the job consums at most the available energy 4630 * 4631 * @note we can round down the compute duration w.r.t. the available energy 4632 */ 4633 assert(demand > 0); 4634 newub = begin - duration + (int) (energy / demand); 4635 4636 /* check if we detected an infeasibility which is the case if the new upper bound is smaller than the current lower 4637 * bound (earliest start time); meaning it is not possible to schedule the job 4638 */ 4639 if( newub < est ) 4640 { 4641 /* initialize conflict analysis if conflict analysis is applicable */ 4642 if( SCIPisConflictAnalysisApplicable(scip) ) 4643 { 4644 SCIP_Real relaxedbd; 4645 4646 assert(SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(var)) > newub); 4647 4648 /* it is enough to undershoot the lower bound of the variable by one */ 4649 relaxedbd = SCIPvarGetLbLocal(var) - 1.0; 4650 4651 /* initialize conflict analysis */ 4652 SCIP_CALL( SCIPinitConflictAnalysis(scip, SCIP_CONFTYPE_PROPAGATION, FALSE) ); 4653 4654 /* added to lower bound (which was undercut be new upper bound) of the variable */ 4655 SCIP_CALL( SCIPaddConflictLb(scip, var, NULL) ); 4656 4657 /* analyze the infeasible */ 4658 SCIP_CALL( analyzeEnergyRequirement(scip, nvars, vars, durations, demands, capacity, 4659 begin, end, var, SCIP_BOUNDTYPE_UPPER, NULL, relaxedbd, conshdlrdata->usebdwidening, explanation) ); 4660 4661 (*initialized) = TRUE; 4662 } 4663 4664 (*cutoff) = TRUE; 4665 } 4666 else if( newub < (*bestub) ) 4667 { 4668 INFERINFO inferinfo; 4669 4670 assert(newub < begin); 4671 4672 inferinfo = getInferInfo(PROPRULE_3_TTEF, begin, end); 4673 4674 /* construct inference information */ 4675 (*inferinfos) = inferInfoToInt(inferinfo); 4676 (*bestub) = newub; 4677 } 4678 4679 return SCIP_OKAY; 4680 } 4681 4682 /** propagate the upper bounds and "opportunistically" the lower bounds using the time-table edge-finding algorithm */ 4683 static 4684 SCIP_RETCODE propagateUbTTEF( 4685 SCIP* scip, /**< SCIP data structure */ 4686 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */ 4687 int nvars, /**< number of start time variables (activities) */ 4688 SCIP_VAR** vars, /**< array of start time variables */ 4689 int* durations, /**< array of durations */ 4690 int* demands, /**< array of demands */ 4691 int capacity, /**< cumulative capacity */ 4692 int hmin, /**< left bound of time axis to be considered (including hmin) */ 4693 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 4694 int* newlbs, /**< array to buffer new lower bounds */ 4695 int* newubs, /**< array to buffer new upper bounds */ 4696 int* lbinferinfos, /**< array to store the inference information for the lower bound changes */ 4697 int* ubinferinfos, /**< array to store the inference information for the upper bound changes */ 4698 int* lsts, /**< array of latest start time of the flexible part in the same order as the variables */ 4699 int* flexenergies, /**< array of flexible energies in the same order as the variables */ 4700 int* perm, /**< permutation of the variables w.r.t. the non-decreasing order of the earliest start times */ 4701 int* ests, /**< array with earliest strart times sorted in non-decreasing order */ 4702 int* lcts, /**< array with latest completion times sorted in non-decreasing order */ 4703 int* coreEnergyAfterEst, /**< core energy after the earliest start times */ 4704 int* coreEnergyAfterLct, /**< core energy after the latest completion times */ 4705 SCIP_Bool* initialized, /**< was conflict analysis initialized */ 4706 SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */ 4707 SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */ 4708 ) 4709 { 4710 int coreEnergyAfterEnd; 4711 SCIP_Longint maxavailable; 4712 SCIP_Longint minavailable; 4713 SCIP_Longint totalenergy; 4714 int nests; 4715 int est; 4716 int lct; 4717 int start; 4718 int end; 4719 int v; 4720 4721 est = INT_MAX; 4722 lct = INT_MIN; 4723 4724 /* compute earliest start and latest completion time of all jobs */ 4725 for( v = 0; v < nvars; ++v ) 4726 { 4727 start = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(vars[v])); 4728 end = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(vars[v])) + durations[v]; 4729 4730 est = MIN(est, start); 4731 lct = MAX(lct, end); 4732 } 4733 4734 /* adjust the effective time horizon */ 4735 hmin = MAX(hmin, est); 4736 hmax = MIN(hmax, lct); 4737 4738 end = hmax + 1; 4739 coreEnergyAfterEnd = -1; 4740 4741 maxavailable = ((SCIP_Longint) hmax - hmin) * capacity; 4742 minavailable = maxavailable; 4743 totalenergy = computeTotalEnergy(durations, demands, nvars); 4744 4745 /* check if the smallest interval has a size such that the total energy fits, if so we can skip the propagator */ 4746 if( ((SCIP_Longint) lcts[0] - ests[nvars-1]) * capacity >= totalenergy ) 4747 return SCIP_OKAY; 4748 4749 nests = nvars; 4750 4751 /* loop over all variable in non-increasing order w.r.t. the latest completion time; thereby, the latest completion 4752 * times define the end of the time interval under investigation 4753 */ 4754 for( v = nvars-1; v >= 0 && !(*cutoff); --v ) 4755 { 4756 int flexenergy; 4757 int minbegin; 4758 int lbenergy; 4759 int lbcand; 4760 int i; 4761 4762 lct = lcts[v]; 4763 4764 /* if the latest completion time is larger then hmax an infeasibility cannot be detected, since after hmax an 4765 * infinity capacity is available; hence we skip that 4766 */ 4767 if( lct > hmax ) 4768 continue; 4769 4770 /* if the latest completion time is smaller then hmin we have to stop */ 4771 if( lct <= hmin ) 4772 { 4773 assert(v == 0 || lcts[v-1] <= lcts[v]); 4774 break; 4775 } 4776 4777 /* if the latest completion time equals to previous end time, we can continue since this particular interval 4778 * induced by end was just analyzed 4779 */ 4780 if( lct == end ) 4781 continue; 4782 4783 assert(lct < end); 4784 4785 /* In case we only want to detect an overload (meaning no bound propagation) we can skip the interval; this is 4786 * the case if the free energy (the energy which is not occupied by any core) is smaller than the previous minimum 4787 * free energy; if so it means that in the next iterate the free-energy cannot be negative 4788 */ 4789 if( !conshdlrdata->ttefinfer && end <= hmax && minavailable < maxavailable ) 4790 { 4791 SCIP_Longint freeenergy; 4792 4793 assert(coreEnergyAfterLct[v] >= coreEnergyAfterEnd); 4794 assert(coreEnergyAfterEnd >= 0); 4795 4796 /* compute the energy which is not consumed by the cores with in the interval [lct, end) */ 4797 freeenergy = capacity * ((SCIP_Longint) end - lct) - coreEnergyAfterLct[v] + coreEnergyAfterEnd; 4798 4799 if( freeenergy <= minavailable ) 4800 { 4801 SCIPdebugMsg(scip, "skip latest completion time <%d> (minimum available energy <%" SCIP_LONGINT_FORMAT ">, free energy <%" SCIP_LONGINT_FORMAT ">)\n", lct, minavailable, freeenergy); 4802 continue; 4803 } 4804 } 4805 4806 SCIPdebugMsg(scip, "check intervals ending with <%d>\n", lct); 4807 4808 end = lct; 4809 coreEnergyAfterEnd = coreEnergyAfterLct[v]; 4810 4811 flexenergy = 0; 4812 minavailable = maxavailable; 4813 minbegin = hmax; 4814 lbcand = -1; 4815 lbenergy = 0; 4816 4817 /* loop over the job in non-increasing order w.r.t. the earliest start time; these earliest start time are 4818 * defining the beginning of the time interval under investigation; Thereby, the time interval gets wider and 4819 * wider 4820 */ 4821 for( i = nests-1; i >= 0; --i ) 4822 { 4823 SCIP_VAR* var; 4824 SCIP_Longint freeenergy; 4825 int duration; 4826 int demand; 4827 int begin; 4828 int idx; 4829 int lst; 4830 4831 idx = perm[i]; 4832 assert(idx >= 0); 4833 assert(idx < nvars); 4834 assert(!(*cutoff)); 4835 4836 /* the earliest start time of the job */ 4837 est = ests[i]; 4838 4839 /* if the job starts after the current end, we can skip it and do not need to consider it again since the 4840 * latest completion times (which define end) are scant in non-increasing order 4841 */ 4842 if( end <= est ) 4843 { 4844 nests--; 4845 continue; 4846 } 4847 4848 /* check if the interval has a size such that the total energy fits, if so we can skip all intervals with the 4849 * current ending time 4850 */ 4851 if( ((SCIP_Longint) end - est) * capacity >= totalenergy ) 4852 break; 4853 4854 var = vars[idx]; 4855 assert(var != NULL); 4856 4857 duration = durations[idx]; 4858 assert(duration > 0); 4859 4860 demand = demands[idx]; 4861 assert(demand > 0); 4862 4863 lct = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(var)) + duration; 4864 4865 /* the latest start time of the free part of the job */ 4866 lst = lsts[idx]; 4867 4868 /* in case the earliest start time is equal to minbegin, the job lies completely within the time window under 4869 * investigation; hence the overload check will do the the job 4870 */ 4871 assert(est <= minbegin); 4872 if( minavailable < maxavailable && est < minbegin ) 4873 { 4874 assert(!(*cutoff)); 4875 4876 /* try to tighten the upper bound */ 4877 SCIP_CALL( tightenUbTTEF(scip, conshdlrdata, nvars, vars, durations, demands, capacity, hmin, hmax, 4878 var, duration, demand, est, lst, lct, minbegin, end, minavailable, &(newubs[idx]), &(ubinferinfos[idx]), 4879 initialized, explanation, cutoff) ); 4880 4881 if( *cutoff ) 4882 break; 4883 } 4884 4885 SCIPdebugMsg(scip, "check variable <%s>[%g,%g] (duration %d, demands %d, est <%d>, lst of free part <%d>\n", 4886 SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var), duration, demand, est, lst); 4887 4888 begin = est; 4889 assert(SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(var)) == est); 4890 4891 /* if the earliest start time is smaller than hmin we can stop here since the next job will not decrease the 4892 * free energy 4893 */ 4894 if( begin < hmin ) 4895 break; 4896 4897 /* compute the contribution to the flexible energy */ 4898 if( lct <= end ) 4899 { 4900 /* if the jobs has to finish before the end, all the energy has to be scheduled */ 4901 assert(lst >= begin); 4902 assert(flexenergies[idx] >= 0); 4903 flexenergy += flexenergies[idx]; 4904 } 4905 else 4906 { 4907 /* the job partly overlaps with the end */ 4908 int candenergy; 4909 int energy; 4910 4911 /* compute the flexible energy which is part of the time interval for sure if the job is scheduled 4912 * w.r.t. latest start time 4913 * 4914 * @note we need to be aware of the effective horizon 4915 */ 4916 energy = MIN(flexenergies[idx], demands[idx] * MAX(0, (end - lst))); 4917 assert(end - lst < duration); 4918 assert(energy >= 0); 4919 4920 /* adjust the flexible energy of the time interval */ 4921 flexenergy += energy; 4922 4923 /* compute the flexible energy of the job which is not part of flexible energy of the time interval */ 4924 candenergy = MIN(flexenergies[idx], demands[idx] * (end - begin)) - energy; 4925 assert(candenergy >= 0); 4926 4927 /* check if we found a better candidate */ 4928 if( candenergy > lbenergy ) 4929 { 4930 lbenergy = candenergy; 4931 lbcand = idx; 4932 } 4933 } 4934 4935 SCIPdebugMsg(scip, "time window [%d,%d) flexible energy <%d>\n", begin, end, flexenergy); 4936 assert(coreEnergyAfterEst[i] >= coreEnergyAfterEnd); 4937 4938 /* compute the energy which is not used yet */ 4939 freeenergy = capacity * ((SCIP_Longint) end - begin) - flexenergy - coreEnergyAfterEst[i] + coreEnergyAfterEnd; 4940 4941 /* check overload */ 4942 if( freeenergy < 0 ) 4943 { 4944 SCIPdebugMsg(scip, "analyze overload within time window [%d,%d) capacity %d\n", begin, end, capacity); 4945 4946 /* initialize conflict analysis if conflict analysis is applicable */ 4947 if( SCIPisConflictAnalysisApplicable(scip) ) 4948 { 4949 /* analyze infeasibilty */ 4950 SCIP_CALL( SCIPinitConflictAnalysis(scip, SCIP_CONFTYPE_PROPAGATION, FALSE) ); 4951 4952 SCIP_CALL( analyzeEnergyRequirement(scip, nvars, vars, durations, demands, capacity, 4953 begin, end, NULL, SCIP_BOUNDTYPE_UPPER, NULL, SCIP_UNKNOWN, 4954 conshdlrdata->usebdwidening, explanation) ); 4955 4956 (*initialized) = TRUE; 4957 } 4958 4959 (*cutoff) = TRUE; 4960 4961 /* for the statistic we count the number of times a cutoff was detected due the time-time-edge-finding */ 4962 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->ncutoffoverloadTTEF++ ); 4963 4964 break; 4965 } 4966 4967 /* check if the available energy is not sufficent to schedule the flexible energy of the best candidate job */ 4968 if( lbenergy > 0 && freeenergy < lbenergy ) 4969 { 4970 SCIP_Longint energy; 4971 int newlb; 4972 int ect; 4973 4974 ect = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(vars[lbcand])) + durations[lbcand]; 4975 lst = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(vars[lbcand])); 4976 4977 /* remove the energy of our job from the ... */ 4978 energy = freeenergy + (computeCoreWithInterval(begin, end, ect, lst) + MAX(0, (SCIP_Longint) end - lsts[lbcand])) * demands[lbcand]; 4979 4980 newlb = end - (int)(energy / demands[lbcand]); 4981 4982 if( newlb > lst ) 4983 { 4984 /* initialize conflict analysis if conflict analysis is applicable */ 4985 if( SCIPisConflictAnalysisApplicable(scip) ) 4986 { 4987 SCIP_Real relaxedbd; 4988 4989 /* analyze infeasibilty */ 4990 SCIP_CALL( SCIPinitConflictAnalysis(scip, SCIP_CONFTYPE_PROPAGATION, FALSE) ); 4991 4992 relaxedbd = lst + 1.0; 4993 4994 /* added to upper bound (which was overcut be new lower bound) of the variable */ 4995 SCIP_CALL( SCIPaddConflictUb(scip, vars[lbcand], NULL) ); 4996 4997 SCIP_CALL( analyzeEnergyRequirement(scip, nvars, vars, durations, demands, capacity, 4998 begin, end, vars[lbcand], SCIP_BOUNDTYPE_LOWER, NULL, relaxedbd, 4999 conshdlrdata->usebdwidening, explanation) ); 5000 5001 (*initialized) = TRUE; 5002 } 5003 5004 (*cutoff) = TRUE; 5005 break; 5006 } 5007 else if( newlb > newlbs[lbcand] ) 5008 { 5009 INFERINFO inferinfo; 5010 5011 /* construct inference information */ 5012 inferinfo = getInferInfo(PROPRULE_3_TTEF, begin, end); 5013 5014 /* buffer upper bound change */ 5015 lbinferinfos[lbcand] = inferInfoToInt(inferinfo); 5016 newlbs[lbcand] = newlb; 5017 } 5018 } 5019 5020 /* check if the current interval has a smaller free energy */ 5021 if( minavailable > freeenergy ) 5022 { 5023 minavailable = freeenergy; 5024 minbegin = begin; 5025 } 5026 assert(minavailable >= 0); 5027 } 5028 } 5029 5030 return SCIP_OKAY; 5031 } 5032 5033 /** propagate the lower bounds and "opportunistically" the upper bounds using the time-table edge-finding algorithm */ 5034 static 5035 SCIP_RETCODE propagateLbTTEF( 5036 SCIP* scip, /**< SCIP data structure */ 5037 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */ 5038 int nvars, /**< number of start time variables (activities) */ 5039 SCIP_VAR** vars, /**< array of start time variables */ 5040 int* durations, /**< array of durations */ 5041 int* demands, /**< array of demands */ 5042 int capacity, /**< cumulative capacity */ 5043 int hmin, /**< left bound of time axis to be considered (including hmin) */ 5044 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 5045 int* newlbs, /**< array to buffer new lower bounds */ 5046 int* newubs, /**< array to buffer new upper bounds */ 5047 int* lbinferinfos, /**< array to store the inference information for the lower bound changes */ 5048 int* ubinferinfos, /**< array to store the inference information for the upper bound changes */ 5049 int* ects, /**< array of earliest completion time of the flexible part in the same order as the variables */ 5050 int* flexenergies, /**< array of flexible energies in the same order as the variables */ 5051 int* perm, /**< permutation of the variables w.r.t. the non-decreasing order of the latest completion times */ 5052 int* ests, /**< array with earliest strart times sorted in non-decreasing order */ 5053 int* lcts, /**< array with latest completion times sorted in non-decreasing order */ 5054 int* coreEnergyAfterEst, /**< core energy after the earliest start times */ 5055 int* coreEnergyAfterLct, /**< core energy after the latest completion times */ 5056 SCIP_Bool* initialized, /**< was conflict analysis initialized */ 5057 SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */ 5058 SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */ 5059 ) 5060 { 5061 int coreEnergyAfterStart; 5062 SCIP_Longint maxavailable; 5063 SCIP_Longint minavailable; 5064 SCIP_Longint totalenergy; 5065 int nlcts; 5066 int begin; 5067 int minest; 5068 int maxlct; 5069 int start; 5070 int end; 5071 int v; 5072 5073 if( *cutoff ) 5074 return SCIP_OKAY; 5075 5076 begin = hmin - 1; 5077 5078 minest = INT_MAX; 5079 maxlct = INT_MIN; 5080 5081 /* compute earliest start and latest completion time of all jobs */ 5082 for( v = 0; v < nvars; ++v ) 5083 { 5084 start = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(vars[v])); 5085 end = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(vars[v])) + durations[v]; 5086 5087 minest = MIN(minest, start); 5088 maxlct = MAX(maxlct, end); 5089 } 5090 5091 /* adjust the effective time horizon */ 5092 hmin = MAX(hmin, minest); 5093 hmax = MIN(hmax, maxlct); 5094 5095 maxavailable = ((SCIP_Longint) hmax - hmin) * capacity; 5096 totalenergy = computeTotalEnergy(durations, demands, nvars); 5097 5098 /* check if the smallest interval has a size such that the total energy fits, if so we can skip the propagator */ 5099 if( ((SCIP_Longint) lcts[0] - ests[nvars-1]) * capacity >= totalenergy ) 5100 return SCIP_OKAY; 5101 5102 nlcts = 0; 5103 5104 /* loop over all variable in non-decreasing order w.r.t. the earliest start times; thereby, the earliest start times 5105 * define the start of the time interval under investigation 5106 */ 5107 for( v = 0; v < nvars; ++v ) 5108 { 5109 int flexenergy; 5110 int minend; 5111 int ubenergy; 5112 int ubcand; 5113 int est; 5114 int i; 5115 5116 est = ests[v]; 5117 5118 /* if the earliest start time is smaller then hmin an infeasibility cannot be detected, since before hmin an 5119 * infinity capacity is available; hence we skip that 5120 */ 5121 if( est < hmin ) 5122 continue; 5123 5124 /* if the earliest start time is larger or equal then hmax we have to stop */ 5125 if( est >= hmax ) 5126 break; 5127 5128 /* if the latest earliest start time equals to previous start time, we can continue since this particular interval 5129 * induced by start was just analyzed 5130 */ 5131 if( est == begin ) 5132 continue; 5133 5134 assert(est > begin); 5135 5136 SCIPdebugMsg(scip, "check intervals starting with <%d>\n", est); 5137 5138 begin = est; 5139 coreEnergyAfterStart = coreEnergyAfterEst[v]; 5140 5141 flexenergy = 0; 5142 minavailable = maxavailable; 5143 minend = hmin; 5144 ubcand = -1; 5145 ubenergy = 0; 5146 5147 /* loop over the job in non-decreasing order w.r.t. the latest completion time; these latest completion times are 5148 * defining the ending of the time interval under investigation; thereby, the time interval gets wider and wider 5149 */ 5150 for( i = nlcts; i < nvars; ++i ) 5151 { 5152 SCIP_VAR* var; 5153 SCIP_Longint freeenergy; 5154 int duration; 5155 int demand; 5156 int idx; 5157 int lct; 5158 int ect; 5159 5160 idx = perm[i]; 5161 assert(idx >= 0); 5162 assert(idx < nvars); 5163 assert(!(*cutoff)); 5164 5165 /* the earliest start time of the job */ 5166 lct = lcts[i]; 5167 5168 /* if the job has a latest completion time before the the current start, we can skip it and do not need to 5169 * consider it again since the earliest start times (which define the start) are scant in non-decreasing order 5170 */ 5171 if( lct <= begin ) 5172 { 5173 nlcts++; 5174 continue; 5175 } 5176 5177 /* check if the interval has a size such that the total energy fits, if so we can skip all intervals which 5178 * start with current beginning time 5179 */ 5180 if( ((SCIP_Longint) lct - begin) * capacity >= totalenergy ) 5181 break; 5182 5183 var = vars[idx]; 5184 assert(var != NULL); 5185 5186 duration = durations[idx]; 5187 assert(duration > 0); 5188 5189 demand = demands[idx]; 5190 assert(demand > 0); 5191 5192 est = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(var)); 5193 5194 /* the earliest completion time of the flexible part of the job */ 5195 ect = ects[idx]; 5196 5197 /* in case the latest completion time is equal to minend, the job lies completely within the time window under 5198 * investigation; hence the overload check will do the the job 5199 */ 5200 assert(lct >= minend); 5201 if( minavailable < maxavailable && lct > minend ) 5202 { 5203 assert(!(*cutoff)); 5204 5205 /* try to tighten the upper bound */ 5206 SCIP_CALL( tightenLbTTEF(scip, conshdlrdata, nvars, vars, durations, demands, capacity, hmin, hmax, 5207 var, duration, demand, est, ect, lct, begin, minend, minavailable, &(newlbs[idx]), &(lbinferinfos[idx]), 5208 initialized, explanation, cutoff) ); 5209 5210 if( *cutoff ) 5211 return SCIP_OKAY; 5212 } 5213 5214 SCIPdebugMsg(scip, "check variable <%s>[%g,%g] (duration %d, demands %d, est <%d>, ect of free part <%d>\n", 5215 SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var), duration, demand, est, ect); 5216 5217 end = lct; 5218 assert(SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(var)) + duration == lct); 5219 5220 /* if the latest completion time is larger than hmax we can stop here since the next job will not decrease the 5221 * free energy 5222 */ 5223 if( end > hmax ) 5224 break; 5225 5226 /* compute the contribution to the flexible energy */ 5227 if( est >= begin ) 5228 { 5229 /* if the jobs has to finish before the end, all the energy has to be scheduled */ 5230 assert(ect <= end); 5231 assert(flexenergies[idx] >= 0); 5232 flexenergy += flexenergies[idx]; 5233 } 5234 else 5235 { 5236 /* the job partly overlaps with the end */ 5237 int candenergy; 5238 int energy; 5239 5240 /* compute the flexible energy which is part of the time interval for sure if the job is scheduled 5241 * w.r.t. latest start time 5242 * 5243 * @note we need to be aware of the effective horizon 5244 */ 5245 energy = MIN(flexenergies[idx], demands[idx] * MAX(0, (ect - begin))); 5246 assert(ect - begin < duration); 5247 assert(energy >= 0); 5248 5249 /* adjust the flexible energy of the time interval */ 5250 flexenergy += energy; 5251 5252 /* compute the flexible energy of the job which is not part of flexible energy of the time interval */ 5253 candenergy = MIN(flexenergies[idx], demands[idx] * (end - begin)) - energy; 5254 assert(candenergy >= 0); 5255 5256 /* check if we found a better candidate */ 5257 if( candenergy > ubenergy ) 5258 { 5259 ubenergy = candenergy; 5260 ubcand = idx; 5261 } 5262 } 5263 5264 SCIPdebugMsg(scip, "time window [%d,%d) flexible energy <%d>\n", begin, end, flexenergy); 5265 assert(coreEnergyAfterLct[i] <= coreEnergyAfterStart); 5266 5267 /* compute the energy which is not used yet */ 5268 freeenergy = capacity * ((SCIP_Longint) end - begin) - flexenergy - coreEnergyAfterStart + coreEnergyAfterLct[i]; 5269 5270 /* check overload */ 5271 if( freeenergy < 0 ) 5272 { 5273 SCIPdebugMsg(scip, "analyze overload within time window [%d,%d) capacity %d\n", begin, end, capacity); 5274 5275 /* initialize conflict analysis if conflict analysis is applicable */ 5276 if( SCIPisConflictAnalysisApplicable(scip) ) 5277 { 5278 /* analyze infeasibilty */ 5279 SCIP_CALL( SCIPinitConflictAnalysis(scip, SCIP_CONFTYPE_PROPAGATION, FALSE) ); 5280 5281 SCIP_CALL( analyzeEnergyRequirement(scip, nvars, vars, durations, demands, capacity, 5282 begin, end, NULL, SCIP_BOUNDTYPE_UPPER, NULL, SCIP_UNKNOWN, 5283 conshdlrdata->usebdwidening, explanation) ); 5284 5285 (*initialized) = TRUE; 5286 } 5287 5288 (*cutoff) = TRUE; 5289 5290 /* for the statistic we count the number of times a cutoff was detected due the time-time-edge-finding */ 5291 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->ncutoffoverloadTTEF++ ); 5292 5293 return SCIP_OKAY; 5294 } 5295 5296 /* check if the available energy is not sufficent to schedule the flexible energy of the best candidate job */ 5297 if( ubenergy > 0 && freeenergy < ubenergy ) 5298 { 5299 SCIP_Longint energy; 5300 int newub; 5301 int lst; 5302 5303 duration = durations[ubcand]; 5304 assert(duration > 0); 5305 5306 ect = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(vars[ubcand])) + duration; 5307 lst = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(vars[ubcand])); 5308 5309 /* remove the energy of our job from the ... */ 5310 energy = freeenergy + (computeCoreWithInterval(begin, end, ect, lst) + MAX(0, (SCIP_Longint) ects[ubcand] - begin)) * demands[ubcand]; 5311 5312 newub = begin - duration + (int)(energy / demands[ubcand]); 5313 5314 if( newub < ect - duration ) 5315 { 5316 /* initialize conflict analysis if conflict analysis is applicable */ 5317 if( SCIPisConflictAnalysisApplicable(scip) ) 5318 { 5319 SCIP_Real relaxedbd; 5320 /* analyze infeasibilty */ 5321 SCIP_CALL( SCIPinitConflictAnalysis(scip, SCIP_CONFTYPE_PROPAGATION, FALSE) ); 5322 5323 relaxedbd = ect - duration - 1.0; 5324 5325 /* added to lower bound (which was undercut be new upper bound) of the variable */ 5326 SCIP_CALL( SCIPaddConflictUb(scip, vars[ubcand], NULL) ); 5327 5328 SCIP_CALL( analyzeEnergyRequirement(scip, nvars, vars, durations, demands, capacity, 5329 begin, end, vars[ubcand], SCIP_BOUNDTYPE_UPPER, NULL, relaxedbd, 5330 conshdlrdata->usebdwidening, explanation) ); 5331 5332 (*initialized) = TRUE; 5333 } 5334 5335 (*cutoff) = TRUE; 5336 return SCIP_OKAY; 5337 } 5338 else if( newub < newubs[ubcand] ) 5339 { 5340 INFERINFO inferinfo; 5341 5342 /* construct inference information */ 5343 inferinfo = getInferInfo(PROPRULE_3_TTEF, begin, end); 5344 5345 /* buffer upper bound change */ 5346 ubinferinfos[ubcand] = inferInfoToInt(inferinfo); 5347 newubs[ubcand] = newub; 5348 } 5349 } 5350 5351 /* check if the current interval has a smaller free energy */ 5352 if( minavailable > freeenergy ) 5353 { 5354 minavailable = freeenergy; 5355 minend = end; 5356 } 5357 assert(minavailable >= 0); 5358 } 5359 } 5360 5361 return SCIP_OKAY; 5362 } 5363 5364 /** checks whether the instance is infeasible due to a overload within a certain time frame using the idea of time-table 5365 * edge-finding 5366 * 5367 * @note The algorithm is based on the following two papers: 5368 * - Petr Vilim, "Timetable Edge Finding Filtering Algorithm for Discrete Cumulative Resources", In: Tobias 5369 * Achterberg and J. Christopher Beck (Eds.), Integration of AI and OR Techniques in Constraint Programming for 5370 * Combinatorial Optimization Problems (CPAIOR 2011), LNCS 6697, pp 230--245 5371 * - Andreas Schutt, Thibaut Feydy, and Peter J. Stuckey, "Explaining Time-Table-Edge-Finding Propagation for the 5372 * Cumulative Resource Constraint (submitted to CPAIOR 2013) 5373 */ 5374 static 5375 SCIP_RETCODE propagateTTEF( 5376 SCIP* scip, /**< SCIP data structure */ 5377 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */ 5378 SCIP_PROFILE* profile, /**< current core profile */ 5379 int nvars, /**< number of start time variables (activities) */ 5380 SCIP_VAR** vars, /**< array of start time variables */ 5381 int* durations, /**< array of durations */ 5382 int* demands, /**< array of demands */ 5383 int capacity, /**< cumulative capacity */ 5384 int hmin, /**< left bound of time axis to be considered (including hmin) */ 5385 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 5386 SCIP_CONS* cons, /**< constraint which is propagated (needed to SCIPinferVar**Cons()) */ 5387 int* nchgbds, /**< pointer to store the number of bound changes */ 5388 SCIP_Bool* initialized, /**< was conflict analysis initialized */ 5389 SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */ 5390 SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */ 5391 ) 5392 { 5393 int* coreEnergyAfterEst; 5394 int* coreEnergyAfterLct; 5395 int* flexenergies; 5396 int* permests; 5397 int* permlcts; 5398 int* lcts; 5399 int* ests; 5400 int* ects; 5401 int* lsts; 5402 5403 int* newlbs; 5404 int* newubs; 5405 int* lbinferinfos; 5406 int* ubinferinfos; 5407 5408 int v; 5409 5410 /* check if a cutoff was already detected */ 5411 if( (*cutoff) ) 5412 return SCIP_OKAY; 5413 5414 /* check if at least the basic overload checking should be perfomed */ 5415 if( !conshdlrdata->ttefcheck ) 5416 return SCIP_OKAY; 5417 5418 SCIPdebugMsg(scip, "run time-table edge-finding overload checking\n"); 5419 5420 SCIP_CALL( SCIPallocBufferArray(scip, &coreEnergyAfterEst, nvars) ); 5421 SCIP_CALL( SCIPallocBufferArray(scip, &coreEnergyAfterLct, nvars) ); 5422 SCIP_CALL( SCIPallocBufferArray(scip, &flexenergies, nvars) ); 5423 SCIP_CALL( SCIPallocBufferArray(scip, &permlcts, nvars) ); 5424 SCIP_CALL( SCIPallocBufferArray(scip, &permests, nvars) ); 5425 SCIP_CALL( SCIPallocBufferArray(scip, &lcts, nvars) ); 5426 SCIP_CALL( SCIPallocBufferArray(scip, &ests, nvars) ); 5427 SCIP_CALL( SCIPallocBufferArray(scip, &ects, nvars) ); 5428 SCIP_CALL( SCIPallocBufferArray(scip, &lsts, nvars) ); 5429 5430 SCIP_CALL( SCIPallocBufferArray(scip, &newlbs, nvars) ); 5431 SCIP_CALL( SCIPallocBufferArray(scip, &newubs, nvars) ); 5432 SCIP_CALL( SCIPallocBufferArray(scip, &lbinferinfos, nvars) ); 5433 SCIP_CALL( SCIPallocBufferArray(scip, &ubinferinfos, nvars) ); 5434 5435 /* we need to buffer the bound changes since the propagation algorithm cannot handle new bound dynamically */ 5436 for( v = 0; v < nvars; ++v ) 5437 { 5438 newlbs[v] = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(vars[v])); 5439 newubs[v] = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(vars[v])); 5440 lbinferinfos[v] = 0; 5441 ubinferinfos[v] = 0; 5442 } 5443 5444 /* collect earliest start times, latest completion time, and free energy contributions */ 5445 collectDataTTEF(scip, nvars, vars, durations, demands, hmin, hmax, permests, ests, permlcts, lcts, ects, lsts, flexenergies); 5446 5447 /* sort the earliest start times and latest completion in non-decreasing order */ 5448 SCIPsortIntInt(ests, permests, nvars); 5449 SCIPsortIntInt(lcts, permlcts, nvars); 5450 5451 /* compute for the different earliest start and latest completion time the core energy of the corresponding time 5452 * points 5453 */ 5454 computeCoreEnergyAfter(profile, nvars, ests, lcts, coreEnergyAfterEst, coreEnergyAfterLct); 5455 5456 /* propagate the upper bounds and "opportunistically" the lower bounds */ 5457 SCIP_CALL( propagateUbTTEF(scip, conshdlrdata, nvars, vars, durations, demands, capacity, hmin, hmax, 5458 newlbs, newubs, lbinferinfos, ubinferinfos, lsts, flexenergies, 5459 permests, ests, lcts, coreEnergyAfterEst, coreEnergyAfterLct, initialized, explanation, cutoff) ); 5460 5461 /* propagate the lower bounds and "opportunistically" the upper bounds */ 5462 SCIP_CALL( propagateLbTTEF(scip, conshdlrdata, nvars, vars, durations, demands, capacity, hmin, hmax, 5463 newlbs, newubs, lbinferinfos, ubinferinfos, ects, flexenergies, 5464 permlcts, ests, lcts, coreEnergyAfterEst, coreEnergyAfterLct, initialized, explanation, cutoff) ); 5465 5466 /* apply the buffer bound changes */ 5467 for( v = 0; v < nvars && !(*cutoff); ++v ) 5468 { 5469 SCIP_Bool infeasible; 5470 SCIP_Bool tightened; 5471 5472 if( inferInfoIsValid(intToInferInfo(lbinferinfos[v])) ) 5473 { 5474 SCIP_CALL( SCIPinferVarLbCons(scip, vars[v], (SCIP_Real)newlbs[v], cons, lbinferinfos[v], 5475 TRUE, &infeasible, &tightened) ); 5476 } 5477 else 5478 { 5479 SCIP_CALL( SCIPtightenVarLb(scip, vars[v], (SCIP_Real)newlbs[v], TRUE, &infeasible, &tightened) ); 5480 } 5481 5482 /* since we change first the lower bound of the variable an infeasibilty should not be detected */ 5483 assert(!infeasible); 5484 5485 if( tightened ) 5486 { 5487 (*nchgbds)++; 5488 5489 /* for the statistic we count the number of times a cutoff was detected due the time-time */ 5490 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->nlbTTEF++ ); 5491 } 5492 5493 if( inferInfoIsValid(intToInferInfo(ubinferinfos[v])) ) 5494 { 5495 SCIP_CALL( SCIPinferVarUbCons(scip, vars[v], (SCIP_Real)newubs[v], cons, ubinferinfos[v], 5496 TRUE, &infeasible, &tightened) ); 5497 } 5498 else 5499 { 5500 SCIP_CALL( SCIPtightenVarUb(scip, vars[v], (SCIP_Real)newubs[v], TRUE, &infeasible, &tightened) ); 5501 } 5502 5503 /* since upper bound was compute w.r.t. the "old" bound the previous lower bound update together with this upper 5504 * bound update can be infeasible 5505 */ 5506 if( infeasible ) 5507 { 5508 /* a small performance improvement is possible here: if the tighten...TEFF and propagate...TEFF methods would 5509 * return not only the inferinfos, but the actual begin and end values, then the infeasibility here could also 5510 * be analyzed in the case when begin and end exceed the 15 bit limit 5511 */ 5512 if( SCIPisConflictAnalysisApplicable(scip) && inferInfoIsValid(intToInferInfo(ubinferinfos[v])) ) 5513 { 5514 INFERINFO inferinfo; 5515 SCIP_VAR* var; 5516 int begin; 5517 int end; 5518 5519 var = vars[v]; 5520 assert(var != NULL); 5521 5522 /* initialize conflict analysis */ 5523 SCIP_CALL( SCIPinitConflictAnalysis(scip, SCIP_CONFTYPE_PROPAGATION, FALSE) ); 5524 5525 /* convert int to inference information */ 5526 inferinfo = intToInferInfo(ubinferinfos[v]); 5527 5528 /* collect time window from inference information */ 5529 begin = inferInfoGetData1(inferinfo); 5530 end = inferInfoGetData2(inferinfo); 5531 assert(begin < end); 5532 5533 /* added to lower bound (which was undercut be new upper bound) of the variable */ 5534 SCIP_CALL( SCIPaddConflictLb(scip, var, NULL) ); 5535 5536 /* analysis the upper bound change */ 5537 SCIP_CALL( analyzeEnergyRequirement(scip, nvars, vars, durations, demands, capacity, 5538 begin, end, var, SCIP_BOUNDTYPE_UPPER, NULL, SCIPvarGetLbLocal(vars[v]) - 1.0, 5539 conshdlrdata->usebdwidening, explanation) ); 5540 5541 (*initialized) = TRUE; 5542 } 5543 5544 /* for the statistic we count the number of times a cutoff was detected due the time-time */ 5545 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->ncutoffoverloadTTEF++ ); 5546 5547 (*cutoff) = TRUE; 5548 break; 5549 } 5550 5551 if( tightened ) 5552 { 5553 (*nchgbds)++; 5554 5555 /* for the statistic we count the number of times a cutoff was detected due the time-time */ 5556 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->nubTTEF++ ); 5557 } 5558 } 5559 5560 SCIPfreeBufferArray(scip, &ubinferinfos); 5561 SCIPfreeBufferArray(scip, &lbinferinfos); 5562 SCIPfreeBufferArray(scip, &newubs); 5563 SCIPfreeBufferArray(scip, &newlbs); 5564 5565 /* free buffer arrays */ 5566 SCIPfreeBufferArray(scip, &lsts); 5567 SCIPfreeBufferArray(scip, &ects); 5568 SCIPfreeBufferArray(scip, &ests); 5569 SCIPfreeBufferArray(scip, &lcts); 5570 SCIPfreeBufferArray(scip, &permests); 5571 SCIPfreeBufferArray(scip, &permlcts); 5572 SCIPfreeBufferArray(scip, &flexenergies); 5573 SCIPfreeBufferArray(scip, &coreEnergyAfterLct); 5574 SCIPfreeBufferArray(scip, &coreEnergyAfterEst); 5575 5576 return SCIP_OKAY; 5577 } 5578 5579 /** a cumulative condition is not satisfied if its capacity is exceeded at a time where jobs cannot be shifted (core) 5580 * anymore we build up a cumulative profile of all cores of jobs and try to improve bounds of all jobs; also known as 5581 * time table propagator 5582 */ 5583 static 5584 SCIP_RETCODE propagateTimetable( 5585 SCIP* scip, /**< SCIP data structure */ 5586 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */ 5587 SCIP_PROFILE* profile, /**< core profile */ 5588 int nvars, /**< number of start time variables (activities) */ 5589 SCIP_VAR** vars, /**< array of start time variables */ 5590 int* durations, /**< array of durations */ 5591 int* demands, /**< array of demands */ 5592 int capacity, /**< cumulative capacity */ 5593 int hmin, /**< left bound of time axis to be considered (including hmin) */ 5594 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 5595 SCIP_CONS* cons, /**< constraint which is propagated (needed to SCIPinferVar**Cons()) */ 5596 int* nchgbds, /**< pointer to store the number of bound changes */ 5597 SCIP_Bool* initialized, /**< was conflict analysis initialized */ 5598 SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */ 5599 SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */ 5600 ) 5601 { 5602 SCIP_Bool infeasible; 5603 int v; 5604 5605 assert(scip != NULL); 5606 assert(nvars > 0); 5607 assert(cons != NULL); 5608 assert(cutoff != NULL); 5609 5610 /* check if already a cutoff was detected */ 5611 if( (*cutoff) ) 5612 return SCIP_OKAY; 5613 5614 /* check if the time tabling should infer bounds */ 5615 if( !conshdlrdata->ttinfer ) 5616 return SCIP_OKAY; 5617 5618 assert(*initialized == FALSE); 5619 5620 SCIPdebugMsg(scip, "propagate cores of cumulative condition of constraint <%s>[%d,%d) <= %d\n", 5621 SCIPconsGetName(cons), hmin, hmax, capacity); 5622 5623 infeasible = FALSE; 5624 5625 /* if core profile is empty; nothing to do */ 5626 if( SCIPprofileGetNTimepoints(profile) <= 1 ) 5627 return SCIP_OKAY; 5628 5629 /* start checking each job whether the bounds can be improved */ 5630 for( v = 0; v < nvars; ++v ) 5631 { 5632 SCIP_VAR* var; 5633 int demand; 5634 int duration; 5635 int begin; 5636 int end; 5637 int est; 5638 int lst; 5639 5640 var = vars[v]; 5641 assert(var != NULL); 5642 5643 duration = durations[v]; 5644 assert(duration > 0); 5645 5646 /* collect earliest and latest start time */ 5647 est = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(var)); 5648 lst = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(var)); 5649 5650 /* check if the start time variables is already fixed; in that case we can ignore the job */ 5651 if( est == lst ) 5652 continue; 5653 5654 /* check if the job runs completely outside of the effective horizon [hmin, hmax); if so skip it */ 5655 if( lst + duration <= hmin || est >= hmax ) 5656 continue; 5657 5658 /* compute core interval w.r.t. effective time horizon */ 5659 begin = MAX(hmin, lst); 5660 end = MIN(hmax, est + duration); 5661 5662 demand = demands[v]; 5663 assert(demand > 0); 5664 5665 /* if the job has a core, remove it first */ 5666 if( begin < end ) 5667 { 5668 SCIPdebugMsg(scip, "variable <%s>[%g,%g] (duration %d, demand %d): remove core [%d,%d)\n", 5669 SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var), duration, demand, begin, end); 5670 5671 SCIP_CALL( SCIPprofileDeleteCore(profile, begin, end, demand) ); 5672 } 5673 5674 /* first try to update the earliest start time */ 5675 SCIP_CALL( coretimesUpdateLb(scip, nvars, vars, durations, demands, capacity, hmin, hmax, cons, 5676 profile, v, nchgbds, conshdlrdata->usebdwidening, initialized, explanation, cutoff) ); 5677 5678 if( *cutoff ) 5679 break; 5680 5681 /* second try to update the latest start time */ 5682 SCIP_CALL( coretimesUpdateUb(scip, var, duration, demand, capacity, cons, 5683 profile, v, nchgbds) ); 5684 5685 if( *cutoff ) 5686 break; 5687 5688 /* collect the potentially updated earliest and latest start time */ 5689 est = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(var)); 5690 lst = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(var)); 5691 5692 /* compute core interval w.r.t. effective time horizon */ 5693 begin = MAX(hmin, lst); 5694 end = MIN(hmax, est + duration); 5695 5696 /* after updating the bound we might have a new core */ 5697 if( begin < end ) 5698 { 5699 int pos; 5700 5701 SCIPdebugMsg(scip, "variable <%s>[%d,%d] (duration %d, demand %d): add core [%d,%d)\n", 5702 SCIPvarGetName(var), est, lst, duration, demand, begin, end); 5703 5704 SCIP_CALL( SCIPprofileInsertCore(profile, begin, end, demand, &pos, &infeasible) ); 5705 5706 if( infeasible ) 5707 { 5708 /* use conflict analysis to analysis the core insertion which was infeasible */ 5709 SCIP_CALL( analyseInfeasibelCoreInsertion(scip, nvars, vars, durations, demands, capacity, hmin, hmax, 5710 var, duration, demand, SCIPprofileGetTime(profile, pos), conshdlrdata->usebdwidening, initialized, explanation) ); 5711 5712 if( explanation != NULL ) 5713 explanation[v] = TRUE; 5714 5715 (*cutoff) = TRUE; 5716 5717 /* for the statistic we count the number of times a cutoff was detected due the time-time */ 5718 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->ncutofftimetable++ ); 5719 5720 break; 5721 } 5722 } 5723 } 5724 5725 return SCIP_OKAY; 5726 } 5727 5728 5729 /** node data structure for the binary tree used for edgefinding (with overload checking) */ 5730 struct SCIP_NodeData 5731 { 5732 SCIP_VAR* var; /**< start time variable of the job if the node data belongs to a leaf, otherwise NULL */ 5733 SCIP_Real key; /**< key which is to insert the corresponding search node */ 5734 int est; /**< earliest start time if the node data belongs to a leaf */ 5735 int lct; /**< latest completion time if the node data belongs to a leaf */ 5736 int demand; /**< demand of the job if the node data belongs to a leaf */ 5737 int duration; /**< duration of the job if the node data belongs to a leaf */ 5738 int leftadjust; /**< left adjustments of the duration w.r.t. hmin */ 5739 int rightadjust; /**< right adjustments of the duration w.r.t. hmax */ 5740 SCIP_Longint enveloptheta; /**< the maximal energy of a subset of jobs part of the theta set */ 5741 int energytheta; /**< energy of the subset of the jobs which are part of theta set */ 5742 int energylambda; 5743 SCIP_Longint enveloplambda; 5744 int idx; /**< index of the start time variable in the (global) variable array */ 5745 SCIP_Bool intheta; /**< belongs the node to the theta set (otherwise to the lambda set) */ 5746 }; 5747 typedef struct SCIP_NodeData SCIP_NODEDATA; 5748 5749 5750 /** update node data structure starting from the given node along the path to the root node */ 5751 static 5752 void updateEnvelope( 5753 SCIP* scip, /**< SCIP data structure */ 5754 SCIP_BTNODE* node /**< search node which inserted */ 5755 ) 5756 { 5757 SCIP_BTNODE* left; 5758 SCIP_BTNODE* right; 5759 SCIP_NODEDATA* nodedata; 5760 SCIP_NODEDATA* leftdata; 5761 SCIP_NODEDATA* rightdata; 5762 5763 SCIPdebugMsg(scip, "update envelop starting from node <%p>\n", (void*)node); 5764 5765 if( SCIPbtnodeIsLeaf(node) ) 5766 node = SCIPbtnodeGetParent(node); 5767 5768 while( node != NULL ) 5769 { 5770 /* get node data */ 5771 nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(node); 5772 assert(nodedata != NULL); 5773 5774 /* collect node data from left node */ 5775 left = SCIPbtnodeGetLeftchild(node); 5776 assert(left != NULL); 5777 leftdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(left); 5778 assert(leftdata != NULL); 5779 5780 /* collect node data from right node */ 5781 right = SCIPbtnodeGetRightchild(node); 5782 assert(right != NULL); 5783 rightdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(right); 5784 assert(rightdata != NULL); 5785 5786 /* update envelop and energy */ 5787 if( leftdata->enveloptheta >= 0 ) 5788 { 5789 assert(rightdata->energytheta != -1); 5790 nodedata->enveloptheta = MAX(leftdata->enveloptheta + rightdata->energytheta, rightdata->enveloptheta); 5791 } 5792 else 5793 nodedata->enveloptheta = rightdata->enveloptheta; 5794 5795 assert(leftdata->energytheta != -1); 5796 assert(rightdata->energytheta != -1); 5797 nodedata->energytheta = leftdata->energytheta + rightdata->energytheta; 5798 5799 if( leftdata->enveloplambda >= 0 ) 5800 { 5801 assert(rightdata->energytheta != -1); 5802 nodedata->enveloplambda = MAX(leftdata->enveloplambda + rightdata->energytheta, rightdata->enveloplambda); 5803 } 5804 else 5805 nodedata->enveloplambda = rightdata->enveloplambda; 5806 5807 if( leftdata->enveloptheta >= 0 && rightdata->energylambda >= 0 ) 5808 nodedata->enveloplambda = MAX(nodedata->enveloplambda, leftdata->enveloptheta + rightdata->energylambda); 5809 5810 SCIPdebugMsg(scip, "node <%p> lambda envelop %" SCIP_LONGINT_FORMAT "\n", (void*)node, nodedata->enveloplambda); 5811 5812 if( leftdata->energylambda >= 0 && rightdata->energylambda >= 0 ) 5813 { 5814 assert(rightdata->energytheta != -1); 5815 assert(leftdata->energytheta != -1); 5816 nodedata->energylambda = MAX(leftdata->energylambda + rightdata->energytheta, leftdata->energytheta + rightdata->energylambda); 5817 } 5818 else if( rightdata->energylambda >= 0 ) 5819 { 5820 assert(leftdata->energytheta != -1); 5821 nodedata->energylambda = leftdata->energytheta + rightdata->energylambda; 5822 } 5823 else if( leftdata->energylambda >= 0 ) 5824 { 5825 assert(rightdata->energytheta != -1); 5826 nodedata->energylambda = leftdata->energylambda + rightdata->energytheta; 5827 } 5828 else 5829 nodedata->energylambda = -1; 5830 5831 /* go to parent */ 5832 node = SCIPbtnodeGetParent(node); 5833 } 5834 5835 SCIPdebugMsg(scip, "updating done\n"); 5836 } 5837 5838 /** updates the key of the first parent on the trace which comes from left */ 5839 static 5840 void updateKeyOnTrace( 5841 SCIP_BTNODE* node, /**< node to start the trace */ 5842 SCIP_Real key /**< update search key */ 5843 ) 5844 { 5845 assert(node != NULL); 5846 5847 while( !SCIPbtnodeIsRoot(node) ) 5848 { 5849 SCIP_BTNODE* parent; 5850 5851 parent = SCIPbtnodeGetParent(node); 5852 assert(parent != NULL); 5853 5854 if( SCIPbtnodeIsLeftchild(node) ) 5855 { 5856 SCIP_NODEDATA* nodedata; 5857 5858 nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(parent); 5859 assert(nodedata != NULL); 5860 5861 nodedata->key = key; 5862 return; 5863 } 5864 5865 node = parent; 5866 } 5867 } 5868 5869 5870 /** deletes the given node and updates all envelops */ 5871 static 5872 SCIP_RETCODE deleteLambdaLeaf( 5873 SCIP* scip, /**< SCIP data structure */ 5874 SCIP_BT* tree, /**< binary tree */ 5875 SCIP_BTNODE* node /**< node to be deleted */ 5876 ) 5877 { 5878 SCIP_BTNODE* parent; 5879 SCIP_BTNODE* grandparent; 5880 SCIP_BTNODE* sibling; 5881 5882 assert(scip != NULL); 5883 assert(tree != NULL); 5884 assert(node != NULL); 5885 5886 assert(SCIPbtnodeIsLeaf(node)); 5887 assert(!SCIPbtnodeIsRoot(node)); 5888 5889 SCIPdebugMsg(scip, "delete node <%p>\n", (void*)node); 5890 5891 parent = SCIPbtnodeGetParent(node); 5892 assert(parent != NULL); 5893 if( SCIPbtnodeIsLeftchild(node) ) 5894 { 5895 sibling = SCIPbtnodeGetRightchild(parent); 5896 SCIPbtnodeSetRightchild(parent, NULL); 5897 } 5898 else 5899 { 5900 sibling = SCIPbtnodeGetLeftchild(parent); 5901 SCIPbtnodeSetLeftchild(parent, NULL); 5902 } 5903 assert(sibling != NULL); 5904 5905 grandparent = SCIPbtnodeGetParent(parent); 5906 5907 if( grandparent != NULL ) 5908 { 5909 /* reset parent of sibling */ 5910 SCIPbtnodeSetParent(sibling, grandparent); 5911 5912 /* reset child of grandparent to sibling */ 5913 if( SCIPbtnodeIsLeftchild(parent) ) 5914 { 5915 SCIPbtnodeSetLeftchild(grandparent, sibling); 5916 } 5917 else 5918 { 5919 SCIP_NODEDATA* nodedata; 5920 5921 assert(SCIPbtnodeIsRightchild(parent)); 5922 SCIPbtnodeSetRightchild(grandparent, sibling); 5923 5924 nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(sibling); 5925 5926 updateKeyOnTrace(grandparent, nodedata->key); 5927 } 5928 5929 updateEnvelope(scip, grandparent); 5930 } 5931 else 5932 { 5933 SCIPbtnodeSetParent(sibling, NULL); 5934 5935 SCIPbtSetRoot(tree, sibling); 5936 } 5937 5938 SCIPbtnodeFree(tree, &parent); 5939 5940 return SCIP_OKAY; 5941 } 5942 5943 /** moves a node form the theta set into the lambda set and updates the envelops */ 5944 static 5945 SCIP_RETCODE moveNodeToLambda( 5946 SCIP* scip, /**< SCIP data structure */ 5947 SCIP_BT* tree, /**< binary tree */ 5948 SCIP_BTNODE* node /**< node to move into the lambda set */ 5949 ) 5950 { 5951 SCIP_NODEDATA* nodedata; 5952 5953 assert(scip != NULL); 5954 assert(tree != NULL); 5955 assert(node != NULL); 5956 5957 nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(node); 5958 assert(nodedata != NULL); 5959 assert(nodedata->intheta); 5960 5961 /* move the contributions form the theta set into the lambda set */ 5962 assert(nodedata->enveloptheta != -1); 5963 assert(nodedata->energytheta != -1); 5964 assert(nodedata->enveloplambda == -1); 5965 assert(nodedata->energylambda == -1); 5966 nodedata->enveloplambda = nodedata->enveloptheta; 5967 nodedata->energylambda = nodedata->energytheta; 5968 5969 nodedata->enveloptheta = -1; 5970 nodedata->energytheta = 0; 5971 nodedata->intheta = FALSE; 5972 5973 /* update the energy and envelop values on trace */ 5974 updateEnvelope(scip, node); 5975 5976 return SCIP_OKAY; 5977 } 5978 5979 /** inserts a node into the theta set and update the envelops */ 5980 static 5981 SCIP_RETCODE insertThetanode( 5982 SCIP* scip, /**< SCIP data structure */ 5983 SCIP_BT* tree, /**< binary tree */ 5984 SCIP_BTNODE* node, /**< node to insert */ 5985 SCIP_NODEDATA* nodedatas, /**< array of node data */ 5986 int* nodedataidx, /**< array of indices for node data */ 5987 int* nnodedatas /**< pointer to number of node data */ 5988 ) 5989 { 5990 /* if the tree is empty the node will be the root node */ 5991 if( SCIPbtIsEmpty(tree) ) 5992 { 5993 SCIPbtSetRoot(tree, node); 5994 } 5995 else 5996 { 5997 SCIP_NODEDATA* newnodedata; 5998 SCIP_NODEDATA* leafdata; 5999 SCIP_NODEDATA* nodedata; 6000 SCIP_BTNODE* leaf; 6001 SCIP_BTNODE* newnode; 6002 SCIP_BTNODE* parent; 6003 6004 leaf = SCIPbtGetRoot(tree); 6005 assert(leaf != NULL); 6006 6007 leafdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(leaf); 6008 assert(leafdata != NULL); 6009 6010 nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(node); 6011 assert(nodedata != NULL); 6012 assert(nodedata->intheta); 6013 6014 /* find the position to insert the node */ 6015 while( !SCIPbtnodeIsLeaf(leaf) ) 6016 { 6017 if( nodedata->key < leafdata->key ) 6018 leaf = SCIPbtnodeGetLeftchild(leaf); 6019 else 6020 leaf = SCIPbtnodeGetRightchild(leaf); 6021 6022 leafdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(leaf); 6023 assert(leafdata != NULL); 6024 } 6025 6026 assert(leaf != NULL); 6027 assert(leaf != node); 6028 6029 /* store node data to be able to delete them latter */ 6030 newnodedata = &nodedatas[*nnodedatas]; 6031 nodedataidx[*nnodedatas] = *nnodedatas; 6032 ++(*nnodedatas); 6033 6034 /* init node data */ 6035 newnodedata->var = NULL; 6036 newnodedata->key = SCIP_INVALID; 6037 newnodedata->est = INT_MIN; 6038 newnodedata->lct = INT_MAX; 6039 newnodedata->duration = 0; 6040 newnodedata->demand = 0; 6041 newnodedata->enveloptheta = -1; 6042 newnodedata->energytheta = 0; 6043 newnodedata->enveloplambda = -1; 6044 newnodedata->energylambda = -1; 6045 newnodedata->idx = -1; 6046 newnodedata->intheta = TRUE; 6047 6048 /* create a new node */ 6049 SCIP_CALL( SCIPbtnodeCreate(tree, &newnode, newnodedata) ); 6050 assert(newnode != NULL); 6051 6052 parent = SCIPbtnodeGetParent(leaf); 6053 6054 if( parent != NULL ) 6055 { 6056 SCIPbtnodeSetParent(newnode, parent); 6057 6058 /* check if the node is the left child */ 6059 if( SCIPbtnodeGetLeftchild(parent) == leaf ) 6060 { 6061 SCIPbtnodeSetLeftchild(parent, newnode); 6062 } 6063 else 6064 { 6065 SCIPbtnodeSetRightchild(parent, newnode); 6066 } 6067 } 6068 else 6069 SCIPbtSetRoot(tree, newnode); 6070 6071 if( nodedata->key < leafdata->key ) 6072 { 6073 /* node is on the left */ 6074 SCIPbtnodeSetLeftchild(newnode, node); 6075 SCIPbtnodeSetRightchild(newnode, leaf); 6076 newnodedata->key = nodedata->key; 6077 } 6078 else 6079 { 6080 /* leaf is on the left */ 6081 SCIPbtnodeSetLeftchild(newnode, leaf); 6082 SCIPbtnodeSetRightchild(newnode, node); 6083 newnodedata->key = leafdata->key; 6084 } 6085 6086 SCIPbtnodeSetParent(leaf, newnode); 6087 SCIPbtnodeSetParent(node, newnode); 6088 } 6089 6090 /* update envelop */ 6091 updateEnvelope(scip, node); 6092 6093 return SCIP_OKAY; 6094 } 6095 6096 /** returns the leaf responsible for the lambda energy */ 6097 static 6098 SCIP_BTNODE* findResponsibleLambdaLeafTraceEnergy( 6099 SCIP_BTNODE* node /**< node which defines the subtree beases on the lambda energy */ 6100 ) 6101 { 6102 SCIP_BTNODE* left; 6103 SCIP_BTNODE* right; 6104 SCIP_NODEDATA* nodedata; 6105 SCIP_NODEDATA* leftdata; 6106 SCIP_NODEDATA* rightdata; 6107 6108 assert(node != NULL); 6109 6110 nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(node); 6111 assert(nodedata != NULL); 6112 6113 /* check if the node is the (responsible) leaf */ 6114 if( SCIPbtnodeIsLeaf(node) ) 6115 { 6116 assert(!nodedata->intheta); 6117 return node; 6118 } 6119 6120 left = SCIPbtnodeGetLeftchild(node); 6121 assert(left != NULL); 6122 6123 leftdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(left); 6124 assert(leftdata != NULL); 6125 6126 right = SCIPbtnodeGetRightchild(node); 6127 assert(right != NULL); 6128 6129 rightdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(right); 6130 assert(rightdata != NULL); 6131 6132 assert(nodedata->energylambda != -1); 6133 assert(rightdata->energytheta != -1); 6134 6135 if( leftdata->energylambda >= 0 && nodedata->energylambda == leftdata->energylambda + rightdata->energytheta ) 6136 return findResponsibleLambdaLeafTraceEnergy(left); 6137 6138 assert(leftdata->energytheta != -1); 6139 assert(rightdata->energylambda != -1); 6140 assert(nodedata->energylambda == leftdata->energytheta + rightdata->energylambda); 6141 6142 return findResponsibleLambdaLeafTraceEnergy(right); 6143 } 6144 6145 /** returns the leaf responsible for the lambda envelop */ 6146 static 6147 SCIP_BTNODE* findResponsibleLambdaLeafTraceEnvelop( 6148 SCIP_BTNODE* node /**< node which defines the subtree beases on the lambda envelop */ 6149 ) 6150 { 6151 SCIP_BTNODE* left; 6152 SCIP_BTNODE* right; 6153 SCIP_NODEDATA* nodedata; 6154 SCIP_NODEDATA* leftdata; 6155 SCIP_NODEDATA* rightdata; 6156 6157 assert(node != NULL); 6158 6159 nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(node); 6160 assert(nodedata != NULL); 6161 6162 /* check if the node is the (responsible) leaf */ 6163 if( SCIPbtnodeIsLeaf(node) ) 6164 { 6165 assert(!nodedata->intheta); 6166 return node; 6167 } 6168 6169 left = SCIPbtnodeGetLeftchild(node); 6170 assert(left != NULL); 6171 6172 leftdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(left); 6173 assert(leftdata != NULL); 6174 6175 right = SCIPbtnodeGetRightchild(node); 6176 assert(right != NULL); 6177 6178 rightdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(right); 6179 assert(rightdata != NULL); 6180 6181 assert(nodedata->enveloplambda != -1); 6182 assert(rightdata->energytheta != -1); 6183 6184 /* check if the left or right child is the one defining the envelop for the lambda set */ 6185 if( leftdata->enveloplambda >= 0 && nodedata->enveloplambda == leftdata->enveloplambda + rightdata->energytheta ) 6186 return findResponsibleLambdaLeafTraceEnvelop(left); 6187 else if( leftdata->enveloptheta >= 0 && rightdata->energylambda >= 0 6188 && nodedata->enveloplambda == leftdata->enveloptheta + rightdata->energylambda ) 6189 return findResponsibleLambdaLeafTraceEnergy(right); 6190 6191 assert(rightdata->enveloplambda != -1); 6192 assert(nodedata->enveloplambda == rightdata->enveloplambda); 6193 6194 return findResponsibleLambdaLeafTraceEnvelop(right); 6195 } 6196 6197 6198 /** reports all elements from set theta to generate a conflicting set */ 6199 static 6200 void collectThetaSubtree( 6201 SCIP_BTNODE* node, /**< node within a theta subtree */ 6202 SCIP_BTNODE** omegaset, /**< array to store the collected jobs */ 6203 int* nelements, /**< pointer to store the number of elements in omegaset */ 6204 int* est, /**< pointer to store the earliest start time of the omega set */ 6205 int* lct, /**< pointer to store the latest start time of the omega set */ 6206 int* energy /**< pointer to store the energy of the omega set */ 6207 ) 6208 { 6209 SCIP_NODEDATA* nodedata; 6210 6211 nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(node); 6212 assert(nodedata != NULL); 6213 6214 if( !SCIPbtnodeIsLeaf(node) ) 6215 { 6216 collectThetaSubtree(SCIPbtnodeGetLeftchild(node), omegaset, nelements, est, lct, energy); 6217 collectThetaSubtree(SCIPbtnodeGetRightchild(node), omegaset, nelements, est, lct, energy); 6218 } 6219 else if( nodedata->intheta ) 6220 { 6221 assert(nodedata->var != NULL); 6222 SCIPdebugMessage("add variable <%s> as elements %d to omegaset\n", SCIPvarGetName(nodedata->var), *nelements); 6223 6224 omegaset[*nelements] = node; 6225 (*est) = MIN(*est, nodedata->est); 6226 (*lct) = MAX(*lct, nodedata->lct); 6227 (*energy) += (nodedata->duration - nodedata->leftadjust - nodedata->rightadjust) * nodedata->demand; 6228 (*nelements)++; 6229 } 6230 } 6231 6232 6233 /** collect the jobs (omega set) which are contribute to theta envelop from the theta set */ 6234 static 6235 void traceThetaEnvelop( 6236 SCIP_BTNODE* node, /**< node whose theta envelop needs to be backtracked */ 6237 SCIP_BTNODE** omegaset, /**< array to store the collected jobs */ 6238 int* nelements, /**< pointer to store the number of elements in omegaset */ 6239 int* est, /**< pointer to store the earliest start time of the omega set */ 6240 int* lct, /**< pointer to store the latest start time of the omega set */ 6241 int* energy /**< pointer to store the energy of the omega set */ 6242 ) 6243 { 6244 assert(node != NULL); 6245 6246 if( SCIPbtnodeIsLeaf(node) ) 6247 { 6248 collectThetaSubtree(node, omegaset, nelements, est, lct, energy); 6249 } 6250 else 6251 { 6252 SCIP_BTNODE* left; 6253 SCIP_BTNODE* right; 6254 SCIP_NODEDATA* nodedata; 6255 SCIP_NODEDATA* leftdata; 6256 SCIP_NODEDATA* rightdata; 6257 6258 nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(node); 6259 assert(nodedata != NULL); 6260 6261 left = SCIPbtnodeGetLeftchild(node); 6262 assert(left != NULL); 6263 6264 leftdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(left); 6265 assert(leftdata != NULL); 6266 6267 right = SCIPbtnodeGetRightchild(node); 6268 assert(right != NULL); 6269 6270 rightdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(right); 6271 assert(rightdata != NULL); 6272 6273 nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(node); 6274 assert(nodedata != NULL); 6275 6276 assert(nodedata->enveloptheta != -1); 6277 assert(rightdata->energytheta != -1); 6278 6279 if( leftdata->enveloptheta >= 0 && nodedata->enveloptheta == leftdata->enveloptheta + rightdata->energytheta ) 6280 { 6281 traceThetaEnvelop(left, omegaset, nelements, est, lct, energy); 6282 collectThetaSubtree(right, omegaset, nelements, est, lct, energy); 6283 } 6284 else 6285 { 6286 assert(rightdata->enveloptheta != -1); 6287 assert(nodedata->enveloptheta == rightdata->enveloptheta); 6288 traceThetaEnvelop(right, omegaset, nelements, est, lct, energy); 6289 } 6290 } 6291 } 6292 6293 /** collect the jobs (omega set) which are contribute to lambda envelop from the theta set */ 6294 static 6295 void traceLambdaEnergy( 6296 SCIP_BTNODE* node, /**< node whose lambda envelop needs to be backtracked */ 6297 SCIP_BTNODE** omegaset, /**< array to store the collected jobs */ 6298 int* nelements, /**< pointer to store the number of elements in omega set */ 6299 int* est, /**< pointer to store the earliest start time of the omega set */ 6300 int* lct, /**< pointer to store the latest start time of the omega set */ 6301 int* energy /**< pointer to store the energy of the omega set */ 6302 ) 6303 { 6304 SCIP_BTNODE* left; 6305 SCIP_BTNODE* right; 6306 SCIP_NODEDATA* nodedata; 6307 SCIP_NODEDATA* leftdata; 6308 SCIP_NODEDATA* rightdata; 6309 6310 assert(node != NULL); 6311 6312 nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(node); 6313 assert(nodedata != NULL); 6314 6315 /* check if the node is a leaf */ 6316 if( SCIPbtnodeIsLeaf(node) ) 6317 return; 6318 6319 left = SCIPbtnodeGetLeftchild(node); 6320 assert(left != NULL); 6321 6322 leftdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(left); 6323 assert(leftdata != NULL); 6324 6325 right = SCIPbtnodeGetRightchild(node); 6326 assert(right != NULL); 6327 6328 rightdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(right); 6329 assert(rightdata != NULL); 6330 6331 assert(nodedata->energylambda != -1); 6332 assert(rightdata->energytheta != -1); 6333 6334 if( leftdata->energylambda >= 0 && nodedata->energylambda == leftdata->energylambda + rightdata->energytheta ) 6335 { 6336 traceLambdaEnergy(left, omegaset, nelements, est, lct, energy); 6337 collectThetaSubtree(right, omegaset, nelements, est, lct, energy); 6338 } 6339 else 6340 { 6341 assert(leftdata->energytheta != -1); 6342 assert(rightdata->energylambda != -1); 6343 assert(nodedata->energylambda == leftdata->energytheta + rightdata->energylambda); 6344 6345 collectThetaSubtree(left, omegaset, nelements, est, lct, energy); 6346 traceLambdaEnergy(right, omegaset, nelements, est, lct, energy); 6347 } 6348 } 6349 6350 /** collect the jobs (omega set) which are contribute to lambda envelop from the theta set */ 6351 static 6352 void traceLambdaEnvelop( 6353 SCIP_BTNODE* node, /**< node whose lambda envelop needs to be backtracked */ 6354 SCIP_BTNODE** omegaset, /**< array to store the collected jobs */ 6355 int* nelements, /**< pointer to store the number of elements in omega set */ 6356 int* est, /**< pointer to store the earliest start time of the omega set */ 6357 int* lct, /**< pointer to store the latest start time of the omega set */ 6358 int* energy /**< pointer to store the energy of the omega set */ 6359 ) 6360 { 6361 SCIP_BTNODE* left; 6362 SCIP_BTNODE* right; 6363 SCIP_NODEDATA* nodedata; 6364 SCIP_NODEDATA* leftdata; 6365 SCIP_NODEDATA* rightdata; 6366 6367 assert(node != NULL); 6368 6369 nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(node); 6370 assert(nodedata != NULL); 6371 6372 /* check if the node is a leaf */ 6373 if( SCIPbtnodeIsLeaf(node) ) 6374 { 6375 assert(!nodedata->intheta); 6376 return; 6377 } 6378 6379 left = SCIPbtnodeGetLeftchild(node); 6380 assert(left != NULL); 6381 6382 leftdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(left); 6383 assert(leftdata != NULL); 6384 6385 right = SCIPbtnodeGetRightchild(node); 6386 assert(right != NULL); 6387 6388 rightdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(right); 6389 assert(rightdata != NULL); 6390 6391 assert(nodedata->enveloplambda != -1); 6392 assert(rightdata->energytheta != -1); 6393 6394 if( leftdata->enveloplambda >= 0 && nodedata->enveloplambda == leftdata->enveloplambda + rightdata->energytheta ) 6395 { 6396 traceLambdaEnvelop(left, omegaset, nelements, est, lct, energy); 6397 collectThetaSubtree(right, omegaset, nelements, est, lct, energy); 6398 } 6399 else 6400 { 6401 if( leftdata->enveloptheta >= 0 && rightdata->energylambda >= 0 6402 && nodedata->enveloplambda == leftdata->enveloptheta + rightdata->energylambda ) 6403 { 6404 traceThetaEnvelop(left, omegaset, nelements, est, lct, energy); 6405 traceLambdaEnergy(right, omegaset, nelements, est, lct, energy); 6406 } 6407 else 6408 { 6409 assert(rightdata->enveloplambda != -1); 6410 assert(nodedata->enveloplambda == rightdata->enveloplambda); 6411 traceLambdaEnvelop(right, omegaset, nelements, est, lct, energy); 6412 } 6413 } 6414 } 6415 6416 /** compute the energy contribution by job which corresponds to the given leaf */ 6417 static 6418 int computeEnergyContribution( 6419 SCIP_BTNODE* node /**< leaf */ 6420 ) 6421 { 6422 SCIP_NODEDATA* nodedata; 6423 int duration; 6424 6425 nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(node); 6426 assert(nodedata != NULL); 6427 assert(nodedata->var != NULL); 6428 6429 duration = nodedata->duration - nodedata->leftadjust - nodedata->rightadjust; 6430 assert(duration > 0); 6431 6432 SCIPdebugMessage("variable <%s>: loc=[%g,%g] glb=[%g,%g] (duration %d, demand %d)\n", 6433 SCIPvarGetName(nodedata->var), SCIPvarGetLbLocal(nodedata->var), SCIPvarGetUbLocal(nodedata->var), 6434 SCIPvarGetLbGlobal(nodedata->var), SCIPvarGetUbGlobal(nodedata->var), duration, nodedata->demand); 6435 6436 /* return energy which is contributed by the start time variable */ 6437 return nodedata->demand * duration; 6438 } 6439 6440 /** comparison method for two node data w.r.t. the earliest start time */ 6441 static 6442 SCIP_DECL_SORTPTRCOMP(compNodeEst) 6443 { 6444 int est1; 6445 int est2; 6446 6447 est1 = ((SCIP_NODEDATA*)SCIPbtnodeGetData((SCIP_BTNODE*)elem1))->est; 6448 est2 = ((SCIP_NODEDATA*)SCIPbtnodeGetData((SCIP_BTNODE*)elem2))->est; 6449 6450 return (est1 - est2); 6451 } 6452 6453 /** comparison method for two node data w.r.t. the latest completion time */ 6454 static 6455 SCIP_DECL_SORTINDCOMP(compNodedataLct) 6456 { 6457 SCIP_NODEDATA* nodedatas; 6458 6459 nodedatas = (SCIP_NODEDATA*) dataptr; 6460 return (nodedatas[ind1].lct - nodedatas[ind2].lct); 6461 } 6462 6463 6464 /** an overload was detected; initialized conflict analysis, add an initial reason 6465 * 6466 * @note the conflict analysis is not performend, only the initialized SCIP_Bool pointer is set to TRUE 6467 */ 6468 static 6469 SCIP_RETCODE analyzeConflictOverload( 6470 SCIP* scip, /**< SCIP data structure */ 6471 SCIP_BTNODE** leaves, /**< responsible leaves for the overload */ 6472 int capacity, /**< cumulative capacity */ 6473 int nleaves, /**< number of responsible leaves */ 6474 int est, /**< earliest start time of the ...... */ 6475 int lct, /**< latest completly time of the .... */ 6476 int reportedenergy, /**< energy which already reported */ 6477 SCIP_Bool propest, /**< should the earliest start times be propagated, otherwise the latest completion times */ 6478 int shift, /**< shift applied to all jobs before adding them to the tree */ 6479 SCIP_Bool usebdwidening, /**< should bound widening be used during conflict analysis? */ 6480 SCIP_Bool* initialized, /**< was conflict analysis initialized */ 6481 SCIP_Bool* explanation /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */ 6482 ) 6483 { 6484 SCIP_Longint energy; 6485 int j; 6486 6487 /* do nothing if conflict analysis is not applicable */ 6488 if( !SCIPisConflictAnalysisApplicable(scip) ) 6489 return SCIP_OKAY; 6490 6491 SCIPdebugMsg(scip, "est=%d, lct=%d, propest %u, reportedenergy %d, shift %d\n", est, lct, propest, reportedenergy, shift); 6492 6493 /* compute energy of initial time window */ 6494 energy = ((SCIP_Longint) lct - est) * capacity; 6495 6496 /* sort the start time variables which were added to search tree w.r.t. earliest start time */ 6497 SCIPsortDownPtr((void**)leaves, compNodeEst, nleaves); 6498 6499 /* collect the energy of the responsible leaves until the cumulative energy is large enough to detect an overload; 6500 * thereby, compute the time window of interest 6501 */ 6502 for( j = 0; j < nleaves && reportedenergy <= energy; ++j ) 6503 { 6504 SCIP_NODEDATA* nodedata; 6505 6506 nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(leaves[j]); 6507 assert(nodedata != NULL); 6508 6509 reportedenergy += computeEnergyContribution(leaves[j]); 6510 6511 /* adjust energy if the earliest start time decrease */ 6512 if( nodedata->est < est ) 6513 { 6514 est = nodedata->est; 6515 energy = ((SCIP_Longint) lct - est) * capacity; 6516 } 6517 } 6518 assert(reportedenergy > energy); 6519 6520 SCIPdebugMsg(scip, "time window [%d,%d) available energy %" SCIP_LONGINT_FORMAT ", required energy %d\n", est, lct, energy, reportedenergy); 6521 6522 /* initialize conflict analysis */ 6523 SCIP_CALL( SCIPinitConflictAnalysis(scip, SCIP_CONFTYPE_PROPAGATION, FALSE) ); 6524 6525 /* flip earliest start time and latest completion time */ 6526 if( !propest ) 6527 { 6528 SCIPswapInts(&est, &lct); 6529 6530 /* shift earliest start time and latest completion time */ 6531 lct = shift - lct; 6532 est = shift - est; 6533 } 6534 else 6535 { 6536 /* shift earliest start time and latest completion time */ 6537 lct = lct + shift; 6538 est = est + shift; 6539 } 6540 6541 nleaves = j; 6542 6543 /* report the variables and relax their bounds to final time interval [est,lct) which was been detected to be 6544 * overloaded 6545 */ 6546 for( j = nleaves-1; j >= 0; --j ) 6547 { 6548 SCIP_NODEDATA* nodedata; 6549 6550 nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(leaves[j]); 6551 assert(nodedata != NULL); 6552 assert(nodedata->var != NULL); 6553 6554 /* check if bound widening should be used */ 6555 if( usebdwidening ) 6556 { 6557 SCIP_CALL( SCIPaddConflictRelaxedUb(scip, nodedata->var, NULL, (SCIP_Real)(est - nodedata->leftadjust)) ); 6558 SCIP_CALL( SCIPaddConflictRelaxedLb(scip, nodedata->var, NULL, (SCIP_Real)(lct - nodedata->duration + nodedata->rightadjust)) ); 6559 } 6560 else 6561 { 6562 SCIP_CALL( SCIPaddConflictLb(scip, nodedata->var, NULL) ); 6563 SCIP_CALL( SCIPaddConflictUb(scip, nodedata->var, NULL) ); 6564 } 6565 6566 if( explanation != NULL ) 6567 explanation[nodedata->idx] = TRUE; 6568 } 6569 6570 (*initialized) = TRUE; 6571 6572 return SCIP_OKAY; 6573 } 6574 6575 /** computes a new latest starting time of the job in 'respleaf' due to the energy consumption and stores the 6576 * responsible interval bounds in *est_omega and *lct_omega 6577 */ 6578 static 6579 int computeEstOmegaset( 6580 SCIP* scip, /**< SCIP data structure */ 6581 int duration, /**< duration of the job to move */ 6582 int demand, /**< demand of the job to move */ 6583 int capacity, /**< cumulative capacity */ 6584 int est, /**< earliest start time of the omega set */ 6585 int lct, /**< latest start time of the omega set */ 6586 int energy /**< energy of the omega set */ 6587 ) 6588 { 6589 int newest; 6590 6591 newest = 0; 6592 6593 assert(scip != NULL); 6594 6595 if( energy > ((SCIP_Longint) capacity - demand) * ((SCIP_Longint) lct - est) ) 6596 { 6597 if( energy + (SCIP_Longint) demand * duration > capacity * ((SCIP_Longint) lct - est) ) 6598 { 6599 newest = (int)SCIPfeasCeil(scip, (energy - (SCIP_Real)(capacity - demand) * (lct - est)) / (SCIP_Real)demand); 6600 newest += est; 6601 } 6602 } 6603 6604 return newest; 6605 } 6606 6607 /** propagates start time using an edge finding algorithm which is based on binary trees (theta lambda trees) 6608 * 6609 * @note The algorithm is based on the paper: Petr Vilim, "Edge Finding Filtering Algorithm for Discrete Cumulative 6610 * Resources in O(kn log n)". *I.P. Gent (Ed.): CP 2009, LNCS 5732, pp. 802-816, 2009. 6611 */ 6612 static 6613 SCIP_RETCODE inferboundsEdgeFinding( 6614 SCIP* scip, /**< SCIP data structure */ 6615 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */ 6616 SCIP_CONS* cons, /**< constraint which is propagated */ 6617 SCIP_BT* tree, /**< binary tree constaining the theta and lambda sets */ 6618 SCIP_BTNODE** leaves, /**< array of all leaves for each job one */ 6619 int capacity, /**< cumulative capacity */ 6620 int ncands, /**< number of candidates */ 6621 SCIP_Bool propest, /**< should the earliest start times be propagated, otherwise the latest completion times */ 6622 int shift, /**< shift applied to all jobs before adding them to the tree */ 6623 SCIP_Bool* initialized, /**< was conflict analysis initialized */ 6624 SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */ 6625 int* nchgbds, /**< pointer to store the number of bound changes */ 6626 SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */ 6627 ) 6628 { 6629 SCIP_NODEDATA* rootdata; 6630 int j; 6631 6632 assert(!SCIPbtIsEmpty(tree)); 6633 6634 rootdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(SCIPbtGetRoot(tree)); 6635 assert(rootdata != NULL); 6636 6637 /* iterate over all added candidate (leaves) in non-increasing order w.r.t. their latest completion time */ 6638 for( j = ncands-1; j >= 0 && !(*cutoff); --j ) 6639 { 6640 SCIP_NODEDATA* nodedata; 6641 6642 if( SCIPbtnodeIsRoot(leaves[j]) ) 6643 break; 6644 6645 nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(leaves[j]); 6646 assert(nodedata->est != -1); 6647 6648 /* check if the root lambda envelop exeeds the available capacity */ 6649 while( !(*cutoff) && rootdata->enveloplambda > (SCIP_Longint) capacity * nodedata->lct ) 6650 { 6651 SCIP_BTNODE** omegaset; 6652 SCIP_BTNODE* leaf; 6653 SCIP_NODEDATA* leafdata; 6654 int nelements; 6655 int energy; 6656 int newest; 6657 int est; 6658 int lct; 6659 6660 assert(!(*cutoff)); 6661 6662 /* find responsible leaf for the lambda envelope */ 6663 leaf = findResponsibleLambdaLeafTraceEnvelop(SCIPbtGetRoot(tree)); 6664 assert(leaf != NULL); 6665 assert(SCIPbtnodeIsLeaf(leaf)); 6666 6667 leafdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(leaf); 6668 assert(leafdata != NULL); 6669 assert(!leafdata->intheta); 6670 assert(leafdata->duration > 0); 6671 assert(leafdata->est >= 0); 6672 6673 /* check if the job has to be removed since its latest completion is to large */ 6674 if( leafdata->est + leafdata->duration >= nodedata->lct ) 6675 { 6676 SCIP_CALL( deleteLambdaLeaf(scip, tree, leaf) ); 6677 6678 /* the root might changed therefore we need to collect the new root node data */ 6679 rootdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(SCIPbtGetRoot(tree)); 6680 assert(rootdata != NULL); 6681 6682 continue; 6683 } 6684 6685 /* compute omega set */ 6686 SCIP_CALL( SCIPallocBufferArray(scip, &omegaset, ncands) ); 6687 6688 nelements = 0; 6689 est = INT_MAX; 6690 lct = INT_MIN; 6691 energy = 0; 6692 6693 /* collect the omega set from theta set */ 6694 traceLambdaEnvelop(SCIPbtGetRoot(tree), omegaset, &nelements, &est, &lct, &energy); 6695 assert(nelements > 0); 6696 assert(nelements < ncands); 6697 6698 newest = computeEstOmegaset(scip, leafdata->duration, leafdata->demand, capacity, est, lct, energy); 6699 6700 /* if the computed earliest start time is greater than the latest completion time of the omega set we detected an overload */ 6701 if( newest > lct ) 6702 { 6703 SCIPdebugMsg(scip, "an overload was detected duration edge-finder propagattion\n"); 6704 6705 /* analyze over load */ 6706 SCIP_CALL( analyzeConflictOverload(scip, omegaset, capacity, nelements, est, lct, 0, propest, shift, 6707 conshdlrdata->usebdwidening, initialized, explanation) ); 6708 (*cutoff) = TRUE; 6709 6710 /* for the statistic we count the number of times a cutoff was detected due the edge-finder */ 6711 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->ncutoffedgefinder++ ); 6712 } 6713 else if( newest > 0 ) 6714 { 6715 SCIP_Bool infeasible; 6716 SCIP_Bool tightened; 6717 INFERINFO inferinfo; 6718 6719 if( propest ) 6720 { 6721 /* constuct inference information; store used propagation rule and the the time window of the omega set */ 6722 inferinfo = getInferInfo(PROPRULE_2_EDGEFINDING, est + shift, lct + shift); 6723 6724 SCIPdebugMsg(scip, "variable <%s> adjust lower bound from %g to %d\n", 6725 SCIPvarGetName(leafdata->var), SCIPvarGetLbLocal(leafdata->var), newest + shift); 6726 6727 if( inferInfoIsValid(inferinfo) ) 6728 { 6729 SCIP_CALL( SCIPinferVarLbCons(scip, leafdata->var, (SCIP_Real)(newest + shift), 6730 cons, inferInfoToInt(inferinfo), TRUE, &infeasible, &tightened) ); 6731 } 6732 else 6733 { 6734 SCIP_CALL( SCIPtightenVarLb(scip, leafdata->var, (SCIP_Real)(newest + shift), 6735 TRUE, &infeasible, &tightened) ); 6736 } 6737 6738 /* for the statistic we count the number of times a lower bound was tightened due the edge-finder */ 6739 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->nlbedgefinder++ ); 6740 } 6741 else 6742 { 6743 /* constuct inference information; store used propagation rule and the the time window of the omega set */ 6744 inferinfo = getInferInfo(PROPRULE_2_EDGEFINDING, shift - lct, shift - est); 6745 6746 SCIPdebugMsg(scip, "variable <%s> adjust upper bound from %g to %d\n", 6747 SCIPvarGetName(leafdata->var), SCIPvarGetUbLocal(leafdata->var), shift - newest - leafdata->duration); 6748 6749 if( inferInfoIsValid(inferinfo) ) 6750 { 6751 SCIP_CALL( SCIPinferVarUbCons(scip, leafdata->var, (SCIP_Real)(shift - newest - leafdata->duration), 6752 cons, inferInfoToInt(inferinfo), TRUE, &infeasible, &tightened) ); 6753 } 6754 else 6755 { 6756 SCIP_CALL( SCIPtightenVarUb(scip, leafdata->var, (SCIP_Real)(shift - newest - leafdata->duration), 6757 TRUE, &infeasible, &tightened) ); 6758 } 6759 6760 /* for the statistic we count the number of times a upper bound was tightened due the edge-finder */ 6761 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->nubedgefinder++ ); 6762 } 6763 6764 /* adjust the earliest start time */ 6765 if( tightened ) 6766 { 6767 leafdata->est = newest; 6768 (*nchgbds)++; 6769 } 6770 6771 if( infeasible ) 6772 { 6773 /* initialize conflict analysis if conflict analysis is applicable */ 6774 if( SCIPisConflictAnalysisApplicable(scip) ) 6775 { 6776 int i; 6777 6778 SCIPdebugMsg(scip, "edge-finder dectected an infeasibility\n"); 6779 6780 SCIP_CALL( SCIPinitConflictAnalysis(scip, SCIP_CONFTYPE_PROPAGATION, FALSE) ); 6781 6782 /* add lower and upper bound of variable which leads to the infeasibilty */ 6783 SCIP_CALL( SCIPaddConflictLb(scip, leafdata->var, NULL) ); 6784 SCIP_CALL( SCIPaddConflictUb(scip, leafdata->var, NULL) ); 6785 6786 if( explanation != NULL ) 6787 explanation[leafdata->idx] = TRUE; 6788 6789 /* add lower and upper bound of variable which lead to the infeasibilty */ 6790 for( i = 0; i < nelements; ++i ) 6791 { 6792 nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(omegaset[i]); 6793 assert(nodedata != NULL); 6794 6795 SCIP_CALL( SCIPaddConflictLb(scip, nodedata->var, NULL) ); 6796 SCIP_CALL( SCIPaddConflictUb(scip, nodedata->var, NULL) ); 6797 6798 if( explanation != NULL ) 6799 explanation[nodedata->idx] = TRUE; 6800 } 6801 6802 (*initialized) = TRUE; 6803 } 6804 6805 (*cutoff) = TRUE; 6806 6807 /* for the statistic we count the number of times a cutoff was detected due the edge-finder */ 6808 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->ncutoffedgefinder++ ); 6809 } 6810 } 6811 6812 /* free omegaset array */ 6813 SCIPfreeBufferArray(scip, &omegaset); 6814 6815 /* delete responsible leaf from lambda */ 6816 SCIP_CALL( deleteLambdaLeaf(scip, tree, leaf) ); 6817 6818 /* the root might changed therefore we need to collect the new root node data */ 6819 rootdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(SCIPbtGetRoot(tree)); 6820 assert(rootdata != NULL); 6821 } 6822 6823 /* move current job j from the theta set into the lambda set */ 6824 SCIP_CALL( moveNodeToLambda(scip, tree, leaves[j]) ); 6825 } 6826 6827 return SCIP_OKAY; 6828 } 6829 6830 /** checks whether the instance is infeasible due to a overload within a certain time frame using the idea of theta trees 6831 * 6832 * @note The algorithm is based on the paper: Petr Vilim, "Max Energy Filtering Algorithm for Discrete Cumulative 6833 * Resources". In: Willem Jan van Hoeve and John N. Hooker (Eds.), Integration of AI and OR Techniques in 6834 * Constraint Programming for Combinatorial Optimization Problems (CPAIOR 2009), LNCS 5547, pp 294--308 6835 */ 6836 static 6837 SCIP_RETCODE checkOverloadViaThetaTree( 6838 SCIP* scip, /**< SCIP data structure */ 6839 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */ 6840 int nvars, /**< number of start time variables (activities) */ 6841 SCIP_VAR** vars, /**< array of start time variables */ 6842 int* durations, /**< array of durations */ 6843 int* demands, /**< array of demands */ 6844 int capacity, /**< cumulative capacity */ 6845 int hmin, /**< left bound of time axis to be considered (including hmin) */ 6846 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 6847 SCIP_CONS* cons, /**< constraint which is propagated */ 6848 SCIP_Bool propest, /**< should the earliest start times be propagated, otherwise the latest completion times */ 6849 SCIP_Bool* initialized, /**< was conflict analysis initialized */ 6850 SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */ 6851 int* nchgbds, /**< pointer to store the number of bound changes */ 6852 SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */ 6853 ) 6854 { 6855 SCIP_NODEDATA* nodedatas; 6856 SCIP_BTNODE** leaves; 6857 SCIP_BT* tree; 6858 int* nodedataidx; 6859 6860 int totalenergy; 6861 int nnodedatas; 6862 int ninsertcands; 6863 int ncands; 6864 6865 int shift; 6866 int idx = -1; 6867 int j; 6868 6869 assert(scip != NULL); 6870 assert(cons != NULL); 6871 assert(initialized != NULL); 6872 assert(cutoff != NULL); 6873 assert(*cutoff == FALSE); 6874 6875 SCIPdebugMsg(scip, "check overload of cumulative condition of constraint <%s> (capacity %d)\n", SCIPconsGetName(cons), capacity); 6876 6877 SCIP_CALL( SCIPallocBufferArray(scip, &nodedatas, 2*nvars) ); 6878 SCIP_CALL( SCIPallocBufferArray(scip, &nodedataidx, 2*nvars) ); 6879 SCIP_CALL( SCIPallocBufferArray(scip, &leaves, nvars) ); 6880 6881 ncands = 0; 6882 totalenergy = 0; 6883 6884 SCIP_CALL( SCIPbtCreate(&tree, SCIPblkmem(scip)) ); 6885 6886 /* compute the shift which we apply to compute .... latest completion time of all jobs */ 6887 if( propest ) 6888 shift = 0; 6889 else 6890 { 6891 shift = 0; 6892 6893 /* compute the latest completion time of all jobs which define the shift we apply to run the algorithm for the 6894 * earliest start time propagation to handle the latest completion times 6895 */ 6896 for( j = 0; j < nvars; ++j ) 6897 { 6898 int lct; 6899 6900 lct = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(vars[j])) + durations[j]; 6901 shift = MAX(shift, lct); 6902 } 6903 } 6904 6905 /* collect earliest and latest completion times and ignore jobs which do not run completion within the effective 6906 * horizon 6907 */ 6908 for( j = 0; j < nvars; ++j ) 6909 { 6910 SCIP_NODEDATA* nodedata; 6911 SCIP_VAR* var; 6912 int duration; 6913 int leftadjust; 6914 int rightadjust; 6915 int energy; 6916 int est; 6917 int lct; 6918 6919 var = vars[j]; 6920 assert(var != NULL); 6921 6922 duration = durations[j]; 6923 assert(duration > 0); 6924 6925 leftadjust = 0; 6926 rightadjust = 0; 6927 6928 est = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(var)); 6929 lct = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(var)) + duration; 6930 6931 /* adjust the duration, earliest start time, and latest completion time of jobs which do not lie completely in the 6932 * effective horizon [hmin,hmax) 6933 */ 6934 if( conshdlrdata->useadjustedjobs ) 6935 { 6936 if( est < hmin ) 6937 { 6938 leftadjust = (hmin - est); 6939 est = hmin; 6940 } 6941 if( lct > hmax ) 6942 { 6943 rightadjust = (lct - hmax); 6944 lct = hmax; 6945 } 6946 6947 /* only consider jobs which have a (adjusted) duration greater than zero (the amound which will run defenetly 6948 * with the effective time horizon 6949 */ 6950 if( duration - leftadjust - rightadjust <= 0 ) 6951 continue; 6952 } 6953 else if( est < hmin || lct > hmax ) 6954 continue; 6955 6956 energy = demands[j] * (duration - leftadjust - rightadjust); 6957 assert(energy > 0); 6958 6959 totalenergy += energy; 6960 6961 /* flip earliest start time and latest completion time */ 6962 if( !propest ) 6963 { 6964 SCIPswapInts(&est, &lct); 6965 6966 /* shift earliest start time and latest completion time */ 6967 lct = shift - lct; 6968 est = shift - est; 6969 } 6970 else 6971 { 6972 /* shift earliest start time and latest completion time */ 6973 lct = lct - shift; 6974 est = est - shift; 6975 } 6976 assert(est < lct); 6977 assert(est >= 0); 6978 assert(lct >= 0); 6979 6980 /* create search node data */ 6981 nodedata = &nodedatas[ncands]; 6982 nodedataidx[ncands] = ncands; 6983 ++ncands; 6984 6985 /* initialize search node data */ 6986 /* adjust earliest start time to make it unique in case several jobs have the same earliest start time */ 6987 nodedata->key = est + j / (2.0 * nvars); 6988 nodedata->var = var; 6989 nodedata->est = est; 6990 nodedata->lct = lct; 6991 nodedata->demand = demands[j]; 6992 nodedata->duration = duration; 6993 nodedata->leftadjust = leftadjust; 6994 nodedata->rightadjust = rightadjust; 6995 6996 /* the envelop is the energy of the job plus the total amount of energy which is available in the time period 6997 * before that job can start, that is [0,est). The envelop is later used to compare the energy consumption of a 6998 * particular time interval [a,b] against the time interval [0,b]. 6999 */ 7000 nodedata->enveloptheta = (SCIP_Longint) capacity * est + energy; 7001 nodedata->energytheta = energy; 7002 nodedata->enveloplambda = -1; 7003 nodedata->energylambda = -1; 7004 7005 nodedata->idx = j; 7006 nodedata->intheta = TRUE; 7007 } 7008 7009 nnodedatas = ncands; 7010 7011 /* sort (non-decreasing) the jobs w.r.t. latest completion times */ 7012 SCIPsortInd(nodedataidx, compNodedataLct, (void*)nodedatas, ncands); 7013 7014 ninsertcands = 0; 7015 7016 /* iterate over all jobs in non-decreasing order of their latest completion times and add them to the theta set until 7017 * the root envelop detects an overload 7018 */ 7019 for( j = 0; j < ncands; ++j ) 7020 { 7021 SCIP_BTNODE* leaf; 7022 SCIP_NODEDATA* rootdata; 7023 7024 idx = nodedataidx[j]; 7025 7026 /* check if the new job opens a time window which size is so large that it offers more energy than the total 7027 * energy of all candidate jobs. If so we skip that one. 7028 */ 7029 if( ((SCIP_Longint) nodedatas[idx].lct - nodedatas[idx].est) * capacity >= totalenergy ) 7030 { 7031 /* set the earliest start time to minus one to mark that candidate to be not used */ 7032 nodedatas[idx].est = -1; 7033 continue; 7034 } 7035 7036 /* create search node */ 7037 SCIP_CALL( SCIPbtnodeCreate(tree, &leaf, (void*)&nodedatas[idx]) ); 7038 7039 /* insert new node into the theta set and updete the envelops */ 7040 SCIP_CALL( insertThetanode(scip, tree, leaf, nodedatas, nodedataidx, &nnodedatas) ); 7041 assert(nnodedatas <= 2*nvars); 7042 7043 /* move the inserted candidates together */ 7044 leaves[ninsertcands] = leaf; 7045 ninsertcands++; 7046 7047 assert(!SCIPbtIsEmpty(tree)); 7048 rootdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(SCIPbtGetRoot(tree)); 7049 assert(rootdata != NULL); 7050 7051 /* check if the theta set envelops exceeds the available capacity */ 7052 if( rootdata->enveloptheta > (SCIP_Longint) capacity * nodedatas[idx].lct ) 7053 { 7054 SCIPdebugMsg(scip, "detects cutoff due to overload in time window [?,%d) (ncands %d)\n", nodedatas[idx].lct, j); 7055 (*cutoff) = TRUE; 7056 7057 /* for the statistic we count the number of times a cutoff was detected due the edge-finder */ 7058 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->ncutoffoverload++ ); 7059 7060 break; 7061 } 7062 } 7063 7064 /* in case an overload was detected and the conflict analysis is applicable, create an initialize explanation */ 7065 if( *cutoff ) 7066 { 7067 int glbenery; 7068 int est; 7069 int lct; 7070 7071 glbenery = 0; 7072 assert( 0 <= idx ); 7073 est = nodedatas[idx].est; 7074 lct = nodedatas[idx].lct; 7075 7076 /* scan the remaining candidates for a global contributions within the time window of the last inserted candidate 7077 * which led to an overload 7078 */ 7079 for( j = j+1; j < ncands; ++j ) 7080 { 7081 SCIP_NODEDATA* nodedata; 7082 int duration; 7083 int glbest; 7084 int glblct; 7085 7086 idx = nodedataidx[j]; 7087 nodedata = &nodedatas[idx]; 7088 assert(nodedata != NULL); 7089 7090 duration = nodedata->duration - nodedata->leftadjust - nodedata->rightadjust; 7091 7092 /* get latest start time */ 7093 glbest = SCIPconvertRealToInt(scip, SCIPvarGetLbGlobal(nodedata->var)); 7094 glblct = SCIPconvertRealToInt(scip, SCIPvarGetUbGlobal(nodedata->var)) + duration; 7095 7096 /* check if parts of the jobs run with the time window defined by the last inserted job */ 7097 if( glbest < est ) 7098 duration -= (est - glbest); 7099 7100 if( glblct > lct ) 7101 duration -= (glblct - lct); 7102 7103 if( duration > 0 ) 7104 { 7105 glbenery += nodedata->demand * duration; 7106 7107 if( explanation != NULL ) 7108 explanation[nodedata->idx] = TRUE; 7109 } 7110 } 7111 7112 /* analyze the overload */ 7113 SCIP_CALL( analyzeConflictOverload(scip, leaves, capacity, ninsertcands, est, lct, glbenery, propest, shift, 7114 conshdlrdata->usebdwidening, initialized, explanation) ); 7115 } 7116 else if( ninsertcands > 1 && conshdlrdata->efinfer ) 7117 { 7118 /* if we have more than one job insterted and edge-finding should be performed we do it */ 7119 SCIP_CALL( inferboundsEdgeFinding(scip, conshdlrdata, cons, tree, leaves, capacity, ninsertcands, 7120 propest, shift, initialized, explanation, nchgbds, cutoff) ); 7121 } 7122 7123 /* free theta tree */ 7124 SCIPbtFree(&tree); 7125 7126 /* free buffer arrays */ 7127 SCIPfreeBufferArray(scip, &leaves); 7128 SCIPfreeBufferArray(scip, &nodedataidx); 7129 SCIPfreeBufferArray(scip, &nodedatas); 7130 7131 return SCIP_OKAY; 7132 } 7133 7134 /** checks whether the instance is infeasible due to a overload within a certain time frame using the idea of theta trees 7135 * 7136 * @note The algorithm is based on the paper: Petr Vilim, "Max Energy Filtering Algorithm for Discrete Cumulative 7137 * Resources". In: Willem Jan van Hoeve and John N. Hooker (Eds.), Integration of AI and OR Techniques in 7138 * Constraint Programming for Combinatorial Optimization Problems (CPAIOR 2009), LNCS 5547, pp 294--308 7139 */ 7140 static 7141 SCIP_RETCODE propagateEdgeFinding( 7142 SCIP* scip, /**< SCIP data structure */ 7143 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */ 7144 int nvars, /**< number of start time variables (activities) */ 7145 SCIP_VAR** vars, /**< array of start time variables */ 7146 int* durations, /**< array of durations */ 7147 int* demands, /**< array of demands */ 7148 int capacity, /**< cumulative capacity */ 7149 int hmin, /**< left bound of time axis to be considered (including hmin) */ 7150 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 7151 SCIP_CONS* cons, /**< constraint which is propagated */ 7152 SCIP_Bool* initialized, /**< was conflict analysis initialized */ 7153 SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */ 7154 int* nchgbds, /**< pointer to store the number of bound changes */ 7155 SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */ 7156 ) 7157 { 7158 /* check if a cutoff was already detected */ 7159 if( (*cutoff) ) 7160 return SCIP_OKAY; 7161 7162 /* check if at least the basic overload checking should be preformed */ 7163 if( !conshdlrdata->efcheck ) 7164 return SCIP_OKAY; 7165 7166 /* check for overload, which may result in a cutoff */ 7167 SCIP_CALL( checkOverloadViaThetaTree(scip, conshdlrdata, nvars, vars, durations, demands, capacity, hmin, hmax, 7168 cons, TRUE, initialized, explanation, nchgbds, cutoff) ); 7169 7170 /* check if a cutoff was detected */ 7171 if( (*cutoff) ) 7172 return SCIP_OKAY; 7173 7174 /* check if bound should be infer */ 7175 if( !conshdlrdata->efinfer ) 7176 return SCIP_OKAY; 7177 7178 /* check for overload, which may result in a cutoff */ 7179 SCIP_CALL( checkOverloadViaThetaTree(scip, conshdlrdata, nvars, vars, durations, demands, capacity, hmin, hmax, 7180 cons, FALSE, initialized, explanation, nchgbds, cutoff) ); 7181 7182 return SCIP_OKAY; 7183 } 7184 7185 /** checks if the constraint is redundant; that is the case if its capacity can never be exceeded; therefore we check 7186 * with respect to the lower and upper bounds of the integer start time variables the maximum capacity usage for all 7187 * event points 7188 */ 7189 static 7190 SCIP_RETCODE consCheckRedundancy( 7191 SCIP* scip, /**< SCIP data structure */ 7192 int nvars, /**< number of start time variables (activities) */ 7193 SCIP_VAR** vars, /**< array of start time variables */ 7194 int* durations, /**< array of durations */ 7195 int* demands, /**< array of demands */ 7196 int capacity, /**< cumulative capacity */ 7197 int hmin, /**< left bound of time axis to be considered (including hmin) */ 7198 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 7199 SCIP_Bool* redundant /**< pointer to store whether this constraint is redundant */ 7200 ) 7201 { 7202 SCIP_VAR* var; 7203 int* starttimes; /* stores when each job is starting */ 7204 int* endtimes; /* stores when each job ends */ 7205 int* startindices; /* we will sort the startsolvalues, thus we need to know wich index of a job it corresponds to */ 7206 int* endindices; /* we will sort the endsolvalues, thus we need to know wich index of a job it corresponds to */ 7207 7208 int lb; 7209 int ub; 7210 int freecapacity; /* remaining capacity */ 7211 int curtime; /* point in time which we are just checking */ 7212 int endindex; /* index of endsolvalues with: endsolvalues[endindex] > curtime */ 7213 int njobs; 7214 int j; 7215 7216 assert(scip != NULL); 7217 assert(redundant != NULL); 7218 7219 (*redundant) = TRUE; 7220 7221 /* if no activities are associated with this cumulative then this constraint is redundant */ 7222 if( nvars == 0 ) 7223 return SCIP_OKAY; 7224 7225 assert(vars != NULL); 7226 7227 SCIP_CALL( SCIPallocBufferArray(scip, &starttimes, nvars) ); 7228 SCIP_CALL( SCIPallocBufferArray(scip, &endtimes, nvars) ); 7229 SCIP_CALL( SCIPallocBufferArray(scip, &startindices, nvars) ); 7230 SCIP_CALL( SCIPallocBufferArray(scip, &endindices, nvars) ); 7231 7232 njobs = 0; 7233 7234 /* assign variables, start and endpoints to arrays */ 7235 for( j = 0; j < nvars; ++j ) 7236 { 7237 assert(durations[j] > 0); 7238 assert(demands[j] > 0); 7239 7240 var = vars[j]; 7241 assert(var != NULL); 7242 7243 lb = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(var)); 7244 ub = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(var)); 7245 7246 /* check if jobs runs completely outside of the effective time horizon */ 7247 if( lb >= hmax || ub + durations[j] <= hmin ) 7248 continue; 7249 7250 starttimes[njobs] = MAX(lb, hmin); 7251 startindices[njobs] = j; 7252 7253 endtimes[njobs] = MIN(ub + durations[j], hmax); 7254 endindices[njobs] = j; 7255 assert(starttimes[njobs] <= endtimes[njobs]); 7256 njobs++; 7257 } 7258 7259 /* sort the arrays not-decreasing according to startsolvalues and endsolvalues (and sort the indices in the same way) */ 7260 SCIPsortIntInt(starttimes, startindices, njobs); 7261 SCIPsortIntInt(endtimes, endindices, njobs); 7262 7263 endindex = 0; 7264 freecapacity = capacity; 7265 7266 /* check each start point of a job whether the capacity is violated or not */ 7267 for( j = 0; j < njobs; ++j ) 7268 { 7269 curtime = starttimes[j]; 7270 7271 /* stop checking, if time point is above hmax */ 7272 if( curtime >= hmax ) 7273 break; 7274 7275 /* subtract all capacity needed up to this point */ 7276 freecapacity -= demands[startindices[j]]; 7277 while( j+1 < njobs && starttimes[j+1] == curtime ) 7278 { 7279 ++j; 7280 freecapacity -= demands[startindices[j]]; 7281 } 7282 7283 /* free all capacity usages of jobs the are no longer running */ 7284 while( endtimes[endindex] <= curtime ) 7285 { 7286 freecapacity += demands[endindices[endindex]]; 7287 ++endindex; 7288 } 7289 assert(freecapacity <= capacity); 7290 7291 /* check freecapacity to be smaller than zero */ 7292 if( freecapacity < 0 && curtime >= hmin ) 7293 { 7294 (*redundant) = FALSE; 7295 break; 7296 } 7297 } /*lint --e{850}*/ 7298 7299 /* free all buffer arrays */ 7300 SCIPfreeBufferArray(scip, &endindices); 7301 SCIPfreeBufferArray(scip, &startindices); 7302 SCIPfreeBufferArray(scip, &endtimes); 7303 SCIPfreeBufferArray(scip, &starttimes); 7304 7305 return SCIP_OKAY; 7306 } 7307 7308 /** creates the worst case resource profile, that is, all jobs are inserted with the earliest start and latest 7309 * completion time 7310 */ 7311 static 7312 SCIP_RETCODE createCoreProfile( 7313 SCIP* scip, /**< SCIP data structure */ 7314 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */ 7315 SCIP_PROFILE* profile, /**< resource profile */ 7316 int nvars, /**< number of variables (jobs) */ 7317 SCIP_VAR** vars, /**< array of integer variable which corresponds to starting times for a job */ 7318 int* durations, /**< array containing corresponding durations */ 7319 int* demands, /**< array containing corresponding demands */ 7320 int capacity, /**< cumulative capacity */ 7321 int hmin, /**< left bound of time axis to be considered (including hmin) */ 7322 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 7323 SCIP_Bool* initialized, /**< was conflict analysis initialized */ 7324 SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */ 7325 SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */ 7326 ) 7327 { 7328 int v; 7329 7330 /* insert all cores */ 7331 for( v = 0; v < nvars; ++v ) 7332 { 7333 SCIP_VAR* var; 7334 SCIP_Bool infeasible; 7335 int duration; 7336 int demand; 7337 int begin; 7338 int end; 7339 int est; 7340 int lst; 7341 int pos; 7342 7343 var = vars[v]; 7344 assert(var != NULL); 7345 assert(SCIPisFeasIntegral(scip, SCIPvarGetLbLocal(var))); 7346 assert(SCIPisFeasIntegral(scip, SCIPvarGetUbLocal(var))); 7347 7348 duration = durations[v]; 7349 assert(duration > 0); 7350 7351 demand = demands[v]; 7352 assert(demand > 0); 7353 7354 /* collect earliest and latest start time */ 7355 est = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(var)); 7356 lst = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(var)); 7357 7358 /* check if the job runs completely outside of the effective horizon [hmin, hmax); if so skip it */ 7359 if( lst + duration <= hmin || est >= hmax ) 7360 continue; 7361 7362 /* compute core interval w.r.t. effective time horizon */ 7363 begin = MAX(hmin, lst); 7364 end = MIN(hmax, est + duration); 7365 7366 /* check if a core exists */ 7367 if( begin >= end ) 7368 continue; 7369 7370 SCIPdebugMsg(scip, "variable <%s>[%d,%d] (duration %d, demand %d): add core [%d,%d)\n", 7371 SCIPvarGetName(var), est, lst, duration, demand, begin, end); 7372 7373 /* insert the core into core resource profile (complexity O(log n)) */ 7374 SCIP_CALL( SCIPprofileInsertCore(profile, begin, end, demand, &pos, &infeasible) ); 7375 7376 /* in case the insertion of the core leads to an infeasibility; start the conflict analysis */ 7377 if( infeasible ) 7378 { 7379 assert(begin <= SCIPprofileGetTime(profile, pos)); 7380 assert(end > SCIPprofileGetTime(profile, pos)); 7381 7382 /* use conflict analysis to analysis the core insertion which was infeasible */ 7383 SCIP_CALL( analyseInfeasibelCoreInsertion(scip, nvars, vars, durations, demands, capacity, hmin, hmax, 7384 var, duration, demand, SCIPprofileGetTime(profile, pos), conshdlrdata->usebdwidening, initialized, explanation) ); 7385 7386 if( explanation != NULL ) 7387 explanation[v] = TRUE; 7388 7389 (*cutoff) = TRUE; 7390 7391 /* for the statistic we count the number of times a cutoff was detected due the time-time */ 7392 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->ncutofftimetable++ ); 7393 7394 break; 7395 } 7396 } 7397 7398 return SCIP_OKAY; 7399 } 7400 7401 /** propagate the cumulative condition */ 7402 static 7403 SCIP_RETCODE propagateCumulativeCondition( 7404 SCIP* scip, /**< SCIP data structure */ 7405 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */ 7406 SCIP_PRESOLTIMING presoltiming, /**< current presolving timing */ 7407 int nvars, /**< number of start time variables (activities) */ 7408 SCIP_VAR** vars, /**< array of start time variables */ 7409 int* durations, /**< array of durations */ 7410 int* demands, /**< array of demands */ 7411 int capacity, /**< cumulative capacity */ 7412 int hmin, /**< left bound of time axis to be considered (including hmin) */ 7413 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 7414 SCIP_CONS* cons, /**< constraint which is propagated (needed to SCIPinferVar**Cons()) */ 7415 int* nchgbds, /**< pointer to store the number of bound changes */ 7416 SCIP_Bool* redundant, /**< pointer to store if the constraint is redundant */ 7417 SCIP_Bool* initialized, /**< was conflict analysis initialized */ 7418 SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */ 7419 SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */ 7420 ) 7421 { 7422 SCIP_PROFILE* profile; 7423 7424 SCIP_RETCODE retcode = SCIP_OKAY; 7425 7426 assert(nchgbds != NULL); 7427 assert(initialized != NULL); 7428 assert(cutoff != NULL); 7429 assert(!(*cutoff)); 7430 7431 /**@todo avoid always sorting the variable array */ 7432 7433 /* check if the constraint is redundant */ 7434 SCIP_CALL( consCheckRedundancy(scip, nvars, vars, durations, demands, capacity, hmin, hmax, redundant) ); 7435 7436 if( *redundant ) 7437 return SCIP_OKAY; 7438 7439 /* create an empty resource profile for profiling the cores of the jobs */ 7440 SCIP_CALL( SCIPprofileCreate(&profile, capacity) ); 7441 7442 /* create core profile (compulsory parts) */ 7443 SCIP_CALL_TERMINATE( retcode, createCoreProfile(scip, conshdlrdata, profile, nvars, vars, durations, demands, capacity, hmin, hmax, 7444 initialized, explanation, cutoff), TERMINATE ); 7445 7446 /* propagate the job cores until nothing else can be detected */ 7447 if( (presoltiming & SCIP_PRESOLTIMING_FAST) != 0 ) 7448 { 7449 SCIP_CALL_TERMINATE( retcode, propagateTimetable(scip, conshdlrdata, profile, nvars, vars, durations, demands, capacity, hmin, hmax, cons, 7450 nchgbds, initialized, explanation, cutoff), TERMINATE ); 7451 } 7452 7453 /* run edge finding propagator */ 7454 if( (presoltiming & SCIP_PRESOLTIMING_EXHAUSTIVE) != 0 ) 7455 { 7456 SCIP_CALL_TERMINATE( retcode, propagateEdgeFinding(scip, conshdlrdata, nvars, vars, durations, demands, capacity, hmin, hmax, 7457 cons, initialized, explanation, nchgbds, cutoff), TERMINATE ); 7458 } 7459 7460 /* run time-table edge-finding propagator */ 7461 if( (presoltiming & SCIP_PRESOLTIMING_MEDIUM) != 0 ) 7462 { 7463 SCIP_CALL_TERMINATE( retcode, propagateTTEF(scip, conshdlrdata, profile, nvars, vars, durations, demands, capacity, hmin, hmax, cons, 7464 nchgbds, initialized, explanation, cutoff), TERMINATE ); 7465 } 7466 /* free resource profile */ 7467 TERMINATE: 7468 SCIPprofileFree(&profile); 7469 7470 return retcode; 7471 } 7472 7473 /** propagate the cumulative constraint */ 7474 static 7475 SCIP_RETCODE propagateCons( 7476 SCIP* scip, /**< SCIP data structure */ 7477 SCIP_CONS* cons, /**< constraint to propagate */ 7478 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */ 7479 SCIP_PRESOLTIMING presoltiming, /**< current presolving timing */ 7480 int* nchgbds, /**< pointer to store the number of bound changes */ 7481 int* ndelconss, /**< pointer to store the number of deleted constraints */ 7482 SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */ 7483 ) 7484 { 7485 SCIP_CONSDATA* consdata; 7486 SCIP_Bool initialized; 7487 SCIP_Bool redundant; 7488 int oldnchgbds; 7489 7490 assert(scip != NULL); 7491 assert(cons != NULL); 7492 7493 consdata = SCIPconsGetData(cons); 7494 assert(consdata != NULL); 7495 7496 oldnchgbds = *nchgbds; 7497 initialized = FALSE; 7498 redundant = FALSE; 7499 7500 if( SCIPconsIsDeleted(cons) ) 7501 { 7502 assert(SCIPinProbing(scip)); 7503 return SCIP_OKAY; 7504 } 7505 7506 /* if the constraint marked to be propagated, do nothing */ 7507 if( consdata->propagated && SCIPgetStage(scip) != SCIP_STAGE_PRESOLVING ) 7508 return SCIP_OKAY; 7509 7510 SCIP_CALL( propagateCumulativeCondition(scip, conshdlrdata, presoltiming, 7511 consdata->nvars, consdata->vars, consdata->durations, consdata->demands, consdata->capacity, 7512 consdata->hmin, consdata->hmax, cons, 7513 nchgbds, &redundant, &initialized, NULL, cutoff) ); 7514 7515 if( redundant ) 7516 { 7517 SCIPdebugMsg(scip, "%s deletes cumulative constraint <%s> since it is redundant\n", 7518 SCIPgetDepth(scip) == 0 ? "globally" : "locally", SCIPconsGetName(cons)); 7519 7520 if( !SCIPinProbing(scip) ) 7521 { 7522 SCIP_CALL( SCIPdelConsLocal(scip, cons) ); 7523 (*ndelconss)++; 7524 } 7525 } 7526 else 7527 { 7528 if( initialized ) 7529 { 7530 /* run conflict analysis since it was initialized */ 7531 assert(*cutoff == TRUE); 7532 SCIPdebugMsg(scip, "start conflict analysis\n"); 7533 SCIP_CALL( SCIPanalyzeConflictCons(scip, cons, NULL) ); 7534 } 7535 7536 /* if successful, reset age of constraint */ 7537 if( *cutoff || *nchgbds > oldnchgbds ) 7538 { 7539 SCIP_CALL( SCIPresetConsAge(scip, cons) ); 7540 } 7541 else 7542 { 7543 /* mark the constraint to be propagated */ 7544 consdata->propagated = TRUE; 7545 } 7546 } 7547 7548 return SCIP_OKAY; 7549 } 7550 7551 /** it is dual feasible to remove the values {leftub+1, ..., rightlb-1} since SCIP current does not feature domain holes 7552 * we use the probing mode to check if one of the two branches is infeasible. If this is the case the dual redundant can 7553 * be realize as domain reduction. Otherwise we do nothing 7554 */ 7555 static 7556 SCIP_RETCODE applyProbingVar( 7557 SCIP* scip, /**< SCIP data structure */ 7558 SCIP_VAR** vars, /**< problem variables */ 7559 int nvars, /**< number of problem variables */ 7560 int probingpos, /**< variable number to apply probing on */ 7561 SCIP_Real leftub, /**< upper bound of probing variable in left branch */ 7562 SCIP_Real rightlb, /**< lower bound of probing variable in right branch */ 7563 SCIP_Real* leftimpllbs, /**< lower bounds after applying implications and cliques in left branch, or NULL */ 7564 SCIP_Real* leftimplubs, /**< upper bounds after applying implications and cliques in left branch, or NULL */ 7565 SCIP_Real* leftproplbs, /**< lower bounds after applying domain propagation in left branch */ 7566 SCIP_Real* leftpropubs, /**< upper bounds after applying domain propagation in left branch */ 7567 SCIP_Real* rightimpllbs, /**< lower bounds after applying implications and cliques in right branch, or NULL */ 7568 SCIP_Real* rightimplubs, /**< upper bounds after applying implications and cliques in right branch, or NULL */ 7569 SCIP_Real* rightproplbs, /**< lower bounds after applying domain propagation in right branch */ 7570 SCIP_Real* rightpropubs, /**< upper bounds after applying domain propagation in right branch */ 7571 int* nfixedvars, /**< pointer to counter which is increased by the number of deduced variable fixations */ 7572 SCIP_Bool* success, /**< buffer to store whether a probing succeed to dual fix the variable */ 7573 SCIP_Bool* cutoff /**< buffer to store whether a cutoff is detected */ 7574 ) 7575 { 7576 SCIP_VAR* var; 7577 SCIP_Bool tightened; 7578 7579 assert(probingpos >= 0); 7580 assert(probingpos < nvars); 7581 assert(success != NULL); 7582 assert(cutoff != NULL); 7583 7584 var = vars[probingpos]; 7585 assert(var != NULL); 7586 assert(SCIPisGE(scip, leftub, SCIPvarGetLbLocal(var))); 7587 assert(SCIPisLE(scip, leftub, SCIPvarGetUbLocal(var))); 7588 assert(SCIPisGE(scip, rightlb, SCIPvarGetLbLocal(var))); 7589 assert(SCIPisLE(scip, rightlb, SCIPvarGetUbLocal(var))); 7590 7591 (*success) = FALSE; 7592 7593 if( SCIPinProbing(scip) || SCIPinRepropagation(scip) ) 7594 return SCIP_OKAY; 7595 7596 /* apply probing for the earliest start time (lower bound) of the variable (x <= est) */ 7597 SCIP_CALL( SCIPapplyProbingVar(scip, vars, nvars, probingpos, SCIP_BOUNDTYPE_UPPER, leftub, -1, 7598 leftimpllbs, leftimplubs, leftproplbs, leftpropubs, cutoff) ); 7599 7600 if( (*cutoff) ) 7601 { 7602 /* note that cutoff may occur if presolving has not been executed fully */ 7603 SCIP_CALL( SCIPtightenVarLb(scip, var, rightlb, TRUE, cutoff, &tightened) ); 7604 7605 if( tightened ) 7606 { 7607 (*success) =TRUE; 7608 (*nfixedvars)++; 7609 } 7610 7611 return SCIP_OKAY; 7612 } 7613 7614 /* note that probing can change the upper bound and thus the right branch may have been detected infeasible if 7615 * presolving has not been executed fully 7616 */ 7617 if( SCIPisGT(scip, rightlb, SCIPvarGetUbLocal(var)) ) 7618 { 7619 /* note that cutoff may occur if presolving has not been executed fully */ 7620 SCIP_CALL( SCIPtightenVarUb(scip, var, leftub, TRUE, cutoff, &tightened) ); 7621 7622 if( tightened ) 7623 { 7624 (*success) = TRUE; 7625 (*nfixedvars)++; 7626 } 7627 7628 return SCIP_OKAY; 7629 } 7630 7631 /* apply probing for the alternative lower bound of the variable (x <= alternativeubs[v]) */ 7632 SCIP_CALL( SCIPapplyProbingVar(scip, vars, nvars, probingpos, SCIP_BOUNDTYPE_LOWER, rightlb, -1, 7633 rightimpllbs, rightimplubs, rightproplbs, rightpropubs, cutoff) ); 7634 7635 if( (*cutoff) ) 7636 { 7637 /* note that cutoff may occur if presolving has not been executed fully */ 7638 SCIP_CALL( SCIPtightenVarUb(scip, var, leftub, TRUE, cutoff, &tightened) ); 7639 7640 if( tightened ) 7641 { 7642 (*success) =TRUE; 7643 (*nfixedvars)++; 7644 } 7645 7646 return SCIP_OKAY; 7647 } 7648 7649 return SCIP_OKAY; 7650 } 7651 7652 /** is it possible, to round variable down w.r.t. objective function */ 7653 static 7654 SCIP_RETCODE varMayRoundDown( 7655 SCIP* scip, /**< SCIP data structure */ 7656 SCIP_VAR* var, /**< problem variable */ 7657 SCIP_Bool* roundable /**< pointer to store if the variable can be rounded down */ 7658 ) 7659 { 7660 SCIP_Real objval; 7661 int scalar; 7662 7663 assert(roundable != NULL); 7664 7665 *roundable = TRUE; 7666 7667 /* a fixed variable can be definition always be safely rounded */ 7668 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_FIXED ) 7669 return SCIP_OKAY; 7670 7671 /* in case the variable is not active we need to check the object coefficient of the active variable */ 7672 if( !SCIPvarIsActive(var) ) 7673 { 7674 SCIP_VAR* actvar; 7675 int constant; 7676 7677 actvar = var; 7678 7679 SCIP_CALL( getActiveVar(scip, &actvar, &scalar, &constant) ); 7680 assert(scalar != 0); 7681 7682 objval = scalar * SCIPvarGetObj(actvar); 7683 } /*lint !e438*/ 7684 else 7685 { 7686 scalar = 1; 7687 objval = SCIPvarGetObj(var); 7688 } 7689 7690 /* rounding the integer variable down is only a valid dual reduction if the object coefficient is zero or positive 7691 * (the transformed problem is always a minimization problem) 7692 * 7693 * @note that we need to check this condition w.r.t. active variable space 7694 */ 7695 if( (scalar > 0 && SCIPisNegative(scip, objval)) || (scalar < 0 && SCIPisPositive(scip, objval)) ) 7696 *roundable = FALSE; 7697 7698 return SCIP_OKAY; 7699 } 7700 7701 /** is it possible, to round variable up w.r.t. objective function */ 7702 static 7703 SCIP_RETCODE varMayRoundUp( 7704 SCIP* scip, /**< SCIP data structure */ 7705 SCIP_VAR* var, /**< problem variable */ 7706 SCIP_Bool* roundable /**< pointer to store if the variable can be rounded down */ 7707 ) 7708 { 7709 SCIP_Real objval; 7710 int scalar; 7711 7712 assert(roundable != NULL); 7713 7714 *roundable = TRUE; 7715 7716 /* a fixed variable can be definition always be safely rounded */ 7717 if( SCIPvarGetStatus(var) == SCIP_VARSTATUS_FIXED ) 7718 return SCIP_OKAY; 7719 7720 /* in case the variable is not active we need to check the object coefficient of the active variable */ 7721 if( !SCIPvarIsActive(var) ) 7722 { 7723 SCIP_VAR* actvar; 7724 int constant; 7725 7726 actvar = var; 7727 7728 SCIP_CALL( getActiveVar(scip, &actvar, &scalar, &constant) ); 7729 assert(scalar != 0); 7730 7731 objval = scalar * SCIPvarGetObj(actvar); 7732 } /*lint !e438*/ 7733 else 7734 { 7735 scalar = 1; 7736 objval = SCIPvarGetObj(var); 7737 } 7738 7739 /* rounding the integer variable up is only a valid dual reduction if the object coefficient is zero or negative 7740 * (the transformed problem is always a minimization problem) 7741 * 7742 * @note that we need to check this condition w.r.t. active variable space 7743 */ 7744 if( (scalar > 0 && SCIPisPositive(scip, objval)) || (scalar < 0 && SCIPisNegative(scip, objval)) ) 7745 *roundable = FALSE; 7746 7747 return SCIP_OKAY; 7748 } 7749 7750 /** For each variable we compute an alternative lower and upper bounds. That is, if the variable is not fixed to its 7751 * lower or upper bound the next reasonable lower or upper bound would be this alternative bound (implying that certain 7752 * values are not of interest). An alternative bound for a particular is only valied if the cumulative constarints are 7753 * the only one locking this variable in the corresponding direction. 7754 */ 7755 static 7756 SCIP_RETCODE computeAlternativeBounds( 7757 SCIP* scip, /**< SCIP data structure */ 7758 SCIP_CONS** conss, /**< array of cumulative constraint constraints */ 7759 int nconss, /**< number of cumulative constraints */ 7760 SCIP_Bool local, /**< use local bounds effective horizon? */ 7761 int* alternativelbs, /**< alternative lower bounds */ 7762 int* alternativeubs, /**< alternative lower bounds */ 7763 int* downlocks, /**< number of constraints with down lock participating by the computation */ 7764 int* uplocks /**< number of constraints with up lock participating by the computation */ 7765 ) 7766 { 7767 int nvars; 7768 int c; 7769 int v; 7770 7771 for( c = 0; c < nconss; ++c ) 7772 { 7773 SCIP_CONSDATA* consdata; 7774 SCIP_CONS* cons; 7775 SCIP_VAR* var; 7776 int hmin; 7777 int hmax; 7778 7779 cons = conss[c]; 7780 assert(cons != NULL); 7781 7782 /* ignore constraints which are already deletet and those which are not check constraints */ 7783 if( SCIPconsIsDeleted(cons) || !SCIPconsIsChecked(cons) ) 7784 continue; 7785 7786 consdata = SCIPconsGetData(cons); 7787 assert(consdata != NULL); 7788 assert(consdata->nvars > 1); 7789 7790 /* compute the hmin and hmax */ 7791 if( local ) 7792 { 7793 SCIP_PROFILE* profile; 7794 SCIP_RETCODE retcode; 7795 7796 /* create empty resource profile with infinity resource capacity */ 7797 SCIP_CALL( SCIPprofileCreate(&profile, INT_MAX) ); 7798 7799 /* create worst case resource profile */ 7800 retcode = SCIPcreateWorstCaseProfile(scip, profile, consdata->nvars, consdata->vars, consdata->durations, consdata->demands); 7801 7802 hmin = SCIPcomputeHmin(scip, profile, consdata->capacity); 7803 hmax = SCIPcomputeHmax(scip, profile, consdata->capacity); 7804 7805 /* free worst case profile */ 7806 SCIPprofileFree(&profile); 7807 7808 if( retcode != SCIP_OKAY ) 7809 return retcode; 7810 } 7811 else 7812 { 7813 hmin = consdata->hmin; 7814 hmax = consdata->hmax; 7815 } 7816 7817 consdata = SCIPconsGetData(cons); 7818 assert(consdata != NULL); 7819 7820 nvars = consdata->nvars; 7821 7822 for( v = 0; v < nvars; ++v ) 7823 { 7824 int scalar; 7825 int constant; 7826 int idx; 7827 7828 var = consdata->vars[v]; 7829 assert(var != NULL); 7830 7831 /* multi-aggregated variables should appear here since we mark the variables to be not mutlt-aggregated */ 7832 assert(SCIPvarGetStatus(var) != SCIP_VARSTATUS_MULTAGGR); 7833 7834 /* ignore variable locally fixed variables */ 7835 if( SCIPvarGetUbLocal(var) - SCIPvarGetLbLocal(var) < 0.5 ) 7836 continue; 7837 7838 SCIP_CALL( getActiveVar(scip, &var, &scalar, &constant) ); 7839 idx = SCIPvarGetProbindex(var); 7840 assert(idx >= 0); 7841 7842 /* first check lower bound fixing */ 7843 if( consdata->downlocks[v] ) 7844 { 7845 int ect; 7846 int est; 7847 7848 /* the variable has a down locked */ 7849 est = scalar * SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(var)) + constant; 7850 ect = est + consdata->durations[v]; 7851 7852 if( ect <= hmin || hmin >= hmax ) 7853 downlocks[idx]++; 7854 else if( est < hmin && alternativelbs[idx] >= (hmin + 1 - constant) / scalar ) 7855 { 7856 alternativelbs[idx] = (hmin + 1 - constant) / scalar; 7857 downlocks[idx]++; 7858 } 7859 } 7860 7861 /* second check upper bound fixing */ 7862 if( consdata->uplocks[v] ) 7863 { 7864 int duration; 7865 int lct; 7866 int lst; 7867 7868 duration = consdata->durations[v]; 7869 7870 /* the variable has a up lock locked */ 7871 lst = scalar * SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(var)) + constant; 7872 lct = lst + duration; 7873 7874 if( lst >= hmax || hmin >= hmax ) 7875 uplocks[idx]++; 7876 else if( lct > hmax && alternativeubs[idx] <= ((hmax - 1 - constant) / scalar) - duration ) 7877 { 7878 alternativeubs[idx] = ((hmax - 1 - constant) / scalar) - duration; 7879 uplocks[idx]++; 7880 } 7881 } 7882 } 7883 } 7884 7885 return SCIP_OKAY; 7886 } 7887 7888 /** apply all fixings which are given by the alternative bounds */ 7889 static 7890 SCIP_RETCODE applyAlternativeBoundsFixing( 7891 SCIP* scip, /**< SCIP data structure */ 7892 SCIP_VAR** vars, /**< array of active variables */ 7893 int nvars, /**< number of active variables */ 7894 int* alternativelbs, /**< alternative lower bounds */ 7895 int* alternativeubs, /**< alternative lower bounds */ 7896 int* downlocks, /**< number of constraints with down lock participating by the computation */ 7897 int* uplocks, /**< number of constraints with up lock participating by the computation */ 7898 int* nfixedvars, /**< pointer to counter which is increased by the number of deduced variable fixations */ 7899 SCIP_Bool* cutoff /**< buffer to store whether a cutoff is detected */ 7900 ) 7901 { 7902 SCIP_Real* downimpllbs; 7903 SCIP_Real* downimplubs; 7904 SCIP_Real* downproplbs; 7905 SCIP_Real* downpropubs; 7906 SCIP_Real* upimpllbs; 7907 SCIP_Real* upimplubs; 7908 SCIP_Real* upproplbs; 7909 SCIP_Real* uppropubs; 7910 int v; 7911 7912 /* get temporary memory for storing probing results */ 7913 SCIP_CALL( SCIPallocBufferArray(scip, &downimpllbs, nvars) ); 7914 SCIP_CALL( SCIPallocBufferArray(scip, &downimplubs, nvars) ); 7915 SCIP_CALL( SCIPallocBufferArray(scip, &downproplbs, nvars) ); 7916 SCIP_CALL( SCIPallocBufferArray(scip, &downpropubs, nvars) ); 7917 SCIP_CALL( SCIPallocBufferArray(scip, &upimpllbs, nvars) ); 7918 SCIP_CALL( SCIPallocBufferArray(scip, &upimplubs, nvars) ); 7919 SCIP_CALL( SCIPallocBufferArray(scip, &upproplbs, nvars) ); 7920 SCIP_CALL( SCIPallocBufferArray(scip, &uppropubs, nvars) ); 7921 7922 for( v = 0; v < nvars; ++v ) 7923 { 7924 SCIP_VAR* var; 7925 SCIP_Bool infeasible; 7926 SCIP_Bool fixed; 7927 SCIP_Bool roundable; 7928 int ub; 7929 int lb; 7930 7931 var = vars[v]; 7932 assert(var != NULL); 7933 7934 /* ignore variables for which no alternative bounds have been computed */ 7935 if( alternativelbs[v] == INT_MAX && alternativeubs[v] == INT_MIN ) 7936 continue; 7937 7938 lb = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(var)); 7939 ub = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(var)); 7940 7941 /* ignore fixed variables */ 7942 if( ub - lb <= 0 ) 7943 continue; 7944 7945 if( SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL) == downlocks[v] ) 7946 { 7947 SCIP_CALL( varMayRoundDown(scip, var, &roundable) ); 7948 7949 if( roundable ) 7950 { 7951 if( alternativelbs[v] > ub ) 7952 { 7953 SCIP_CALL( SCIPfixVar(scip, var, SCIPvarGetLbLocal(var), &infeasible, &fixed) ); 7954 assert(!infeasible); 7955 assert(fixed); 7956 7957 (*nfixedvars)++; 7958 7959 /* for the statistic we count the number of jobs which are dual fixed due the information of all cumulative 7960 * constraints 7961 */ 7962 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->nallconsdualfixs++ ); 7963 } 7964 else 7965 { 7966 SCIP_Bool success; 7967 7968 /* In the current version SCIP, variable domains are single intervals. Meaning that domain holes or not 7969 * representable. To retrieve a potential dual reduction we using probing to check both branches. If one in 7970 * infeasible we can apply the dual reduction; otherwise we do nothing 7971 */ 7972 SCIP_CALL( applyProbingVar(scip, vars, nvars, v, (SCIP_Real) lb, (SCIP_Real) alternativelbs[v], 7973 downimpllbs, downimplubs, downproplbs, downpropubs, upimpllbs, upimplubs, upproplbs, uppropubs, 7974 nfixedvars, &success, cutoff) ); 7975 7976 if( success ) 7977 { 7978 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->nallconsdualfixs++ ); 7979 } 7980 } 7981 } 7982 } 7983 7984 lb = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(var)); 7985 ub = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(var)); 7986 7987 /* ignore fixed variables */ 7988 if( ub - lb <= 0 ) 7989 continue; 7990 7991 if( SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL) == uplocks[v] ) 7992 { 7993 SCIP_CALL( varMayRoundUp(scip, var, &roundable) ); 7994 7995 if( roundable ) 7996 { 7997 if( alternativeubs[v] < lb ) 7998 { 7999 SCIP_CALL( SCIPfixVar(scip, var, SCIPvarGetUbLocal(var), &infeasible, &fixed) ); 8000 assert(!infeasible); 8001 assert(fixed); 8002 8003 (*nfixedvars)++; 8004 8005 /* for the statistic we count the number of jobs which are dual fixed due the information of all cumulative 8006 * constraints 8007 */ 8008 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->nallconsdualfixs++ ); 8009 } 8010 else 8011 { 8012 SCIP_Bool success; 8013 8014 /* In the current version SCIP, variable domains are single intervals. Meaning that domain holes or not 8015 * representable. To retrieve a potential dual reduction we using probing to check both branches. If one in 8016 * infeasible we can apply the dual reduction; otherwise we do nothing 8017 */ 8018 SCIP_CALL( applyProbingVar(scip, vars, nvars, v, (SCIP_Real) alternativeubs[v], (SCIP_Real) ub, 8019 downimpllbs, downimplubs, downproplbs, downpropubs, upimpllbs, upimplubs, upproplbs, uppropubs, 8020 nfixedvars, &success, cutoff) ); 8021 8022 if( success ) 8023 { 8024 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->nallconsdualfixs++ ); 8025 } 8026 } 8027 } 8028 } 8029 } 8030 8031 /* free temporary memory */ 8032 SCIPfreeBufferArray(scip, &uppropubs); 8033 SCIPfreeBufferArray(scip, &upproplbs); 8034 SCIPfreeBufferArray(scip, &upimplubs); 8035 SCIPfreeBufferArray(scip, &upimpllbs); 8036 SCIPfreeBufferArray(scip, &downpropubs); 8037 SCIPfreeBufferArray(scip, &downproplbs); 8038 SCIPfreeBufferArray(scip, &downimplubs); 8039 SCIPfreeBufferArray(scip, &downimpllbs); 8040 8041 return SCIP_OKAY; 8042 } 8043 8044 /** propagate all constraints together */ 8045 static 8046 SCIP_RETCODE propagateAllConss( 8047 SCIP* scip, /**< SCIP data structure */ 8048 SCIP_CONS** conss, /**< all cumulative constraint */ 8049 int nconss, /**< number of cumulative constraints */ 8050 SCIP_Bool local, /**< use local bounds effective horizon? */ 8051 int* nfixedvars, /**< pointer to counter which is increased by the number of deduced variable fixations */ 8052 SCIP_Bool* cutoff, /**< buffer to store whether a cutoff is detected */ 8053 SCIP_Bool* branched /**< pointer to store if a branching was applied, or NULL to avoid branching */ 8054 ) 8055 { /*lint --e{715}*/ 8056 SCIP_VAR** vars; 8057 int* downlocks; 8058 int* uplocks; 8059 int* alternativelbs; 8060 int* alternativeubs; 8061 int oldnfixedvars; 8062 int nvars; 8063 int v; 8064 8065 if( SCIPinProbing(scip) || SCIPinRepropagation(scip) ) 8066 return SCIP_OKAY; 8067 8068 nvars = SCIPgetNVars(scip); 8069 oldnfixedvars = *nfixedvars; 8070 8071 SCIP_CALL( SCIPduplicateBufferArray(scip, &vars, SCIPgetVars(scip), nvars) ); /*lint !e666*/ 8072 SCIP_CALL( SCIPallocBufferArray(scip, &downlocks, nvars) ); 8073 SCIP_CALL( SCIPallocBufferArray(scip, &uplocks, nvars) ); 8074 SCIP_CALL( SCIPallocBufferArray(scip, &alternativelbs, nvars) ); 8075 SCIP_CALL( SCIPallocBufferArray(scip, &alternativeubs, nvars) ); 8076 8077 /* initialize arrays */ 8078 for( v = 0; v < nvars; ++v ) 8079 { 8080 downlocks[v] = 0; 8081 uplocks[v] = 0; 8082 alternativelbs[v] = INT_MAX; 8083 alternativeubs[v] = INT_MIN; 8084 } 8085 8086 /* compute alternative bounds */ 8087 SCIP_CALL( computeAlternativeBounds(scip, conss, nconss, local, alternativelbs, alternativeubs, downlocks, uplocks) ); 8088 8089 /* apply fixing which result of the alternative bounds directly */ 8090 SCIP_CALL( applyAlternativeBoundsFixing(scip, vars, nvars, alternativelbs, alternativeubs, downlocks, uplocks, 8091 nfixedvars, cutoff) ); 8092 8093 if( !(*cutoff) && oldnfixedvars == *nfixedvars && branched != NULL ) 8094 { 8095 SCIP_CALL( applyAlternativeBoundsBranching(scip, vars, nvars, alternativelbs, alternativeubs, downlocks, uplocks, branched) ); 8096 } 8097 8098 /* free all buffers */ 8099 SCIPfreeBufferArray(scip, &alternativeubs); 8100 SCIPfreeBufferArray(scip, &alternativelbs); 8101 SCIPfreeBufferArray(scip, &uplocks); 8102 SCIPfreeBufferArray(scip, &downlocks); 8103 SCIPfreeBufferArray(scip, &vars); 8104 8105 return SCIP_OKAY; 8106 } 8107 8108 /**@} */ 8109 8110 /**@name Linear relaxations 8111 * 8112 * @{ 8113 */ 8114 8115 /** creates covering cuts for jobs violating resource constraints */ 8116 static 8117 SCIP_RETCODE createCoverCutsTimepoint( 8118 SCIP* scip, /**< SCIP data structure */ 8119 SCIP_CONS* cons, /**< constraint to be checked */ 8120 int* startvalues, /**< upper bounds on finishing time per job for activities from 0,..., nactivities -1 */ 8121 int time /**< at this point in time covering constraints are valid */ 8122 ) 8123 { 8124 SCIP_CONSDATA* consdata; 8125 SCIP_ROW* row; 8126 int* flexibleids; 8127 int* demands; 8128 8129 char rowname[SCIP_MAXSTRLEN]; 8130 8131 int remainingcap; 8132 int smallcoversize; /* size of a small cover */ 8133 int bigcoversize; /* size of a big cover */ 8134 int nvars; 8135 8136 int nflexible; 8137 int sumdemand; /* demand of all jobs up to a certain index */ 8138 int j; 8139 8140 assert(cons != NULL); 8141 8142 /* get constraint data structure */ 8143 consdata = SCIPconsGetData(cons); 8144 assert(consdata != NULL ); 8145 8146 nvars = consdata->nvars; 8147 8148 /* sort jobs according to demands */ 8149 SCIP_CALL( SCIPallocBufferArray(scip, &demands, nvars) ); 8150 SCIP_CALL( SCIPallocBufferArray(scip, &flexibleids, nvars) ); 8151 8152 nflexible = 0; 8153 remainingcap = consdata->capacity; 8154 8155 /* get all jobs intersecting point 'time' with their bounds */ 8156 for( j = 0; j < nvars; ++j ) 8157 { 8158 int ub; 8159 8160 ub = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(consdata->vars[j])); 8161 8162 /* only add jobs to array if they intersect with point 'time' */ 8163 if( startvalues[j] <= time && ub + consdata->durations[j] > time ) 8164 { 8165 /* if job is fixed, capacity has to be decreased */ 8166 if( startvalues[j] == ub ) 8167 { 8168 remainingcap -= consdata->demands[j]; 8169 } 8170 else 8171 { 8172 demands[nflexible] = consdata->demands[j]; 8173 flexibleids[nflexible] = j; 8174 ++nflexible; 8175 } 8176 } 8177 } 8178 assert(remainingcap >= 0); 8179 8180 /* sort demands and job ids */ 8181 SCIPsortIntInt(demands, flexibleids, nflexible); 8182 8183 /* 8184 * version 1: 8185 * D_j := sum_i=0,...,j d_i, finde j maximal, so dass D_j <= remainingcap 8186 * erzeuge cover constraint 8187 * 8188 */ 8189 8190 /* find maximum number of jobs that can run in parallel (-->coversize = j) */ 8191 sumdemand = 0; 8192 j = 0; 8193 8194 while( j < nflexible && sumdemand <= remainingcap ) 8195 { 8196 sumdemand += demands[j]; 8197 j++; 8198 } 8199 8200 /* j jobs form a conflict, set coversize to 'j - 1' */ 8201 bigcoversize = j-1; 8202 assert(sumdemand > remainingcap); 8203 assert(bigcoversize < nflexible); 8204 8205 /* - create a row for all jobs and their binary variables. 8206 * - at most coversize many binary variables of jobs can be set to one 8207 */ 8208 8209 /* construct row name */ 8210 (void)SCIPsnprintf(rowname, SCIP_MAXSTRLEN, "capacity_coverbig_%d", time); 8211 SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, cons, rowname, -SCIPinfinity(scip), (SCIP_Real)bigcoversize, 8212 SCIPconsIsLocal(cons), SCIPconsIsModifiable(cons), TRUE) ); 8213 SCIP_CALL( SCIPcacheRowExtensions(scip, row) ); 8214 8215 for( j = 0; j < nflexible; ++j ) 8216 { 8217 SCIP_VAR** binvars; 8218 SCIP_Real* vals; 8219 int nbinvars; 8220 int idx; 8221 int start; 8222 int end; 8223 int lb; 8224 int ub; 8225 int b; 8226 8227 idx = flexibleids[j]; 8228 8229 /* get and add binvars into var array */ 8230 SCIP_CALL( SCIPgetBinvarsLinking(scip, consdata->linkingconss[idx], &binvars, &nbinvars) ); 8231 assert(nbinvars != 0); 8232 8233 vals = SCIPgetValsLinking(scip, consdata->linkingconss[idx]); 8234 assert(vals != NULL); 8235 8236 lb = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(consdata->vars[idx])); 8237 ub = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(consdata->vars[idx])); 8238 8239 /* compute start and finishing time */ 8240 start = time - consdata->durations[idx] + 1; 8241 end = MIN(time, ub); 8242 8243 /* add all neccessary binary variables */ 8244 for( b = 0; b < nbinvars; ++b ) 8245 { 8246 if( vals[b] < start || vals[b] < lb ) 8247 continue; 8248 8249 if( vals[b] > end ) 8250 break; 8251 8252 assert(binvars[b] != NULL); 8253 SCIP_CALL( SCIPaddVarToRow(scip, row, binvars[b], 1.0) ); 8254 } 8255 } 8256 8257 /* insert and release row */ 8258 SCIP_CALL( SCIPflushRowExtensions(scip, row) ); 8259 8260 if( consdata->bcoverrowssize == 0 ) 8261 { 8262 consdata->bcoverrowssize = 10; 8263 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->bcoverrows, consdata->bcoverrowssize) ); 8264 } 8265 if( consdata->nbcoverrows == consdata->bcoverrowssize ) 8266 { 8267 consdata->bcoverrowssize *= 2; 8268 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->bcoverrows, consdata->nbcoverrows, consdata->bcoverrowssize) ); 8269 } 8270 8271 consdata->bcoverrows[consdata->nbcoverrows] = row; 8272 consdata->nbcoverrows++; 8273 8274 /* 8275 * version 2: 8276 * D_j := sum_i=j,...,0 d_i, finde j minimal, so dass D_j <= remainingcap 8277 * erzeuge cover constraint und fuege alle jobs i hinzu, mit d_i = d_largest 8278 */ 8279 /* find maximum number of jobs that can run in parallel (= coversize -1) */ 8280 sumdemand = 0; 8281 j = nflexible -1; 8282 while( sumdemand <= remainingcap ) 8283 { 8284 assert(j >= 0); 8285 sumdemand += demands[j]; 8286 j--; 8287 } 8288 8289 smallcoversize = nflexible - (j + 1) - 1; 8290 while( j > 0 && demands[j] == demands[nflexible-1] ) 8291 --j; 8292 8293 assert(smallcoversize < nflexible); 8294 8295 if( smallcoversize != 1 || smallcoversize != nflexible - (j + 1) - 1 ) 8296 { 8297 /* construct row name */ 8298 (void)SCIPsnprintf(rowname, SCIP_MAXSTRLEN, "capacity_coversmall_%d", time); 8299 SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, cons, rowname, -SCIPinfinity(scip), (SCIP_Real)smallcoversize, 8300 SCIPconsIsLocal(cons), SCIPconsIsModifiable(cons), TRUE) ); 8301 SCIP_CALL( SCIPcacheRowExtensions(scip, row) ); 8302 8303 /* filter binary variables for each unfixed job */ 8304 for( j = j + 1; j < nflexible; ++j ) 8305 { 8306 SCIP_VAR** binvars; 8307 SCIP_Real* vals; 8308 int nbinvars; 8309 int idx; 8310 int start; 8311 int end; 8312 int lb; 8313 int ub; 8314 int b; 8315 8316 idx = flexibleids[j]; 8317 8318 /* get and add binvars into var array */ 8319 SCIP_CALL( SCIPgetBinvarsLinking(scip, consdata->linkingconss[idx], &binvars, &nbinvars) ); 8320 assert(nbinvars != 0); 8321 8322 vals = SCIPgetValsLinking(scip, consdata->linkingconss[idx]); 8323 assert(vals != NULL); 8324 8325 lb = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(consdata->vars[idx])); 8326 ub = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(consdata->vars[idx])); 8327 8328 /* compute start and finishing time */ 8329 start = time - consdata->durations[idx] + 1; 8330 end = MIN(time, ub); 8331 8332 /* add all neccessary binary variables */ 8333 for( b = 0; b < nbinvars; ++b ) 8334 { 8335 if( vals[b] < start || vals[b] < lb ) 8336 continue; 8337 8338 if( vals[b] > end ) 8339 break; 8340 8341 assert(binvars[b] != NULL); 8342 SCIP_CALL( SCIPaddVarToRow(scip, row, binvars[b], 1.0) ); 8343 } 8344 } 8345 8346 /* insert and release row */ 8347 SCIP_CALL( SCIPflushRowExtensions(scip, row) ); 8348 if( consdata->scoverrowssize == 0 ) 8349 { 8350 consdata->scoverrowssize = 10; 8351 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->scoverrows, consdata->scoverrowssize) ); 8352 } 8353 if( consdata->nscoverrows == consdata->scoverrowssize ) 8354 { 8355 consdata->scoverrowssize *= 2; 8356 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->scoverrows, consdata->nscoverrows, consdata->scoverrowssize) ); 8357 } 8358 8359 consdata->scoverrows[consdata->nscoverrows] = row; 8360 consdata->nscoverrows++; 8361 } 8362 8363 /* free buffer arrays */ 8364 SCIPfreeBufferArray(scip, &flexibleids); 8365 SCIPfreeBufferArray(scip, &demands); 8366 8367 return SCIP_OKAY; 8368 } 8369 8370 /** method to construct cover cuts for all points in time */ 8371 static 8372 SCIP_RETCODE createCoverCuts( 8373 SCIP* scip, /**< SCIP data structure */ 8374 SCIP_CONS* cons /**< constraint to be separated */ 8375 ) 8376 { 8377 SCIP_CONSDATA* consdata; 8378 8379 int* startvalues; /* stores when each job is starting */ 8380 int* endvalues; /* stores when each job ends */ 8381 int* startvaluessorted; /* stores when each job is starting */ 8382 int* endvaluessorted; /* stores when each job ends */ 8383 int* startindices; /* we sort the startvalues, so we need to know wich index of a job it corresponds to */ 8384 int* endindices; /* we sort the endvalues, so we need to know wich index of a job it corresponds to */ 8385 8386 int nvars; /* number of jobs for this constraint */ 8387 int freecapacity; /* remaining capacity */ 8388 int curtime; /* point in time which we are just checking */ 8389 int endidx; /* index of endsolvalues with: endsolvalues[endindex] > curtime */ 8390 8391 int hmin; 8392 int hmax; 8393 8394 int j; 8395 int t; 8396 8397 assert(scip != NULL); 8398 assert(cons != NULL); 8399 8400 consdata = SCIPconsGetData(cons); 8401 assert(consdata != NULL); 8402 8403 /* if no activities are associated with this resource then this constraint is redundant */ 8404 if( consdata->vars == NULL ) 8405 return SCIP_OKAY; 8406 8407 nvars = consdata->nvars; 8408 hmin = consdata->hmin; 8409 hmax = consdata->hmax; 8410 8411 SCIP_CALL( SCIPallocBufferArray(scip, &startvalues, nvars) ); 8412 SCIP_CALL( SCIPallocBufferArray(scip, &endvalues, nvars) ); 8413 SCIP_CALL( SCIPallocBufferArray(scip, &startvaluessorted, nvars) ); 8414 SCIP_CALL( SCIPallocBufferArray(scip, &endvaluessorted, nvars) ); 8415 SCIP_CALL( SCIPallocBufferArray(scip, &startindices, nvars) ); 8416 SCIP_CALL( SCIPallocBufferArray(scip, &endindices, nvars) ); 8417 8418 /* assign start and endpoints to arrays */ 8419 for ( j = 0; j < nvars; ++j ) 8420 { 8421 startvalues[j] = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(consdata->vars[j])); 8422 startvaluessorted[j] = startvalues[j]; 8423 8424 endvalues[j] = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(consdata->vars[j])) + consdata->durations[j]; 8425 endvaluessorted[j] = endvalues[j]; 8426 8427 startindices[j] = j; 8428 endindices[j] = j; 8429 } 8430 8431 /* sort the arrays not-decreasing according to startsolvalues and endsolvalues 8432 * (and sort the indices in the same way) */ 8433 SCIPsortIntInt(startvaluessorted, startindices, nvars); 8434 SCIPsortIntInt(endvaluessorted, endindices, nvars); 8435 8436 endidx = 0; 8437 freecapacity = consdata->capacity; 8438 8439 /* check each startpoint of a job whether the capacity is kept or not */ 8440 for( j = 0; j < nvars; ++j ) 8441 { 8442 curtime = startvaluessorted[j]; 8443 if( curtime >= hmax ) 8444 break; 8445 8446 /* subtract all capacity needed up to this point */ 8447 freecapacity -= consdata->demands[startindices[j]]; 8448 8449 while( j+1 < nvars && startvaluessorted[j+1] == curtime ) 8450 { 8451 ++j; 8452 freecapacity -= consdata->demands[startindices[j]]; 8453 } 8454 8455 /* free all capacity usages of jobs the are no longer running */ 8456 while( endidx < nvars && curtime >= endvaluessorted[endidx] ) 8457 { 8458 freecapacity += consdata->demands[endindices[endidx]]; 8459 ++endidx; 8460 } 8461 8462 assert(freecapacity <= consdata->capacity); 8463 assert(endidx <= nvars); 8464 8465 /* --> endindex - points to the next job which will finish 8466 * j - points to the last job that has been released 8467 */ 8468 8469 /* check freecapacity to be smaller than zero 8470 * then we will add cover constraints to the MIP 8471 */ 8472 if( freecapacity < 0 && curtime >= hmin ) 8473 { 8474 int nextprofilechange; 8475 8476 /* we can create covering constraints for each pint in time in interval [curtime; nextprofilechange[ */ 8477 if( j < nvars-1 ) 8478 nextprofilechange = MIN( startvaluessorted[j+1], endvaluessorted[endidx] ); 8479 else 8480 nextprofilechange = endvaluessorted[endidx]; 8481 8482 nextprofilechange = MIN(nextprofilechange, hmax); 8483 8484 for( t = curtime; t < nextprofilechange; ++t ) 8485 { 8486 SCIPdebugMsg(scip, "add cover constraint for time %d\n", curtime); 8487 8488 /* create covering constraint */ 8489 SCIP_CALL( createCoverCutsTimepoint(scip, cons, startvalues, t) ); 8490 } 8491 } /* end if freecapacity > 0 */ 8492 } /*lint --e{850}*/ 8493 8494 consdata->covercuts = TRUE; 8495 8496 /* free all buffer arrays */ 8497 SCIPfreeBufferArray(scip, &endindices); 8498 SCIPfreeBufferArray(scip, &startindices); 8499 SCIPfreeBufferArray(scip, &endvaluessorted); 8500 SCIPfreeBufferArray(scip, &startvaluessorted); 8501 SCIPfreeBufferArray(scip, &endvalues); 8502 SCIPfreeBufferArray(scip, &startvalues); 8503 8504 return SCIP_OKAY; 8505 } 8506 8507 /** this method creates a row for time point curtime which insures the capacity restriction of the cumulative 8508 * constraint 8509 */ 8510 static 8511 SCIP_RETCODE createCapacityRestriction( 8512 SCIP* scip, /**< SCIP data structure */ 8513 SCIP_CONS* cons, /**< constraint to be checked */ 8514 int* startindices, /**< permutation with rspect to the start times */ 8515 int curtime, /**< current point in time */ 8516 int nstarted, /**< number of jobs that start before the curtime or at curtime */ 8517 int nfinished, /**< number of jobs that finished before curtime or at curtime */ 8518 SCIP_Bool cutsasconss /**< should the cumulative constraint create the cuts as constraints? */ 8519 ) 8520 { 8521 SCIP_CONSDATA* consdata; 8522 SCIP_VAR** binvars; 8523 int* coefs; 8524 int nbinvars; 8525 char name[SCIP_MAXSTRLEN]; 8526 int capacity; 8527 int b; 8528 8529 assert(nstarted > nfinished); 8530 8531 consdata = SCIPconsGetData(cons); 8532 assert(consdata != NULL); 8533 assert(consdata->nvars > 0); 8534 8535 capacity = consdata->capacity; 8536 assert(capacity > 0); 8537 8538 nbinvars = 0; 8539 SCIP_CALL( collectBinaryVars(scip, consdata, &binvars, &coefs, &nbinvars, startindices, curtime, nstarted, nfinished) ); 8540 8541 /* construct row name */ 8542 (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_%d[%d]", SCIPconsGetName(cons), nstarted-1, curtime); 8543 8544 if( cutsasconss ) 8545 { 8546 SCIP_CONS* lincons; 8547 8548 /* create knapsack constraint for the given time point */ 8549 SCIP_CALL( SCIPcreateConsKnapsack(scip, &lincons, name, 0, NULL, NULL, (SCIP_Longint)(capacity), 8550 TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE) ); 8551 8552 for( b = 0; b < nbinvars; ++b ) 8553 { 8554 SCIP_CALL( SCIPaddCoefKnapsack(scip, lincons, binvars[b], (SCIP_Longint)coefs[b]) ); 8555 } 8556 8557 /* add and release the new constraint */ 8558 SCIP_CALL( SCIPaddCons(scip, lincons) ); 8559 SCIP_CALL( SCIPreleaseCons(scip, &lincons) ); 8560 } 8561 else 8562 { 8563 SCIP_ROW* row; 8564 8565 SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, cons, name, -SCIPinfinity(scip), (SCIP_Real)capacity, FALSE, FALSE, SCIPconsIsRemovable(cons)) ); 8566 SCIP_CALL( SCIPcacheRowExtensions(scip, row) ); 8567 8568 for( b = 0; b < nbinvars; ++b ) 8569 { 8570 SCIP_CALL( SCIPaddVarToRow(scip, row, binvars[b], (SCIP_Real)coefs[b]) ); 8571 } 8572 8573 SCIP_CALL( SCIPflushRowExtensions(scip, row) ); 8574 SCIPdebug( SCIP_CALL(SCIPprintRow(scip, row, NULL)) ); 8575 8576 if( consdata->demandrowssize == 0 ) 8577 { 8578 consdata->demandrowssize = 10; 8579 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->demandrows, consdata->demandrowssize) ); 8580 } 8581 if( consdata->ndemandrows == consdata->demandrowssize ) 8582 { 8583 consdata->demandrowssize *= 2; 8584 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->demandrows, consdata->ndemandrows, consdata->demandrowssize) ); 8585 } 8586 8587 consdata->demandrows[consdata->ndemandrows] = row; 8588 consdata->ndemandrows++; 8589 } 8590 8591 SCIPfreeBufferArrayNull(scip, &binvars); 8592 SCIPfreeBufferArrayNull(scip, &coefs); 8593 8594 return SCIP_OKAY; 8595 } 8596 8597 /** this method checks how many cumulatives can run at most at one time if this is greater than the capacity it creates 8598 * row 8599 */ 8600 static 8601 SCIP_RETCODE consCapacityConstraintsFinder( 8602 SCIP* scip, /**< SCIP data structure */ 8603 SCIP_CONS* cons, /**< constraint to be checked */ 8604 SCIP_Bool cutsasconss /**< should the cumulative constraint create the cuts as constraints? */ 8605 ) 8606 { 8607 SCIP_CONSDATA* consdata; 8608 8609 int* starttimes; /* stores when each job is starting */ 8610 int* endtimes; /* stores when each job ends */ 8611 int* startindices; /* we will sort the startsolvalues, thus we need to know wich index of a job it corresponds to */ 8612 int* endindices; /* we will sort the endsolvalues, thus we need to know wich index of a job it corresponds to */ 8613 8614 int nvars; /* number of activities for this constraint */ 8615 int freecapacity; /* remaining capacity */ 8616 int curtime; /* point in time which we are just checking */ 8617 int endindex; /* index of endsolvalues with: endsolvalues[endindex] > curtime */ 8618 8619 int hmin; 8620 int hmax; 8621 8622 int j; 8623 8624 assert(scip != NULL); 8625 assert(cons != NULL); 8626 8627 consdata = SCIPconsGetData(cons); 8628 assert(consdata != NULL); 8629 8630 nvars = consdata->nvars; 8631 8632 /* if no activities are associated with this cumulative then this constraint is redundant */ 8633 if( nvars == 0 ) 8634 return SCIP_OKAY; 8635 8636 assert(consdata->vars != NULL); 8637 8638 SCIP_CALL( SCIPallocBufferArray(scip, &starttimes, nvars) ); 8639 SCIP_CALL( SCIPallocBufferArray(scip, &endtimes, nvars) ); 8640 SCIP_CALL( SCIPallocBufferArray(scip, &startindices, nvars) ); 8641 SCIP_CALL( SCIPallocBufferArray(scip, &endindices, nvars) ); 8642 8643 SCIPdebugMsg(scip, "create sorted event points for cumulative constraint <%s> with %d jobs\n", 8644 SCIPconsGetName(cons), nvars); 8645 8646 /* create event point arrays */ 8647 createSortedEventpoints(scip, nvars, consdata->vars, consdata->durations, 8648 starttimes, endtimes, startindices, endindices, FALSE); 8649 8650 endindex = 0; 8651 freecapacity = consdata->capacity; 8652 hmin = consdata->hmin; 8653 hmax = consdata->hmax; 8654 8655 /* check each startpoint of a job whether the capacity is kept or not */ 8656 for( j = 0; j < nvars; ++j ) 8657 { 8658 curtime = starttimes[j]; 8659 SCIPdebugMsg(scip, "look at %d-th job with start %d\n", j, curtime); 8660 8661 if( curtime >= hmax ) 8662 break; 8663 8664 /* remove the capacity requirments for all job which start at the curtime */ 8665 subtractStartingJobDemands(consdata, curtime, starttimes, startindices, &freecapacity, &j, nvars); 8666 8667 /* add the capacity requirments for all job which end at the curtime */ 8668 addEndingJobDemands(consdata, curtime, endtimes, endindices, &freecapacity, &endindex, nvars); 8669 8670 assert(freecapacity <= consdata->capacity); 8671 assert(endindex <= nvars); 8672 8673 /* endindex - points to the next job which will finish */ 8674 /* j - points to the last job that has been released */ 8675 8676 /* if free capacity is smaller than zero, then add rows to the LP */ 8677 if( freecapacity < 0 && curtime >= hmin ) 8678 { 8679 int nextstarttime; 8680 int t; 8681 8682 /* step forward until next job is released and see whether capacity constraint is met or not */ 8683 if( j < nvars-1 ) 8684 nextstarttime = starttimes[j+1]; 8685 else 8686 nextstarttime = endtimes[nvars-1]; 8687 8688 nextstarttime = MIN(nextstarttime, hmax); 8689 8690 /* create capacity restriction row for current event point */ 8691 SCIP_CALL( createCapacityRestriction(scip, cons, startindices, curtime, j+1, endindex, cutsasconss) ); 8692 8693 /* create for all points in time between the current event point and next start event point a row if the free 8694 * capacity is still smaller than zero */ 8695 for( t = curtime+1 ; t < nextstarttime; ++t ) 8696 { 8697 /* add the capacity requirments for all job which end at the curtime */ 8698 addEndingJobDemands(consdata, t, endtimes, endindices, &freecapacity, &endindex, nvars); 8699 8700 if( freecapacity < 0 ) 8701 { 8702 /* add constraint */ 8703 SCIPdebugMsg(scip, "add capacity constraint at time %d\n", t); 8704 8705 /* create capacity restriction row */ 8706 SCIP_CALL( createCapacityRestriction(scip, cons, startindices, t, j+1, endindex, cutsasconss) ); 8707 } 8708 else 8709 break; 8710 } 8711 } 8712 } /*lint --e{850}*/ 8713 8714 /* free all buffer arrays */ 8715 SCIPfreeBufferArray(scip, &endindices); 8716 SCIPfreeBufferArray(scip, &startindices); 8717 SCIPfreeBufferArray(scip, &endtimes); 8718 SCIPfreeBufferArray(scip, &starttimes); 8719 8720 return SCIP_OKAY; 8721 } 8722 8723 /** creates LP rows corresponding to cumulative constraint; therefore, check each point in time if the maximal needed 8724 * capacity is larger than the capacity of the cumulative constraint 8725 * - for each necessary point in time: 8726 * 8727 * sum_j sum_t demand_j * x_{j,t} <= capacity 8728 * 8729 * where x(j,t) is the binary variables of job j at time t 8730 */ 8731 static 8732 SCIP_RETCODE createRelaxation( 8733 SCIP* scip, /**< SCIP data structure */ 8734 SCIP_CONS* cons, /**< cumulative constraint */ 8735 SCIP_Bool cutsasconss /**< should the cumulative constraint create the cuts as constraints? */ 8736 ) 8737 { 8738 SCIP_CONSDATA* consdata; 8739 8740 consdata = SCIPconsGetData(cons); 8741 assert(consdata != NULL); 8742 assert(consdata->demandrows == NULL); 8743 assert(consdata->ndemandrows == 0); 8744 8745 /* collect the linking constraints */ 8746 if( consdata->linkingconss == NULL ) 8747 { 8748 SCIP_CALL( consdataCollectLinkingCons(scip, consdata) ); 8749 } 8750 8751 SCIP_CALL( consCapacityConstraintsFinder(scip, cons, cutsasconss) ); 8752 8753 /* switch of separation for the cumulative constraint if linear constraints are add as cuts */ 8754 if( cutsasconss ) 8755 { 8756 if( SCIPconsIsInitial(cons) ) 8757 { 8758 SCIP_CALL( SCIPsetConsInitial(scip, cons, FALSE) ); 8759 } 8760 if( SCIPconsIsSeparated(cons) ) 8761 { 8762 SCIP_CALL( SCIPsetConsSeparated(scip, cons, FALSE) ); 8763 } 8764 if( SCIPconsIsEnforced(cons) ) 8765 { 8766 SCIP_CALL( SCIPsetConsEnforced(scip, cons, FALSE) ); 8767 } 8768 } 8769 8770 return SCIP_OKAY; 8771 } 8772 8773 /** adds linear relaxation of cumulative constraint to the LP */ 8774 static 8775 SCIP_RETCODE addRelaxation( 8776 SCIP* scip, /**< SCIP data structure */ 8777 SCIP_CONS* cons, /**< cumulative constraint */ 8778 SCIP_Bool cutsasconss, /**< should the cumulative constraint create the cuts as constraints? */ 8779 SCIP_Bool* infeasible /**< pointer to store whether an infeasibility was detected */ 8780 ) 8781 { 8782 SCIP_CONSDATA* consdata; 8783 int r; 8784 8785 consdata = SCIPconsGetData(cons); 8786 assert(consdata != NULL); 8787 8788 if( consdata->demandrows == NULL ) 8789 { 8790 assert(consdata->ndemandrows == 0); 8791 8792 SCIP_CALL( createRelaxation(scip, cons, cutsasconss) ); 8793 8794 return SCIP_OKAY; 8795 } 8796 8797 for( r = 0; r < consdata->ndemandrows && !(*infeasible); ++r ) 8798 { 8799 if( !SCIProwIsInLP(consdata->demandrows[r]) ) 8800 { 8801 assert(consdata->demandrows[r] != NULL); 8802 SCIP_CALL( SCIPaddRow(scip, consdata->demandrows[r], FALSE, infeasible) ); 8803 } 8804 } 8805 8806 return SCIP_OKAY; 8807 } 8808 8809 /** checks constraint for violation, and adds it as a cut if possible */ 8810 static 8811 SCIP_RETCODE separateConsBinaryRepresentation( 8812 SCIP* scip, /**< SCIP data structure */ 8813 SCIP_CONS* cons, /**< cumulative constraint to be separated */ 8814 SCIP_SOL* sol, /**< primal CIP solution, NULL for current LP solution */ 8815 SCIP_Bool* separated, /**< pointer to store TRUE, if a cut was found */ 8816 SCIP_Bool* cutoff /**< whether a cutoff has been detected */ 8817 ) 8818 { /*lint --e{715}*/ 8819 SCIP_CONSDATA* consdata; 8820 int ncuts; 8821 int r; 8822 8823 assert(scip != NULL); 8824 assert(cons != NULL); 8825 assert(separated != NULL); 8826 assert(cutoff != NULL); 8827 8828 *separated = FALSE; 8829 *cutoff = FALSE; 8830 8831 consdata = SCIPconsGetData(cons); 8832 assert(consdata != NULL); 8833 8834 SCIPdebugMsg(scip, "separate cumulative constraint <%s>\n", SCIPconsGetName(cons)); 8835 8836 if( consdata->demandrows == NULL ) 8837 { 8838 assert(consdata->ndemandrows == 0); 8839 8840 SCIP_CALL( createRelaxation(scip, cons, FALSE) ); 8841 8842 return SCIP_OKAY; 8843 } 8844 8845 ncuts = 0; 8846 8847 /* check each row that is not contained in LP */ 8848 for( r = 0; r < consdata->ndemandrows; ++r ) 8849 { 8850 if( !SCIProwIsInLP(consdata->demandrows[r]) ) 8851 { 8852 SCIP_Real feasibility; 8853 8854 if( sol != NULL ) 8855 feasibility = SCIPgetRowSolFeasibility(scip, consdata->demandrows[r], sol); 8856 else 8857 feasibility = SCIPgetRowLPFeasibility(scip, consdata->demandrows[r]); 8858 8859 if( SCIPisFeasNegative(scip, feasibility) ) 8860 { 8861 SCIP_CALL( SCIPaddRow(scip, consdata->demandrows[r], FALSE, cutoff) ); 8862 if ( *cutoff ) 8863 { 8864 SCIP_CALL( SCIPresetConsAge(scip, cons) ); 8865 return SCIP_OKAY; 8866 } 8867 *separated = TRUE; 8868 ncuts++; 8869 } 8870 } 8871 } 8872 8873 if( ncuts > 0 ) 8874 { 8875 SCIPdebugMsg(scip, "cumulative constraint <%s> separated %d cuts\n", SCIPconsGetName(cons), ncuts); 8876 8877 /* if successful, reset age of constraint */ 8878 SCIP_CALL( SCIPresetConsAge(scip, cons) ); 8879 (*separated) = TRUE; 8880 } 8881 8882 return SCIP_OKAY; 8883 } 8884 8885 /** checks constraint for violation, and adds it as a cut if possible */ 8886 static 8887 SCIP_RETCODE separateCoverCutsCons( 8888 SCIP* scip, /**< SCIP data structure */ 8889 SCIP_CONS* cons, /**< logic or constraint to be separated */ 8890 SCIP_SOL* sol, /**< primal CIP solution, NULL for current LP solution */ 8891 SCIP_Bool* separated, /**< pointer to store TRUE, if a cut was found */ 8892 SCIP_Bool* cutoff /**< whether a cutoff has been detected */ 8893 ) 8894 { 8895 SCIP_CONSDATA* consdata; 8896 SCIP_ROW* row; 8897 SCIP_Real minfeasibility; 8898 int r; 8899 8900 assert(scip != NULL); 8901 assert(cons != NULL); 8902 assert(separated != NULL); 8903 assert(cutoff != NULL); 8904 8905 *separated = FALSE; 8906 *cutoff = FALSE; 8907 8908 consdata = SCIPconsGetData(cons); 8909 assert(consdata != NULL); 8910 8911 SCIPdebugMsg(scip, "separate cumulative constraint <%s>\n", SCIPconsGetName(cons)); 8912 8913 /* collect the linking constraints */ 8914 if( consdata->linkingconss == NULL ) 8915 { 8916 SCIP_CALL( consdataCollectLinkingCons(scip, consdata) ); 8917 } 8918 8919 if( !consdata->covercuts ) 8920 { 8921 SCIP_CALL( createCoverCuts(scip, cons) ); 8922 } 8923 8924 row = NULL; 8925 minfeasibility = SCIPinfinity(scip); 8926 8927 /* check each row of small covers that is not contained in LP */ 8928 for( r = 0; r < consdata->nscoverrows; ++r ) 8929 { 8930 if( !SCIProwIsInLP(consdata->scoverrows[r]) ) 8931 { 8932 SCIP_Real feasibility; 8933 8934 assert(consdata->scoverrows[r] != NULL); 8935 if( sol != NULL ) 8936 feasibility = SCIPgetRowSolFeasibility(scip, consdata->scoverrows[r], sol); 8937 else 8938 feasibility = SCIPgetRowLPFeasibility(scip, consdata->scoverrows[r]); 8939 8940 if( minfeasibility > feasibility ) 8941 { 8942 minfeasibility = feasibility; 8943 row = consdata->scoverrows[r]; 8944 } 8945 } 8946 } 8947 8948 assert(!SCIPisFeasNegative(scip, minfeasibility) || row != NULL); 8949 8950 if( row != NULL && SCIPisFeasNegative(scip, minfeasibility) ) 8951 { 8952 SCIPdebugMsg(scip, "cumulative constraint <%s> separated 1 cover cut with feasibility %g\n", 8953 SCIPconsGetName(cons), minfeasibility); 8954 8955 SCIP_CALL( SCIPaddRow(scip, row, FALSE, cutoff) ); 8956 SCIP_CALL( SCIPresetConsAge(scip, cons) ); 8957 if ( *cutoff ) 8958 return SCIP_OKAY; 8959 (*separated) = TRUE; 8960 } 8961 8962 minfeasibility = SCIPinfinity(scip); 8963 row = NULL; 8964 8965 /* check each row of small covers that is not contained in LP */ 8966 for( r = 0; r < consdata->nbcoverrows; ++r ) 8967 { 8968 if( !SCIProwIsInLP(consdata->bcoverrows[r]) ) 8969 { 8970 SCIP_Real feasibility; 8971 8972 assert(consdata->bcoverrows[r] != NULL); 8973 if( sol != NULL ) 8974 feasibility = SCIPgetRowSolFeasibility(scip, consdata->bcoverrows[r], sol); 8975 else 8976 feasibility = SCIPgetRowLPFeasibility(scip, consdata->bcoverrows[r]); 8977 8978 if( minfeasibility > feasibility ) 8979 { 8980 minfeasibility = feasibility; 8981 row = consdata->bcoverrows[r]; 8982 } 8983 } 8984 } 8985 8986 assert(!SCIPisFeasNegative(scip, minfeasibility) || row != NULL); 8987 8988 if( row != NULL && SCIPisFeasNegative(scip, minfeasibility) ) 8989 { 8990 SCIPdebugMsg(scip, "cumulative constraint <%s> separated 1 cover cut with feasibility %g\n", 8991 SCIPconsGetName(cons), minfeasibility); 8992 8993 assert(row != NULL); 8994 SCIP_CALL( SCIPaddRow(scip, row, FALSE, cutoff) ); 8995 SCIP_CALL( SCIPresetConsAge(scip, cons) ); 8996 if ( *cutoff ) 8997 return SCIP_OKAY; 8998 (*separated) = TRUE; 8999 } 9000 9001 return SCIP_OKAY; 9002 } 9003 9004 /** this method creates a row for time point @p curtime which ensures the capacity restriction of the cumulative constraint */ 9005 static 9006 SCIP_RETCODE createCapacityRestrictionIntvars( 9007 SCIP* scip, /**< SCIP data structure */ 9008 SCIP_CONS* cons, /**< constraint to be checked */ 9009 int* startindices, /**< permutation with rspect to the start times */ 9010 int curtime, /**< current point in time */ 9011 int nstarted, /**< number of jobs that start before the curtime or at curtime */ 9012 int nfinished, /**< number of jobs that finished before curtime or at curtime */ 9013 SCIP_Bool lower, /**< shall cuts be created due to lower or upper bounds? */ 9014 SCIP_Bool* cutoff /**< pointer to store TRUE, if a cutoff was detected */ 9015 ) 9016 { 9017 SCIP_CONSDATA* consdata; 9018 char name[SCIP_MAXSTRLEN]; 9019 int lhs; /* left hand side of constraint */ 9020 9021 SCIP_VAR** activevars; 9022 SCIP_ROW* row; 9023 9024 int v; 9025 9026 assert(nstarted > nfinished); 9027 9028 consdata = SCIPconsGetData(cons); 9029 assert(consdata != NULL); 9030 assert(consdata->nvars > 0); 9031 9032 SCIP_CALL( SCIPallocBufferArray(scip, &activevars, nstarted-nfinished) ); 9033 9034 SCIP_CALL( collectIntVars(scip, consdata, &activevars, startindices, curtime, nstarted, nfinished, lower, &lhs ) ); 9035 9036 if( lower ) 9037 { 9038 (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "lower(%d)", curtime); 9039 9040 SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, cons, name, (SCIP_Real) lhs, SCIPinfinity(scip), 9041 TRUE, FALSE, SCIPconsIsRemovable(cons)) ); 9042 } 9043 else 9044 { 9045 (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "upper(%d)", curtime); 9046 SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, cons, name, -SCIPinfinity(scip), (SCIP_Real) lhs, 9047 TRUE, FALSE, SCIPconsIsRemovable(cons)) ); 9048 } 9049 9050 SCIP_CALL( SCIPcacheRowExtensions(scip, row) ); 9051 9052 for( v = 0; v < nstarted - nfinished; ++v ) 9053 { 9054 SCIP_CALL( SCIPaddVarToRow(scip, row, activevars[v], 1.0) ); 9055 } 9056 9057 SCIP_CALL( SCIPflushRowExtensions(scip, row) ); 9058 SCIPdebug( SCIP_CALL(SCIPprintRow(scip, row, NULL)) ); 9059 9060 SCIP_CALL( SCIPaddRow(scip, row, TRUE, cutoff) ); 9061 9062 SCIP_CALL( SCIPreleaseRow(scip, &row) ); 9063 9064 /* free buffers */ 9065 SCIPfreeBufferArrayNull(scip, &activevars); 9066 9067 return SCIP_OKAY; 9068 } 9069 9070 /** checks constraint for violation, and adds it as a cut if possible */ 9071 static 9072 SCIP_RETCODE separateConsOnIntegerVariables( 9073 SCIP* scip, /**< SCIP data structure */ 9074 SCIP_CONS* cons, /**< cumulative constraint to be separated */ 9075 SCIP_SOL* sol, /**< primal CIP solution, NULL for current LP solution */ 9076 SCIP_Bool lower, /**< shall cuts be created according to lower bounds? */ 9077 SCIP_Bool* separated, /**< pointer to store TRUE, if a cut was found */ 9078 SCIP_Bool* cutoff /**< pointer to store TRUE, if a cutoff was detected */ 9079 ) 9080 { 9081 SCIP_CONSDATA* consdata; 9082 9083 int* starttimes; /* stores when each job is starting */ 9084 int* endtimes; /* stores when each job ends */ 9085 int* startindices; /* we will sort the startsolvalues, thus we need to know wich index of a job it corresponds to */ 9086 int* endindices; /* we will sort the endsolvalues, thus we need to know wich index of a job it corresponds to */ 9087 9088 int nvars; /* number of activities for this constraint */ 9089 int freecapacity; /* remaining capacity */ 9090 int curtime; /* point in time which we are just checking */ 9091 int endindex; /* index of endsolvalues with: endsolvalues[endindex] > curtime */ 9092 9093 int hmin; 9094 int hmax; 9095 int j; 9096 9097 assert(scip != NULL); 9098 assert(cons != NULL); 9099 9100 consdata = SCIPconsGetData(cons); 9101 assert(consdata != NULL); 9102 9103 nvars = consdata->nvars; 9104 9105 /* if no activities are associated with this cumulative then this constraint is redundant */ 9106 if( nvars <= 1 ) 9107 return SCIP_OKAY; 9108 9109 assert(consdata->vars != NULL); 9110 9111 SCIP_CALL( SCIPallocBufferArray(scip, &starttimes, nvars) ); 9112 SCIP_CALL( SCIPallocBufferArray(scip, &endtimes, nvars) ); 9113 SCIP_CALL( SCIPallocBufferArray(scip, &startindices, nvars) ); 9114 SCIP_CALL( SCIPallocBufferArray(scip, &endindices, nvars) ); 9115 9116 SCIPdebugMsg(scip, "create sorted event points for cumulative constraint <%s> with %d jobs\n", 9117 SCIPconsGetName(cons), nvars); 9118 9119 /* create event point arrays */ 9120 createSelectedSortedEventpointsSol(scip, consdata, sol, starttimes, endtimes, startindices, endindices, &nvars, lower); 9121 9122 /* now nvars might be smaller than before! */ 9123 9124 endindex = 0; 9125 freecapacity = consdata->capacity; 9126 hmin = consdata->hmin; 9127 hmax = consdata->hmax; 9128 9129 /* check each startpoint of a job whether the capacity is kept or not */ 9130 for( j = 0; j < nvars && !(*cutoff); ++j ) 9131 { 9132 curtime = starttimes[j]; 9133 9134 if( curtime >= hmax ) 9135 break; 9136 9137 /* remove the capacity requirements for all job which start at the curtime */ 9138 subtractStartingJobDemands(consdata, curtime, starttimes, startindices, &freecapacity, &j, nvars); 9139 9140 /* add the capacity requirments for all job which end at the curtime */ 9141 addEndingJobDemands(consdata, curtime, endtimes, endindices, &freecapacity, &endindex, nvars); 9142 9143 assert(freecapacity <= consdata->capacity); 9144 assert(endindex <= nvars); 9145 9146 /* endindex - points to the next job which will finish */ 9147 /* j - points to the last job that has been released */ 9148 9149 /* if free capacity is smaller than zero, then add rows to the LP */ 9150 if( freecapacity < 0 && curtime >= hmin) 9151 { 9152 /* create capacity restriction row for current event point */ 9153 SCIP_CALL( createCapacityRestrictionIntvars(scip, cons, startindices, curtime, j+1, endindex, lower, cutoff) ); 9154 *separated = TRUE; 9155 } 9156 } /*lint --e{850}*/ 9157 9158 /* free all buffer arrays */ 9159 SCIPfreeBufferArray(scip, &endindices); 9160 SCIPfreeBufferArray(scip, &startindices); 9161 SCIPfreeBufferArray(scip, &endtimes); 9162 SCIPfreeBufferArray(scip, &starttimes); 9163 9164 return SCIP_OKAY; 9165 } 9166 9167 /**@} */ 9168 9169 9170 /**@name Presolving 9171 * 9172 * @{ 9173 */ 9174 9175 #ifndef NDEBUG 9176 /** returns TRUE if all demands are smaller than the capacity of the cumulative constraint and if the total demand is 9177 * correct 9178 */ 9179 static 9180 SCIP_Bool checkDemands( 9181 SCIP* scip, /**< SCIP data structure */ 9182 SCIP_CONS* cons /**< constraint to be checked */ 9183 ) 9184 { 9185 SCIP_CONSDATA* consdata; 9186 int capacity; 9187 int nvars; 9188 int j; 9189 9190 assert(scip != NULL); 9191 assert(cons != NULL); 9192 9193 consdata = SCIPconsGetData(cons); 9194 assert(consdata != NULL); 9195 9196 nvars = consdata->nvars; 9197 9198 /* if no activities are associated with this cumulative then this constraint is not infeasible, return */ 9199 if( nvars <= 1 ) 9200 return TRUE; 9201 9202 assert(consdata->vars != NULL); 9203 capacity = consdata->capacity; 9204 9205 /* check each activity: if demand is larger than capacity the problem is infeasible */ 9206 for ( j = 0; j < nvars; ++j ) 9207 { 9208 if( consdata->demands[j] > capacity ) 9209 return FALSE; 9210 } 9211 9212 return TRUE; 9213 } 9214 #endif 9215 9216 /** delete constraint if it consists of at most one job 9217 * 9218 * @todo this method needs to be adjusted w.r.t. effective horizon 9219 */ 9220 static 9221 SCIP_RETCODE deleteTrivilCons( 9222 SCIP* scip, /**< SCIP data structure */ 9223 SCIP_CONS* cons, /**< constraint to propagate */ 9224 int* ndelconss, /**< pointer to store the number of deleted constraints */ 9225 SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */ 9226 ) 9227 { 9228 SCIP_CONSDATA* consdata; 9229 9230 assert(scip != NULL); 9231 assert(cons != NULL); 9232 9233 consdata = SCIPconsGetData(cons); 9234 assert(consdata != NULL); 9235 9236 if( consdata->nvars == 0 ) 9237 { 9238 SCIPdebugMsg(scip, "delete cumulative constraints <%s>\n", SCIPconsGetName(cons)); 9239 9240 SCIP_CALL( SCIPdelCons(scip, cons) ); 9241 (*ndelconss)++; 9242 } 9243 else if( consdata->nvars == 1 ) 9244 { 9245 if( consdata->demands[0] > consdata->capacity ) 9246 (*cutoff) = TRUE; 9247 else 9248 { 9249 SCIPdebugMsg(scip, "delete cumulative constraints <%s>\n", SCIPconsGetName(cons)); 9250 9251 SCIP_CALL( SCIPdelCons(scip, cons) ); 9252 (*ndelconss)++; 9253 } 9254 } 9255 9256 return SCIP_OKAY; 9257 } 9258 9259 /** remove jobs which have a duration or demand of zero (zero energy) or lay outside the efficient horizon [hmin, hmax); 9260 * this is done in the SCIP_DECL_CONSINITPRE() callback 9261 */ 9262 static 9263 SCIP_RETCODE removeIrrelevantJobs( 9264 SCIP* scip, /**< SCIP data structure */ 9265 SCIP_CONS* cons /**< constraint to propagate */ 9266 ) 9267 { 9268 SCIP_CONSDATA* consdata; 9269 SCIP_VAR* var; 9270 int demand; 9271 int duration; 9272 int hmin; 9273 int hmax; 9274 int est; 9275 int lct; 9276 int j; 9277 9278 assert(scip != NULL); 9279 assert(cons != NULL); 9280 9281 consdata = SCIPconsGetData(cons); 9282 assert(consdata != NULL); 9283 9284 hmin = consdata->hmin; 9285 hmax = consdata->hmax; 9286 9287 SCIPdebugMsg(scip, "check for irrelevant jobs within cumulative constraint <%s>[%d,%d)\n", 9288 SCIPconsGetName(cons), hmin, hmax); 9289 9290 for( j = consdata->nvars-1; j >= 0; --j ) 9291 { 9292 var = consdata->vars[j]; 9293 demand = consdata->demands[j]; 9294 duration = consdata->durations[j]; 9295 9296 /* earliest completion time (ect) and latest start time (lst) */ 9297 est = SCIPconvertRealToInt(scip, SCIPvarGetLbGlobal(var)); 9298 lct = SCIPconvertRealToInt(scip, SCIPvarGetUbGlobal(var)) + duration; 9299 9300 if( demand == 0 || duration == 0 ) 9301 { 9302 /* jobs with zero demand or zero duration can be removed */ 9303 SCIPdebugMsg(scip, " remove variable <%s> due to zero %s\n", 9304 SCIPvarGetName(var), demand == 0 ? "demand" : "duration"); 9305 9306 /* remove variable form constraint */ 9307 SCIP_CALL( consdataDeletePos(scip, consdata, cons, j) ); 9308 } 9309 else if( est >= hmax || lct <= hmin ) 9310 { 9311 SCIPdebugMsg(scip, " remove variable <%s>[%d,%d] with duration <%d>\n", 9312 SCIPvarGetName(var), est, lct - duration, duration); 9313 9314 /* delete variable at the given position */ 9315 SCIP_CALL( consdataDeletePos(scip, consdata, cons, j) ); 9316 9317 /* for the statistic we count the number of jobs which are irrelevant */ 9318 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->nirrelevantjobs++ ); 9319 } 9320 } 9321 9322 return SCIP_OKAY; 9323 } 9324 9325 /** adjust bounds of over sizeed job (the demand is larger than the capacity) */ 9326 static 9327 SCIP_RETCODE adjustOversizedJobBounds( 9328 SCIP* scip, /**< SCIP data structure */ 9329 SCIP_CONSDATA* consdata, /**< constraint data */ 9330 int pos, /**< position of job in the consdata */ 9331 int* nchgbds, /**< pointer to store the number of changed bounds */ 9332 int* naddconss, /**< pointer to store the number of added constraints */ 9333 SCIP_Bool* cutoff /**< pointer to store if a cutoff was detected */ 9334 ) 9335 { 9336 SCIP_VAR* var; 9337 SCIP_Bool tightened; 9338 int duration; 9339 int ect; 9340 int lst; 9341 9342 assert(scip != NULL); 9343 9344 /* zero energy jobs should be removed already */ 9345 assert(consdata->durations[pos] > 0); 9346 assert(consdata->demands[pos] > 0); 9347 9348 var = consdata->vars[pos]; 9349 assert(var != NULL); 9350 duration = consdata->durations[pos]; 9351 9352 /* jobs with a demand greater than the the capacity have to moved outside the time interval [hmin,hmax) */ 9353 SCIPdebugMsg(scip, " variable <%s>: demand <%d> is larger than the capacity <%d>\n", 9354 SCIPvarGetName(var), consdata->demands[pos], consdata->capacity); 9355 9356 /* earliest completion time (ect) and latest start time (lst) */ 9357 ect = SCIPconvertRealToInt(scip, SCIPvarGetLbGlobal(var)) + duration; 9358 lst = SCIPconvertRealToInt(scip, SCIPvarGetUbGlobal(var)); 9359 9360 /* the jobs has to have an overlap with the efficient horizon otherwise it would be already removed */ 9361 if( ect - duration >= consdata->hmax || lst + duration <= consdata->hmin) 9362 return SCIP_OKAY; 9363 9364 if( ect > consdata->hmin && lst < consdata->hmax ) 9365 { 9366 /* the job will at least run partly in the time interval [hmin,hmax) this means the problem is infeasible */ 9367 *cutoff = TRUE; 9368 } 9369 else if( lst < consdata->hmax ) 9370 { 9371 /* move the latest start time of this job in such a way that it finishes before or at hmin */ 9372 SCIP_CALL( SCIPtightenVarUb(scip, var, (SCIP_Real)(consdata->hmin - duration), TRUE, cutoff, &tightened) ); 9373 assert(tightened); 9374 assert(!(*cutoff)); 9375 (*nchgbds)++; 9376 } 9377 else if( ect > consdata->hmin ) 9378 { 9379 /* move the earliest start time of this job in such a way that it starts after or at hmax */ 9380 SCIP_CALL( SCIPtightenVarLb(scip, var, (SCIP_Real)(consdata->hmax), TRUE, cutoff, &tightened) ); 9381 assert(tightened); 9382 assert(!(*cutoff)); 9383 (*nchgbds)++; 9384 } 9385 else 9386 { 9387 /* this job can run before or after the time interval [hmin,hmax) thus we create a bound disjunction 9388 * constraint to ensure that it does not overlap with the time interval [hmin,hmax); that is: 9389 * 9390 * (var <= hmin - duration) /\ (var >= hmax) 9391 */ 9392 SCIP_CONS* cons; 9393 9394 SCIP_VAR* vartuple[2]; 9395 SCIP_BOUNDTYPE boundtypetuple[2]; 9396 SCIP_Real boundtuple[2]; 9397 9398 char name[SCIP_MAXSTRLEN]; 9399 int leftbound; 9400 int rightbound; 9401 9402 leftbound = consdata->hmin - duration; 9403 rightbound = consdata->hmax; 9404 9405 /* allocate temporary memory for arrays */ 9406 vartuple[0] = var; 9407 vartuple[1] = var; 9408 boundtuple[0] = (SCIP_Real)leftbound; 9409 boundtuple[1] = (SCIP_Real)rightbound; 9410 boundtypetuple[0] = SCIP_BOUNDTYPE_UPPER; 9411 boundtypetuple[1] = SCIP_BOUNDTYPE_LOWER; 9412 9413 (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s<=%d or %s >= %d", 9414 SCIPvarGetName(var), leftbound, SCIPvarGetName(var), rightbound); 9415 9416 /* create and add bounddisjunction constraint */ 9417 SCIP_CALL( SCIPcreateConsBounddisjunction(scip, &cons, name, 2, vartuple, boundtypetuple, boundtuple, 9418 TRUE, FALSE, TRUE, TRUE /*check*/, TRUE/*prop*/, FALSE, FALSE, FALSE, FALSE, FALSE) ); 9419 9420 SCIPdebugPrintCons(scip, cons, NULL); 9421 9422 /* add and release the new constraint */ 9423 SCIP_CALL( SCIPaddCons(scip, cons) ); 9424 SCIP_CALL( SCIPreleaseCons(scip, &cons) ); 9425 (*naddconss)++; 9426 } 9427 9428 return SCIP_OKAY; 9429 } 9430 9431 /** try to removed over sizeed jobs (the demand is larger than the capacity) */ 9432 static 9433 SCIP_RETCODE removeOversizedJobs( 9434 SCIP* scip, /**< SCIP data structure */ 9435 SCIP_CONS* cons, /**< constraint */ 9436 int* nchgbds, /**< pointer to store the number of changed bounds */ 9437 int* nchgcoefs, /**< pointer to store the number of changed coefficient */ 9438 int* naddconss, /**< pointer to store the number of added constraints */ 9439 SCIP_Bool* cutoff /**< pointer to store if a cutoff was detected */ 9440 ) 9441 { 9442 SCIP_CONSDATA* consdata; 9443 int capacity; 9444 int j; 9445 9446 consdata = SCIPconsGetData(cons); 9447 assert(consdata != NULL); 9448 9449 /* if a cutoff was already detected just return */ 9450 if( *cutoff ) 9451 return SCIP_OKAY; 9452 9453 capacity = consdata->capacity; 9454 9455 for( j = consdata->nvars-1; j >= 0 && !(*cutoff); --j ) 9456 { 9457 if( consdata->demands[j] > capacity ) 9458 { 9459 SCIP_CALL( adjustOversizedJobBounds(scip, consdata, j, nchgbds, naddconss, cutoff) ); 9460 9461 /* remove variable form constraint */ 9462 SCIP_CALL( consdataDeletePos(scip, consdata, cons, j) ); 9463 (*nchgcoefs)++; 9464 } 9465 } 9466 9467 SCIPdebugMsg(scip, "cumulative constraint <%s> has %d jobs left, cutoff %u\n", SCIPconsGetName(cons), consdata->nvars, *cutoff); 9468 9469 return SCIP_OKAY; 9470 } 9471 9472 /** fix integer variable to upper bound if the rounding locks and the object coefficient are in favor of that */ 9473 static 9474 SCIP_RETCODE fixIntegerVariableUb( 9475 SCIP* scip, /**< SCIP data structure */ 9476 SCIP_VAR* var, /**< integer variable to fix */ 9477 SCIP_Bool uplock, /**< has thet start time variable a up lock */ 9478 int* nfixedvars /**< pointer to store the number fixed variables */ 9479 ) 9480 { 9481 SCIP_Bool infeasible; 9482 SCIP_Bool tightened; 9483 SCIP_Bool roundable; 9484 9485 /* if SCIP is in probing mode or repropagation we cannot perform this dual reductions since this dual reduction 9486 * would/could end in an implication which can lead to cutoff of the/all optimal solution 9487 */ 9488 if( SCIPinProbing(scip) || SCIPinRepropagation(scip) ) 9489 return SCIP_OKAY; 9490 9491 /* rounding the variable to the upper bound is only a feasible dual reduction if the cumulative constraint 9492 * handler is the only one locking that variable up 9493 */ 9494 assert(uplock == TRUE || uplock == FALSE); 9495 assert((int)TRUE == 1); /*lint !e506*/ 9496 assert((int)FALSE == 0); /*lint !e506*/ 9497 9498 if( SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL) > (int)(uplock) ) 9499 return SCIP_OKAY; 9500 9501 SCIP_CALL( varMayRoundUp(scip, var, &roundable) ); 9502 9503 /* rounding the integer variable up is only a valid dual reduction if the object coefficient is zero or negative 9504 * (the transformed problem is always a minimization problem) 9505 */ 9506 if( !roundable ) 9507 return SCIP_OKAY; 9508 9509 SCIPdebugMsg(scip, "try fixing variable <%s>[%g,%g] to upper bound %g\n", SCIPvarGetName(var), 9510 SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var), SCIPvarGetUbLocal(var)); 9511 9512 SCIP_CALL( SCIPfixVar(scip, var, SCIPvarGetUbLocal(var), &infeasible, &tightened) ); 9513 assert(!infeasible); 9514 9515 if( tightened ) 9516 { 9517 SCIPdebugMsg(scip, "fix variable <%s> to upper bound %g\n", SCIPvarGetName(var), SCIPvarGetUbLocal(var)); 9518 (*nfixedvars)++; 9519 } 9520 9521 return SCIP_OKAY; 9522 } 9523 9524 /** fix integer variable to lower bound if the rounding locks and the object coefficient are in favor of that */ 9525 static 9526 SCIP_RETCODE fixIntegerVariableLb( 9527 SCIP* scip, /**< SCIP data structure */ 9528 SCIP_VAR* var, /**< integer variable to fix */ 9529 SCIP_Bool downlock, /**< has the variable a down lock */ 9530 int* nfixedvars /**< pointer to store the number fixed variables */ 9531 ) 9532 { 9533 SCIP_Bool infeasible; 9534 SCIP_Bool tightened; 9535 SCIP_Bool roundable; 9536 9537 /* if SCIP is in probing mode or repropagation we cannot perform this dual reductions since this dual reduction 9538 * would/could end in an implication which can lead to cutoff of the/all optimal solution 9539 */ 9540 if( SCIPinProbing(scip) || SCIPinRepropagation(scip) ) 9541 return SCIP_OKAY; 9542 9543 /* rounding the variable to the lower bound is only a feasible dual reduction if the cumulative constraint 9544 * handler is the only one locking that variable down 9545 */ 9546 assert(downlock == TRUE || downlock == FALSE); 9547 assert((int)TRUE == 1); /*lint !e506*/ 9548 assert((int)FALSE == 0); /*lint !e506*/ 9549 9550 if( SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL) > (int)(downlock) ) 9551 return SCIP_OKAY; 9552 9553 SCIP_CALL( varMayRoundDown(scip, var, &roundable) ); 9554 9555 /* is it possible, to round variable down w.r.t. objective function? */ 9556 if( !roundable ) 9557 return SCIP_OKAY; 9558 9559 SCIP_CALL( SCIPfixVar(scip, var, SCIPvarGetLbLocal(var), &infeasible, &tightened) ); 9560 assert(!infeasible); 9561 9562 if( tightened ) 9563 { 9564 SCIPdebugMsg(scip, "fix variable <%s> to lower bound %g\n", SCIPvarGetName(var), SCIPvarGetLbLocal(var)); 9565 (*nfixedvars)++; 9566 } 9567 9568 return SCIP_OKAY; 9569 } 9570 9571 /** normalize cumulative condition */ 9572 static 9573 void normalizeCumulativeCondition( 9574 SCIP* scip, /**< SCIP data structure */ 9575 int nvars, /**< number of start time variables (activities) */ 9576 int* demands, /**< array of demands */ 9577 int* capacity, /**< pointer to store the changed cumulative capacity */ 9578 int* nchgcoefs, /**< pointer to count total number of changed coefficients */ 9579 int* nchgsides /**< pointer to count number of side changes */ 9580 ) 9581 { /*lint --e{715}*/ 9582 SCIP_Longint gcd; 9583 int mindemand1; 9584 int mindemand2; 9585 int v; 9586 9587 if( *capacity == 1 || nvars <= 1 ) 9588 return; 9589 9590 assert(demands[nvars-1] <= *capacity); 9591 assert(demands[nvars-2] <= *capacity); 9592 9593 gcd = (SCIP_Longint)demands[nvars-1]; 9594 mindemand1 = MIN(demands[nvars-1], demands[nvars-2]); 9595 mindemand2 = MAX(demands[nvars-1], demands[nvars-2]); 9596 9597 for( v = nvars-2; v >= 0 && (gcd >= 2 || mindemand1 + mindemand2 > *capacity); --v ) 9598 { 9599 assert(mindemand1 <= mindemand2); 9600 assert(demands[v] <= *capacity); 9601 9602 gcd = SCIPcalcGreComDiv(gcd, (SCIP_Longint)demands[v]); 9603 9604 if( mindemand1 > demands[v] ) 9605 { 9606 mindemand2 = mindemand1; 9607 mindemand1 = demands[v]; 9608 } 9609 else if( mindemand2 > demands[v] ) 9610 mindemand2 = demands[v]; 9611 } 9612 9613 if( mindemand1 + mindemand2 > *capacity ) 9614 { 9615 SCIPdebugMsg(scip, "update cumulative condition (%d + %d > %d) to unary cumulative condition\n", mindemand1, mindemand2, *capacity); 9616 9617 for( v = 0; v < nvars; ++v ) 9618 demands[v] = 1; 9619 9620 (*capacity) = 1; 9621 9622 (*nchgcoefs) += nvars; 9623 (*nchgsides)++; 9624 } 9625 else if( gcd >= 2 ) 9626 { 9627 SCIPdebugMsg(scip, "cumulative condition: dividing demands by %" SCIP_LONGINT_FORMAT "\n", gcd); 9628 9629 for( v = 0; v < nvars; ++v ) 9630 demands[v] /= (int) gcd; 9631 9632 (*capacity) /= (int) gcd; 9633 9634 (*nchgcoefs) += nvars; 9635 (*nchgsides)++; 9636 } 9637 } 9638 9639 /** divides demands by their greatest common divisor and divides capacity by the same value, rounding down the result; 9640 * in case the the smallest demands add up to more than the capacity we reductions all demands to one as well as the 9641 * capacity since in that case none of the jobs can run in parallel 9642 */ 9643 static 9644 void normalizeDemands( 9645 SCIP* scip, /**< SCIP data structure */ 9646 SCIP_CONS* cons, /**< cumulative constraint */ 9647 int* nchgcoefs, /**< pointer to count total number of changed coefficients */ 9648 int* nchgsides /**< pointer to count number of side changes */ 9649 ) 9650 { 9651 SCIP_CONSDATA* consdata; 9652 int capacity; 9653 9654 assert(nchgcoefs != NULL); 9655 assert(nchgsides != NULL); 9656 assert(!SCIPconsIsModifiable(cons)); 9657 9658 consdata = SCIPconsGetData(cons); 9659 assert(consdata != NULL); 9660 9661 if( consdata->normalized ) 9662 return; 9663 9664 capacity = consdata->capacity; 9665 9666 /**@todo sort items w.r.t. the demands, because we can stop earlier if the smaller weights are evaluated first */ 9667 9668 normalizeCumulativeCondition(scip, consdata->nvars, consdata->demands, &consdata->capacity, nchgcoefs, nchgsides); 9669 9670 consdata->normalized = TRUE; 9671 9672 if( capacity > consdata->capacity ) 9673 consdata->varbounds = FALSE; 9674 } 9675 9676 /** computes for the given cumulative condition the effective horizon */ 9677 static 9678 SCIP_RETCODE computeEffectiveHorizonCumulativeCondition( 9679 SCIP* scip, /**< SCIP data structure */ 9680 int nvars, /**< number of variables (jobs) */ 9681 SCIP_VAR** vars, /**< array of integer variable which corresponds to starting times for a job */ 9682 int* durations, /**< array containing corresponding durations */ 9683 int* demands, /**< array containing corresponding demands */ 9684 int capacity, /**< available cumulative capacity */ 9685 int* hmin, /**< pointer to store the left bound of the effective horizon */ 9686 int* hmax, /**< pointer to store the right bound of the effective horizon */ 9687 int* split /**< point were the cumulative condition can be split */ 9688 ) 9689 { 9690 SCIP_PROFILE* profile; 9691 9692 /* create empty resource profile with infinity resource capacity */ 9693 SCIP_CALL( SCIPprofileCreate(&profile, INT_MAX) ); 9694 9695 /* create worst case resource profile */ 9696 SCIP_CALL_FINALLY( SCIPcreateWorstCaseProfile(scip, profile, nvars, vars, durations, demands), SCIPprofileFree(&profile) ); 9697 9698 /* print resource profile in if SCIP_DEBUG is defined */ 9699 SCIPdebug( SCIPprofilePrint(profile, SCIPgetMessagehdlr(scip), NULL) ); 9700 9701 /* computes the first time point where the resource capacity can be violated */ 9702 (*hmin) = SCIPcomputeHmin(scip, profile, capacity); 9703 9704 /* computes the first time point where the resource capacity is satisfied for sure */ 9705 (*hmax) = SCIPcomputeHmax(scip, profile, capacity); 9706 9707 (*split) = (*hmax); 9708 9709 if( *hmin < *hmax && !SCIPinRepropagation(scip) ) 9710 { 9711 int* timepoints; 9712 int* loads; 9713 int ntimepoints; 9714 int t; 9715 9716 /* If SCIP is repropagating the root node, it is not possible to decompose the constraints. This is the case since 9717 * the conflict analysis stores the constraint pointer for bound changes made by this constraint. These pointer 9718 * are used during the resolve propagation phase to explain bound changes. If we would decompose certain jobs into 9719 * a new cumulative constraint, the "old" pointer is not valid. More precise, the "old" constraint is not able to 9720 * explain the certain "old" bound changes 9721 */ 9722 9723 /* search for time points */ 9724 ntimepoints = SCIPprofileGetNTimepoints(profile); 9725 timepoints = SCIPprofileGetTimepoints(profile); 9726 loads = SCIPprofileGetLoads(profile); 9727 9728 /* check if there exist a time point within the effective horizon [hmin,hmax) such that the capacity is not exceed w.r.t. worst case profile */ 9729 for( t = 0; t < ntimepoints; ++t ) 9730 { 9731 /* ignore all time points before the effective horizon */ 9732 if( timepoints[t] <= *hmin ) 9733 continue; 9734 9735 /* ignore all time points after the effective horizon */ 9736 if( timepoints[t] >= *hmax ) 9737 break; 9738 9739 /* check if the current time point does not exceed the capacity w.r.t. worst case resource profile; if so we 9740 * can split the cumulative constraint into two cumulative constraints 9741 */ 9742 if( loads[t] <= capacity ) 9743 { 9744 (*split) = timepoints[t]; 9745 break; 9746 } 9747 } 9748 } 9749 9750 /* free worst case profile */ 9751 SCIPprofileFree(&profile); 9752 9753 return SCIP_OKAY; 9754 } 9755 9756 /** creates and adds a cumulative constraint */ 9757 static 9758 SCIP_RETCODE createConsCumulative( 9759 SCIP* scip, /**< SCIP data structure */ 9760 const char* name, /**< name of constraint */ 9761 int nvars, /**< number of variables (jobs) */ 9762 SCIP_VAR** vars, /**< array of integer variable which corresponds to starting times for a job */ 9763 int* durations, /**< array containing corresponding durations */ 9764 int* demands, /**< array containing corresponding demands */ 9765 int capacity, /**< available cumulative capacity */ 9766 int hmin, /**< left bound of time axis to be considered (including hmin) */ 9767 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 9768 SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP? 9769 * Usually set to TRUE. Set to FALSE for 'lazy constraints'. */ 9770 SCIP_Bool separate, /**< should the constraint be separated during LP processing? 9771 * Usually set to TRUE. */ 9772 SCIP_Bool enforce, /**< should the constraint be enforced during node processing? 9773 * TRUE for model constraints, FALSE for additional, redundant constraints. */ 9774 SCIP_Bool check, /**< should the constraint be checked for feasibility? 9775 * TRUE for model constraints, FALSE for additional, redundant constraints. */ 9776 SCIP_Bool propagate, /**< should the constraint be propagated during node processing? 9777 * Usually set to TRUE. */ 9778 SCIP_Bool local, /**< is constraint only valid locally? 9779 * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */ 9780 SCIP_Bool modifiable, /**< is constraint modifiable (subject to column generation)? 9781 * Usually set to FALSE. In column generation applications, set to TRUE if pricing 9782 * adds coefficients to this constraint. */ 9783 SCIP_Bool dynamic, /**< is constraint subject to aging? 9784 * Usually set to FALSE. Set to TRUE for own cuts which 9785 * are seperated as constraints. */ 9786 SCIP_Bool removable, /**< should the relaxation be removed from the LP due to aging or cleanup? 9787 * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */ 9788 SCIP_Bool stickingatnode /**< should the constraint always be kept at the node where it was added, even 9789 * if it may be moved to a more global node? 9790 * Usually set to FALSE. Set to TRUE to for constraints that represent node data. */ 9791 ) 9792 { 9793 SCIP_CONS* cons; 9794 9795 /* creates cumulative constraint and adds it to problem */ 9796 SCIP_CALL( SCIPcreateConsCumulative(scip, &cons, name, nvars, vars, durations, demands, capacity, 9797 initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode) ); 9798 9799 /* adjust the effective time horizon of the new constraint */ 9800 SCIP_CALL( SCIPsetHminCumulative(scip, cons, hmin) ); 9801 SCIP_CALL( SCIPsetHmaxCumulative(scip, cons, hmax) ); 9802 9803 /* add and release new cumulative constraint */ 9804 SCIP_CALL( SCIPaddCons(scip, cons) ); 9805 SCIP_CALL( SCIPreleaseCons(scip, &cons) ); 9806 9807 return SCIP_OKAY; 9808 } 9809 9810 /** computes the effective horizon and checks if the constraint can be decompsed */ 9811 static 9812 SCIP_RETCODE computeEffectiveHorizon( 9813 SCIP* scip, /**< SCIP data structure */ 9814 SCIP_CONS* cons, /**< cumulative constraint */ 9815 int* ndelconss, /**< pointer to store the number of deleted constraints */ 9816 int* naddconss, /**< pointer to store the number of added constraints */ 9817 int* nchgsides /**< pointer to store the number of changed sides */ 9818 ) 9819 { 9820 SCIP_CONSDATA* consdata; 9821 int hmin; 9822 int hmax; 9823 int split; 9824 9825 consdata = SCIPconsGetData(cons); 9826 assert(consdata != NULL); 9827 9828 if( consdata->nvars <= 1 ) 9829 return SCIP_OKAY; 9830 9831 SCIP_CALL( computeEffectiveHorizonCumulativeCondition(scip, consdata->nvars, consdata->vars, 9832 consdata->durations, consdata->demands, consdata->capacity, &hmin, &hmax, &split) ); 9833 9834 /* check if this time point improves the effective horizon */ 9835 if( consdata->hmin < hmin ) 9836 { 9837 SCIPdebugMsg(scip, "cumulative constraint <%s> adjust hmin <%d> -> <%d>\n", SCIPconsGetName(cons), consdata->hmin, hmin); 9838 9839 consdata->hmin = hmin; 9840 (*nchgsides)++; 9841 } 9842 9843 /* check if this time point improves the effective horizon */ 9844 if( consdata->hmax > hmax ) 9845 { 9846 SCIPdebugMsg(scip, "cumulative constraint <%s> adjust hmax <%d> -> <%d>\n", SCIPconsGetName(cons), consdata->hmax, hmax); 9847 consdata->hmax = hmax; 9848 (*nchgsides)++; 9849 } 9850 9851 /* check if the constraint is redundant */ 9852 if( consdata->hmax <= consdata->hmin ) 9853 { 9854 SCIPdebugMsg(scip, "constraint <%s> is redundant since hmax(%d) <= hmin(%d)\n", 9855 SCIPconsGetName(cons), consdata->hmax, consdata->hmin); 9856 9857 SCIP_CALL( SCIPdelCons(scip, cons) ); 9858 (*ndelconss)++; 9859 } 9860 else if( consdata->hmin < split && split < consdata->hmax ) 9861 { 9862 char name[SCIP_MAXSTRLEN]; 9863 (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "(%s)'", SCIPconsGetName(cons)); 9864 9865 SCIPdebugMsg(scip, "split cumulative constraint <%s>[%d,%d) with %d jobs at time point %d\n", 9866 SCIPconsGetName(cons), consdata->hmin, consdata->hmax, consdata->nvars, split); 9867 9868 assert(split < consdata->hmax); 9869 9870 /* creates cumulative constraint and adds it to problem */ 9871 SCIP_CALL( createConsCumulative(scip, name, consdata->nvars, consdata->vars, 9872 consdata->durations, consdata->demands, consdata->capacity, split, consdata->hmax, 9873 SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), SCIPconsIsEnforced(cons), SCIPconsIsChecked(cons), SCIPconsIsPropagated(cons), 9874 SCIPconsIsLocal(cons), SCIPconsIsModifiable(cons), SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons), SCIPconsIsStickingAtNode(cons)) ); 9875 9876 /* adjust the effective time horizon of the constraint */ 9877 consdata->hmax = split; 9878 9879 assert(consdata->hmin < consdata->hmax); 9880 9881 /* for the statistic we count the number of time we decompose a cumulative constraint */ 9882 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->ndecomps++ ); 9883 (*naddconss)++; 9884 } 9885 9886 return SCIP_OKAY; 9887 } 9888 9889 9890 /** presolve cumulative condition w.r.t. the earlier start times (est) and the hmin of the effective horizon 9891 * 9892 * (1) If the latest completion time (lct) of a job is smaller or equal than hmin, the corresponding job can be removed 9893 * form the constraint. This is the case since it cannot effect any assignment within the effective horizon 9894 * 9895 * (2) If the latest start time (lst) of a job is smaller or equal than hmin it follows that the this jobs can run 9896 * before the effective horizon or it overlaps with the effective horizon such that hmin in included. Hence, the 9897 * down-lock of the corresponding start time variable can be removed. 9898 * 9899 * (3) If the earlier completion time (ect) of a job is smaller or equal than hmin, the cumulative is the only one 9900 * locking the corresponding variable down, and the objective coefficient of the start time variable is not 9901 * negative, than the job can be dual fixed to its earlier start time (est). 9902 * 9903 * (4) If the earlier start time (est) of job is smaller than the hmin, the cumulative is the only one locking the 9904 * corresponding variable down, and the objective coefficient of the start time variable is not negative, than 9905 * removing the values {est+1,...,hmin} form variable domain is dual feasible. 9906 * 9907 * (5) If the earlier start time (est) of job is smaller than the smallest earlier completion times of all other jobs 9908 * (lets denote this with minect), the cumulative is the only one locking the corresponding variable down, and the 9909 * objective coefficient of the start time variable is not negative, than removing the values {est+1,...,minect-1} 9910 * form variable domain is dual feasible. 9911 * 9912 * @note That method does not remove any variable form the arrays. It only marks the variables which are irrelevant for 9913 * the cumulative condition; The deletion has to be done later. 9914 */ 9915 static 9916 SCIP_RETCODE presolveConsEst( 9917 SCIP* scip, /**< SCIP data structure */ 9918 int nvars, /**< number of start time variables (activities) */ 9919 SCIP_VAR** vars, /**< array of start time variables */ 9920 int* durations, /**< array of durations */ 9921 int hmin, /**< left bound of time axis to be considered (including hmin) */ 9922 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 9923 SCIP_Bool* downlocks, /**< array to store if the variable has a down lock, or NULL */ 9924 SCIP_Bool* uplocks, /**< array to store if the variable has an up lock, or NULL */ 9925 SCIP_CONS* cons, /**< underlying constraint, or NULL */ 9926 SCIP_Bool* irrelevants, /**< array mark those variables which are irrelevant for the cumulative condition */ 9927 int* nfixedvars, /**< pointer to store the number of fixed variables */ 9928 int* nchgsides, /**< pointer to store the number of changed sides */ 9929 SCIP_Bool* cutoff /**< buffer to store whether a cutoff is detected */ 9930 ) 9931 { 9932 SCIP_Real* downimpllbs; 9933 SCIP_Real* downimplubs; 9934 SCIP_Real* downproplbs; 9935 SCIP_Real* downpropubs; 9936 SCIP_Real* upimpllbs; 9937 SCIP_Real* upimplubs; 9938 SCIP_Real* upproplbs; 9939 SCIP_Real* uppropubs; 9940 9941 int firstminect; 9942 int secondminect; 9943 int v; 9944 9945 /* get temporary memory for storing probing results needed for step (4) and (5) */ 9946 SCIP_CALL( SCIPallocBufferArray(scip, &downimpllbs, nvars) ); 9947 SCIP_CALL( SCIPallocBufferArray(scip, &downimplubs, nvars) ); 9948 SCIP_CALL( SCIPallocBufferArray(scip, &downproplbs, nvars) ); 9949 SCIP_CALL( SCIPallocBufferArray(scip, &downpropubs, nvars) ); 9950 SCIP_CALL( SCIPallocBufferArray(scip, &upimpllbs, nvars) ); 9951 SCIP_CALL( SCIPallocBufferArray(scip, &upimplubs, nvars) ); 9952 SCIP_CALL( SCIPallocBufferArray(scip, &upproplbs, nvars) ); 9953 SCIP_CALL( SCIPallocBufferArray(scip, &uppropubs, nvars) ); 9954 9955 assert(scip != NULL); 9956 assert(nvars > 1); 9957 assert(cons != NULL); 9958 9959 SCIPdebugMsg(scip, "check for irrelevant variable for cumulative condition (hmin %d) w.r.t. earlier start time\n", hmin); 9960 9961 firstminect = INT_MAX; 9962 secondminect = INT_MAX; 9963 9964 /* compute the two smallest earlier completion times; which are needed for step (5) */ 9965 for( v = 0; v < nvars; ++v ) 9966 { 9967 int ect; 9968 9969 ect = SCIPconvertRealToInt(scip, SCIPvarGetLbGlobal(vars[v])) + durations[v]; 9970 9971 if( ect < firstminect ) 9972 { 9973 secondminect = firstminect; 9974 firstminect = ect; 9975 } 9976 else if( ect < secondminect ) 9977 secondminect = ect; 9978 } 9979 9980 /* loop over all jobs and check if one of the 5 reductions can be applied */ 9981 for( v = 0; v < nvars; ++v ) 9982 { 9983 SCIP_VAR* var; 9984 int duration; 9985 9986 int alternativelb; 9987 int minect; 9988 int est; 9989 int ect; 9990 int lst; 9991 int lct; 9992 9993 var = vars[v]; 9994 assert(var != NULL); 9995 9996 duration = durations[v]; 9997 assert(duration > 0); 9998 9999 /* collect earlier start time (est), earlier completion time (ect), latest start time (lst), and latest completion 10000 * time (lct) 10001 */ 10002 est = SCIPconvertRealToInt(scip, SCIPvarGetLbGlobal(var)); 10003 ect = est + duration; 10004 lst = SCIPconvertRealToInt(scip, SCIPvarGetUbGlobal(var)); 10005 lct = lst + duration; 10006 10007 /* compute the earliest completion time of all remaining jobs */ 10008 if( ect == firstminect ) 10009 minect = secondminect; 10010 else 10011 minect = firstminect; 10012 10013 /* compute potential alternative lower bound (step (4) and (5)) */ 10014 alternativelb = MAX(hmin+1, minect); 10015 alternativelb = MIN(alternativelb, hmax); 10016 10017 if( lct <= hmin ) 10018 { 10019 /* (1) check if the job runs completely before the effective horizon; if so the job can be removed form the 10020 * cumulative condition 10021 */ 10022 SCIPdebugMsg(scip, " variable <%s>[%g,%g] with duration <%d> is irrelevant\n", 10023 SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var), duration); 10024 10025 /* mark variable to be irrelevant */ 10026 irrelevants[v] = TRUE; 10027 10028 /* for the statistic we count the number of jobs which are irrelevant */ 10029 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->nirrelevantjobs++ ); 10030 } 10031 else if( lst <= hmin && SCIPconsIsChecked(cons) ) 10032 { 10033 /* (2) check if the jobs overlaps with the time point hmin if it overlaps at all with the effective horizon; if 10034 * so the down lock can be omitted 10035 */ 10036 10037 assert(downlocks != NULL); 10038 assert(uplocks != NULL); 10039 10040 if( !uplocks[v] ) 10041 { 10042 /* the variables has no up lock and we can also remove the down lock; 10043 * => lst <= hmin and ect >= hmax 10044 * => remove job and reduce capacity by the demand of that job 10045 * 10046 * We mark the job to be deletable. The removement together with the capacity reducion is done later 10047 */ 10048 10049 SCIPdebugMsg(scip, " variables <%s>[%d,%d] (duration <%d>) is irrelevant due to no up lock\n", 10050 SCIPvarGetName(var), ect - duration, lst, duration); 10051 10052 /* mark variable to be irrelevant */ 10053 irrelevants[v] = TRUE; 10054 10055 /* for the statistic we count the number of jobs which always run during the effective horizon */ 10056 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->nalwaysruns++ ); 10057 } 10058 10059 if( downlocks[v] ) 10060 { 10061 SCIPdebugMsg(scip, " remove down lock of variable <%s>[%g,%g] with duration <%d>\n", 10062 SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var), duration); 10063 10064 SCIP_CALL( SCIPunlockVarCons(scip, var, cons, TRUE, FALSE) ); 10065 downlocks[v] = FALSE; 10066 (*nchgsides)++; 10067 10068 /* for the statistic we count the number of removed locks */ 10069 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->nremovedlocks++ ); 10070 } 10071 } 10072 else if( ect <= hmin ) 10073 { 10074 /* (3) check if the job can finish before the effective horizon starts; if so and the job can be fixed to its 10075 * earliest start time (which implies that it finishes before the effective horizon starts), the job can be 10076 * removed form the cumulative condition after it was fixed to its earliest start time 10077 */ 10078 10079 /* job can be removed from the constraint only if the integer start time variable can be fixed to its lower 10080 * bound; 10081 */ 10082 if( downlocks != NULL && SCIPconsIsChecked(cons) ) 10083 { 10084 /* fix integer start time variable if possible to it lower bound */ 10085 SCIP_CALL( fixIntegerVariableLb(scip, var, downlocks[v], nfixedvars) ); 10086 } 10087 10088 if( SCIPvarGetLbGlobal(var) + 0.5 > SCIPvarGetUbGlobal(var) ) 10089 { 10090 SCIPdebugMsg(scip, " variable <%s>[%d,%d] with duration <%d> is irrelevant due to dual fixing wrt EST\n", 10091 SCIPvarGetName(var), ect - duration, lst, duration); 10092 10093 /* after fixing the start time variable to its lower bound, the (new) earliest completion time should be smaller or equal ti hmin */ 10094 assert(SCIPconvertRealToInt(scip, SCIPvarGetLbGlobal(var)) + duration <= hmin); 10095 10096 /* mark variable to be irrelevant */ 10097 irrelevants[v] = TRUE; 10098 10099 /* for the statistic we count the number of jobs which are dual fixed */ 10100 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->ndualfixs++ ); 10101 } 10102 } 10103 else if( est < lst && est < alternativelb && SCIPconsIsChecked(cons) ) 10104 { 10105 assert(downlocks != NULL); 10106 10107 /* check step (4) and (5) */ 10108 10109 /* check if the cumulative constraint is the only one looking this variable down and if the objective function 10110 * is in favor of rounding the variable down 10111 */ 10112 if( SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL) == (int)(downlocks[v]) ) 10113 { 10114 SCIP_Bool roundable; 10115 10116 SCIP_CALL( varMayRoundDown(scip, var, &roundable) ); 10117 10118 if( roundable ) 10119 { 10120 if( alternativelb > lst ) 10121 { 10122 SCIP_Bool infeasible; 10123 SCIP_Bool fixed; 10124 10125 SCIP_CALL( SCIPfixVar(scip, var, SCIPvarGetLbLocal(var), &infeasible, &fixed) ); 10126 assert(!infeasible); 10127 assert(fixed); 10128 10129 (*nfixedvars)++; 10130 10131 /* for the statistic we count the number of jobs which are dual fixed due the information of all cumulative 10132 * constraints 10133 */ 10134 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->ndualbranchs++ ); 10135 } 10136 else 10137 { 10138 SCIP_Bool success; 10139 10140 /* In the current version SCIP, variable domains are single intervals. Meaning that domain holes or not 10141 * representable. To retrieve a potential dual reduction we using probing to check both branches. If one in 10142 * infeasible we can apply the dual reduction; otherwise we do nothing 10143 */ 10144 SCIP_CALL( applyProbingVar(scip, vars, nvars, v, (SCIP_Real) est, (SCIP_Real) alternativelb, 10145 downimpllbs, downimplubs, downproplbs, downpropubs, upimpllbs, upimplubs, upproplbs, uppropubs, 10146 nfixedvars, &success, cutoff) ); 10147 10148 if( success ) 10149 { 10150 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->ndualbranchs++ ); 10151 } 10152 } 10153 } 10154 } 10155 } 10156 10157 SCIPdebugMsg(scip, "********* check variable <%s>[%g,%g] with duration <%d> (hmin %d)\n", 10158 SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var), duration, hmin); 10159 } 10160 10161 /* free temporary memory */ 10162 SCIPfreeBufferArray(scip, &uppropubs); 10163 SCIPfreeBufferArray(scip, &upproplbs); 10164 SCIPfreeBufferArray(scip, &upimplubs); 10165 SCIPfreeBufferArray(scip, &upimpllbs); 10166 SCIPfreeBufferArray(scip, &downpropubs); 10167 SCIPfreeBufferArray(scip, &downproplbs); 10168 SCIPfreeBufferArray(scip, &downimplubs); 10169 SCIPfreeBufferArray(scip, &downimpllbs); 10170 10171 return SCIP_OKAY; 10172 } 10173 10174 /** presolve cumulative condition w.r.t. the latest completion times (lct) and the hmax of the effective horizon 10175 * 10176 * (1) If the earliest start time (est) of a job is larger or equal than hmax, the corresponding job can be removed 10177 * form the constraint. This is the case since it cannot effect any assignment within the effective horizon 10178 * 10179 * (2) If the earliest completion time (ect) of a job is larger or equal than hmax it follows that the this jobs can run 10180 * before the effective horizon or it overlaps with the effective horizon such that hmax in included. Hence, the 10181 * up-lock of the corresponding start time variable can be removed. 10182 * 10183 * (3) If the latest start time (lst) of a job is larger or equal than hmax, the cumulative is the only one 10184 * locking the corresponding variable up, and the objective coefficient of the start time variable is not 10185 * positive, than the job can be dual fixed to its latest start time (lst). 10186 * 10187 * (4) If the latest completion time (lct) of job is larger than the hmax, the cumulative is the only one locking the 10188 * corresponding variable up, and the objective coefficient of the start time variable is not positive, than 10189 * removing the values {hmax - p_j, ..., lst-1} form variable domain is dual feasible (p_j is the processing time 10190 * of the corresponding job). 10191 10192 * (5) If the latest completion time (lct) of job is smaller than the largerst latest start time of all other jobs 10193 * (lets denote this with maxlst), the cumulative is the only one locking the corresponding variable up, and the 10194 * objective coefficient of the start time variable is not positive, than removing the values {maxlst - p_j + 1, 10195 * ..., lst-1} form variable domain is dual feasible (p_j is the processing time of the corresponding job). 10196 * 10197 * @note That method does not remove any variable form the arrays. It only marks the variables which are irrelevant for 10198 * the cumulative condition; The deletion has to be done later. 10199 */ 10200 static 10201 SCIP_RETCODE presolveConsLct( 10202 SCIP* scip, /**< SCIP data structure */ 10203 int nvars, /**< number of start time variables (activities) */ 10204 SCIP_VAR** vars, /**< array of start time variables */ 10205 int* durations, /**< array of durations */ 10206 int hmin, /**< left bound of time axis to be considered (including hmin) */ 10207 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 10208 SCIP_Bool* downlocks, /**< array to store if the variable has a down lock, or NULL */ 10209 SCIP_Bool* uplocks, /**< array to store if the variable has an up lock, or NULL */ 10210 SCIP_CONS* cons, /**< underlying constraint, or NULL */ 10211 SCIP_Bool* irrelevants, /**< array mark those variables which are irrelevant for the cumulative condition */ 10212 int* nfixedvars, /**< pointer to counter which is increased by the number of deduced variable fixations */ 10213 int* nchgsides, /**< pointer to store the number of changed sides */ 10214 SCIP_Bool* cutoff /**< buffer to store whether a cutoff is detected */ 10215 ) 10216 { 10217 SCIP_Real* downimpllbs; 10218 SCIP_Real* downimplubs; 10219 SCIP_Real* downproplbs; 10220 SCIP_Real* downpropubs; 10221 SCIP_Real* upimpllbs; 10222 SCIP_Real* upimplubs; 10223 SCIP_Real* upproplbs; 10224 SCIP_Real* uppropubs; 10225 10226 int firstmaxlst; 10227 int secondmaxlst; 10228 int v; 10229 10230 /* get temporary memory for storing probing results needed for step (4) and (5) */ 10231 SCIP_CALL( SCIPallocBufferArray(scip, &downimpllbs, nvars) ); 10232 SCIP_CALL( SCIPallocBufferArray(scip, &downimplubs, nvars) ); 10233 SCIP_CALL( SCIPallocBufferArray(scip, &downproplbs, nvars) ); 10234 SCIP_CALL( SCIPallocBufferArray(scip, &downpropubs, nvars) ); 10235 SCIP_CALL( SCIPallocBufferArray(scip, &upimpllbs, nvars) ); 10236 SCIP_CALL( SCIPallocBufferArray(scip, &upimplubs, nvars) ); 10237 SCIP_CALL( SCIPallocBufferArray(scip, &upproplbs, nvars) ); 10238 SCIP_CALL( SCIPallocBufferArray(scip, &uppropubs, nvars) ); 10239 10240 assert(scip != NULL); 10241 assert(nvars > 1); 10242 assert(cons != NULL); 10243 10244 SCIPdebugMsg(scip, "check for irrelevant variable for cumulative condition (hmax %d) w.r.t. latest completion time\n", hmax); 10245 10246 firstmaxlst = INT_MIN; 10247 secondmaxlst = INT_MIN; 10248 10249 /* compute the two largest latest start times; which are needed for step (5) */ 10250 for( v = 0; v < nvars; ++v ) 10251 { 10252 int lst; 10253 10254 lst = SCIPconvertRealToInt(scip, SCIPvarGetUbGlobal(vars[v])); 10255 10256 if( lst > firstmaxlst ) 10257 { 10258 secondmaxlst = firstmaxlst; 10259 firstmaxlst = lst; 10260 } 10261 else if( lst > secondmaxlst ) 10262 secondmaxlst = lst; 10263 } 10264 10265 /* loop over all jobs and check if one of the 5 reductions can be applied */ 10266 for( v = 0; v < nvars; ++v ) 10267 { 10268 SCIP_VAR* var; 10269 int duration; 10270 10271 int alternativeub; 10272 int maxlst; 10273 int est; 10274 int ect; 10275 int lst; 10276 10277 var = vars[v]; 10278 assert(var != NULL); 10279 10280 duration = durations[v]; 10281 assert(duration > 0); 10282 10283 /* collect earlier start time (est), earlier completion time (ect), latest start time (lst), and latest completion 10284 * time (lct) 10285 */ 10286 est = SCIPconvertRealToInt(scip, SCIPvarGetLbGlobal(var)); 10287 ect = est + duration; 10288 lst = SCIPconvertRealToInt(scip, SCIPvarGetUbGlobal(var)); 10289 10290 /* compute the latest start time of all remaining jobs */ 10291 if( lst == firstmaxlst ) 10292 maxlst = secondmaxlst; 10293 else 10294 maxlst = firstmaxlst; 10295 10296 /* compute potential alternative upper bound (step (4) and (5)) */ 10297 alternativeub = MIN(hmax - 1, maxlst) - duration; 10298 alternativeub = MAX(alternativeub, hmin); 10299 10300 if( est >= hmax ) 10301 { 10302 /* (1) check if the job runs completely after the effective horizon; if so the job can be removed form the 10303 * cumulative condition 10304 */ 10305 SCIPdebugMsg(scip, " variable <%s>[%g,%g] with duration <%d> is irrelevant\n", 10306 SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var), duration); 10307 10308 /* mark variable to be irrelevant */ 10309 irrelevants[v] = TRUE; 10310 10311 /* for the statistic we count the number of jobs which are irrelevant */ 10312 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->nirrelevantjobs++ ); 10313 } 10314 else if( ect >= hmax && SCIPconsIsChecked(cons) ) 10315 { 10316 assert(downlocks != NULL); 10317 assert(uplocks != NULL); 10318 10319 /* (2) check if the jobs overlaps with the time point hmax if it overlaps at all with the effective horizon; if 10320 * so the up lock can be omitted 10321 */ 10322 10323 if( !downlocks[v] ) 10324 { 10325 /* the variables has no down lock and we can also remove the up lock; 10326 * => lst <= hmin and ect >= hmax 10327 * => remove job and reduce capacity by the demand of that job 10328 */ 10329 SCIPdebugMsg(scip, " variables <%s>[%d,%d] with duration <%d> is irrelevant due to no down lock\n", 10330 SCIPvarGetName(var), est, lst, duration); 10331 10332 /* mark variable to be irrelevant */ 10333 irrelevants[v] = TRUE; 10334 10335 /* for the statistic we count the number of jobs which always run during the effective horizon */ 10336 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->nalwaysruns++ ); 10337 } 10338 10339 if( uplocks[v] ) 10340 { 10341 SCIPdebugMsg(scip, " remove up lock of variable <%s>[%g,%g] with duration <%d>\n", 10342 SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var), duration); 10343 10344 SCIP_CALL( SCIPunlockVarCons(scip, var, cons, FALSE, TRUE) ); 10345 uplocks[v] = FALSE; 10346 (*nchgsides)++; 10347 10348 /* for the statistic we count the number of removed locks */ 10349 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->nremovedlocks++ ); 10350 } 10351 } 10352 else if( lst >= hmax ) 10353 { 10354 /* (3) check if the job can start after the effective horizon finishes; if so and the job can be fixed to its 10355 * latest start time (which implies that it starts after the effective horizon finishes), the job can be 10356 * removed form the cumulative condition after it was fixed to its latest start time 10357 */ 10358 10359 /* job can be removed from the constraint only if the integer start time variable can be fixed to its upper 10360 * bound 10361 */ 10362 if( uplocks != NULL && SCIPconsIsChecked(cons) ) 10363 { 10364 /* fix integer start time variable if possible to its upper bound */ 10365 SCIP_CALL( fixIntegerVariableUb(scip, var, uplocks[v], nfixedvars) ); 10366 } 10367 10368 if( SCIPvarGetLbGlobal(var) + 0.5 > SCIPvarGetUbGlobal(var) ) 10369 { 10370 SCIPdebugMsg(scip, " variable <%s>[%d,%d] with duration <%d> is irrelevant due to dual fixing wrt LCT\n", 10371 SCIPvarGetName(var), est, lst, duration); 10372 10373 /* after fixing the start time variable to its upper bound, the (new) latest start time should be greather or equal ti hmax */ 10374 assert(SCIPconvertRealToInt(scip, SCIPvarGetUbGlobal(var)) >= hmax); 10375 10376 /* mark variable to be irrelevant */ 10377 irrelevants[v] = TRUE; 10378 10379 /* for the statistic we count the number of jobs which are dual fixed */ 10380 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->ndualfixs++ ); 10381 } 10382 } 10383 else if( est < lst && lst > alternativeub && SCIPconsIsChecked(cons) ) 10384 { 10385 assert(uplocks != NULL); 10386 10387 /* check step (4) and (5) */ 10388 10389 /* check if the cumulative constraint is the only one looking this variable down and if the objective function 10390 * is in favor of rounding the variable down 10391 */ 10392 if( SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL) == (int)(uplocks[v]) ) 10393 { 10394 SCIP_Bool roundable; 10395 10396 SCIP_CALL( varMayRoundUp(scip, var, &roundable) ); 10397 10398 if( roundable ) 10399 { 10400 if( alternativeub < est ) 10401 { 10402 SCIP_Bool infeasible; 10403 SCIP_Bool fixed; 10404 10405 SCIP_CALL( SCIPfixVar(scip, var, SCIPvarGetUbLocal(var), &infeasible, &fixed) ); 10406 assert(!infeasible); 10407 assert(fixed); 10408 10409 (*nfixedvars)++; 10410 10411 /* for the statistic we count the number of jobs which are dual fixed due the information of all cumulative 10412 * constraints 10413 */ 10414 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->ndualbranchs++ ); 10415 } 10416 else 10417 { 10418 SCIP_Bool success; 10419 10420 /* In the current version SCIP, variable domains are single intervals. Meaning that domain holes or not 10421 * representable. To retrieve a potential dual reduction we using probing to check both branches. If one 10422 * in infeasible we can apply the dual reduction; otherwise we do nothing 10423 */ 10424 SCIP_CALL( applyProbingVar(scip, vars, nvars, v, (SCIP_Real) alternativeub, (SCIP_Real) lst, 10425 downimpllbs, downimplubs, downproplbs, downpropubs, upimpllbs, upimplubs, upproplbs, uppropubs, 10426 nfixedvars, &success, cutoff) ); 10427 10428 if( success ) 10429 { 10430 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->ndualbranchs++ ); 10431 } 10432 } 10433 } 10434 } 10435 } 10436 } 10437 10438 /* free temporary memory */ 10439 SCIPfreeBufferArray(scip, &uppropubs); 10440 SCIPfreeBufferArray(scip, &upproplbs); 10441 SCIPfreeBufferArray(scip, &upimplubs); 10442 SCIPfreeBufferArray(scip, &upimpllbs); 10443 SCIPfreeBufferArray(scip, &downpropubs); 10444 SCIPfreeBufferArray(scip, &downproplbs); 10445 SCIPfreeBufferArray(scip, &downimplubs); 10446 SCIPfreeBufferArray(scip, &downimpllbs); 10447 10448 return SCIP_OKAY; 10449 } 10450 10451 /** presolve cumulative constraint w.r.t. the boundary of the effective horizon */ 10452 static 10453 SCIP_RETCODE presolveConsEffectiveHorizon( 10454 SCIP* scip, /**< SCIP data structure */ 10455 SCIP_CONS* cons, /**< cumulative constraint */ 10456 int* nfixedvars, /**< pointer to store the number of fixed variables */ 10457 int* nchgcoefs, /**< pointer to store the number of changed coefficients */ 10458 int* nchgsides, /**< pointer to store the number of changed sides */ 10459 SCIP_Bool* cutoff /**< pointer to store if a cutoff was detected */ 10460 ) 10461 { 10462 SCIP_CONSDATA* consdata; 10463 SCIP_Bool* irrelevants; 10464 int nvars; 10465 int v; 10466 10467 assert(scip != NULL); 10468 assert(cons != NULL); 10469 assert(!(*cutoff)); 10470 10471 consdata = SCIPconsGetData(cons); 10472 assert(consdata != NULL); 10473 10474 nvars = consdata->nvars; 10475 10476 if( nvars <= 1 ) 10477 return SCIP_OKAY; 10478 10479 SCIP_CALL( SCIPallocBufferArray(scip, &irrelevants, nvars) ); 10480 BMSclearMemoryArray(irrelevants, nvars); 10481 10482 /* presolve constraint form the earlier start time point of view */ 10483 SCIP_CALL( presolveConsEst(scip, nvars, consdata->vars, consdata->durations, 10484 consdata->hmin, consdata->hmax, consdata->downlocks, consdata->uplocks, cons, 10485 irrelevants, nfixedvars, nchgsides, cutoff) ); 10486 10487 /* presolve constraint form the latest completion time point of view */ 10488 SCIP_CALL( presolveConsLct(scip, nvars, consdata->vars, consdata->durations, 10489 consdata->hmin, consdata->hmax, consdata->downlocks, consdata->uplocks, cons, 10490 irrelevants, nfixedvars, nchgsides, cutoff) ); 10491 10492 /* remove variables from the cumulative constraint which are marked to be deleted; we need to that in the reverse 10493 * order to ensure a correct behaviour 10494 */ 10495 for( v = nvars-1; v >= 0; --v ) 10496 { 10497 if( irrelevants[v] ) 10498 { 10499 SCIP_VAR* var; 10500 int ect; 10501 int lst; 10502 10503 var = consdata->vars[v]; 10504 assert(var != NULL); 10505 10506 ect = SCIPconvertRealToInt(scip, SCIPvarGetLbGlobal(var)) + consdata->durations[v]; 10507 lst = SCIPconvertRealToInt(scip, SCIPvarGetUbGlobal(var)); 10508 10509 /* check if the jobs runs completely during the effective horizon */ 10510 if( lst <= consdata->hmin && ect >= consdata->hmax ) 10511 { 10512 if( consdata->capacity < consdata->demands[v] ) 10513 { 10514 *cutoff = TRUE; 10515 break; 10516 } 10517 10518 consdata->capacity -= consdata->demands[v]; 10519 consdata->varbounds = FALSE; 10520 } 10521 10522 SCIP_CALL( consdataDeletePos(scip, consdata, cons, v) ); 10523 (*nchgcoefs)++; 10524 } 10525 } 10526 10527 SCIPfreeBufferArray(scip, &irrelevants); 10528 10529 return SCIP_OKAY; 10530 } 10531 10532 /** stores all demands which are smaller than the capacity of those jobs that are running at 'curtime' */ 10533 static 10534 void collectDemands( 10535 SCIP* scip, /**< SCIP data structure */ 10536 SCIP_CONSDATA* consdata, /**< constraint data */ 10537 int* startindices, /**< permutation with rspect to the start times */ 10538 int curtime, /**< current point in time */ 10539 int nstarted, /**< number of jobs that start before the curtime or at curtime */ 10540 int nfinished, /**< number of jobs that finished before curtime or at curtime */ 10541 SCIP_Longint** demands, /**< pointer to array storing the demands */ 10542 int* ndemands /**< pointer to store the number of different demands */ 10543 ) 10544 { 10545 int startindex; 10546 int ncountedvars; 10547 10548 assert(demands != NULL); 10549 assert(ndemands != NULL); 10550 10551 ncountedvars = 0; 10552 startindex = nstarted - 1; 10553 10554 *ndemands = 0; 10555 10556 /* search for the (nstarted - nfinished) jobs which are active at curtime */ 10557 while( nstarted - nfinished > ncountedvars ) 10558 { 10559 SCIP_VAR* var; 10560 int endtime; 10561 int varidx; 10562 10563 /* collect job information */ 10564 varidx = startindices[startindex]; 10565 assert(varidx >= 0 && varidx < consdata->nvars); 10566 10567 var = consdata->vars[varidx]; 10568 assert(var != NULL); 10569 10570 endtime = SCIPconvertRealToInt(scip, SCIPvarGetUbGlobal(var)) + consdata->durations[varidx]; 10571 10572 /* check the end time of this job is larger than the curtime; in this case the job is still running */ 10573 if( endtime > curtime ) 10574 { 10575 if( consdata->demands[varidx] < consdata->capacity ) 10576 { 10577 (*demands)[*ndemands] = consdata->demands[varidx]; 10578 (*ndemands)++; 10579 } 10580 ncountedvars++; 10581 } 10582 10583 startindex--; 10584 } 10585 } 10586 10587 /** this method creates a row for time point curtime which insures the capacity restriction of the cumulative 10588 * constraint 10589 */ 10590 static 10591 SCIP_RETCODE getHighestCapacityUsage( 10592 SCIP* scip, /**< SCIP data structure */ 10593 SCIP_CONS* cons, /**< constraint to be checked */ 10594 int* startindices, /**< permutation with rspect to the start times */ 10595 int curtime, /**< current point in time */ 10596 int nstarted, /**< number of jobs that start before the curtime or at curtime */ 10597 int nfinished, /**< number of jobs that finished before curtime or at curtime */ 10598 int* bestcapacity /**< pointer to store the maximum possible capacity usage */ 10599 ) 10600 { 10601 SCIP_CONSDATA* consdata; 10602 SCIP_Longint* demands; 10603 SCIP_Real* profits; 10604 int* items; 10605 int ndemands; 10606 SCIP_Bool success; 10607 SCIP_Real solval; 10608 int j; 10609 assert(nstarted > nfinished); 10610 10611 consdata = SCIPconsGetData(cons); 10612 assert(consdata != NULL); 10613 assert(consdata->nvars > 0); 10614 assert(consdata->capacity > 0); 10615 10616 SCIP_CALL( SCIPallocBufferArray(scip, &demands, consdata->nvars) ); 10617 ndemands = 0; 10618 10619 /* get demand array to initialize knapsack problem */ 10620 collectDemands(scip, consdata, startindices, curtime, nstarted, nfinished, &demands, &ndemands); 10621 10622 /* create array for profits */ 10623 SCIP_CALL( SCIPallocBufferArray(scip, &profits, ndemands) ); 10624 SCIP_CALL( SCIPallocBufferArray(scip, &items, ndemands) ); 10625 for( j = 0; j < ndemands; ++j ) 10626 { 10627 profits[j] = (SCIP_Real) demands[j]; 10628 items[j] = j;/* this is only a dummy value*/ 10629 } 10630 10631 /* solve knapsack problem and get maximum capacity usage <= capacity */ 10632 SCIP_CALL( SCIPsolveKnapsackExactly(scip, ndemands, demands, profits, (SCIP_Longint)consdata->capacity, 10633 items, NULL, NULL, NULL, NULL, &solval, &success) ); 10634 10635 assert(SCIPisFeasIntegral(scip, solval)); 10636 10637 /* store result */ 10638 *bestcapacity = SCIPconvertRealToInt(scip, solval); 10639 10640 SCIPfreeBufferArray(scip, &items); 10641 SCIPfreeBufferArray(scip, &profits); 10642 SCIPfreeBufferArray(scip, &demands); 10643 10644 return SCIP_OKAY; 10645 } 10646 10647 /** try to tighten the capacity 10648 * -- using DP for knapsack, we find the maximum possible capacity usage 10649 * -- neglects hmin and hmax, such that it is also able to check solutions globally 10650 */ 10651 static 10652 SCIP_RETCODE tightenCapacity( 10653 SCIP* scip, /**< SCIP data structure */ 10654 SCIP_CONS* cons, /**< cumulative constraint */ 10655 int* nchgcoefs, /**< pointer to count total number of changed coefficients */ 10656 int* nchgsides /**< pointer to store the number of changed sides */ 10657 ) 10658 { 10659 SCIP_CONSDATA* consdata; 10660 int* starttimes; /* stores when each job is starting */ 10661 int* endtimes; /* stores when each job ends */ 10662 int* startindices; /* we will sort the startsolvalues, thus we need to know wich index of a job it corresponds to */ 10663 int* endindices; /* we will sort the endsolvalues, thus we need to know wich index of a job it corresponds to */ 10664 10665 int nvars; /* number of activities for this constraint */ 10666 int freecapacity; /* remaining capacity */ 10667 int curtime; /* point in time which we are just checking */ 10668 int endindex; /* index of endsolvalues with: endsolvalues[endindex] > curtime */ 10669 10670 int bestcapacity; 10671 10672 int j; 10673 10674 assert(scip != NULL); 10675 assert(cons != NULL); 10676 assert(nchgsides != NULL); 10677 10678 consdata = SCIPconsGetData(cons); 10679 assert(consdata != NULL); 10680 10681 nvars = consdata->nvars; 10682 10683 /* if no activities are associated with this cumulative or the capacity is 1, then this constraint is redundant */ 10684 if( nvars <= 1 || consdata->capacity <= 1 ) 10685 return SCIP_OKAY; 10686 10687 assert(consdata->vars != NULL); 10688 10689 SCIPdebugMsg(scip, "try to tighten capacity for cumulative constraint <%s> with capacity %d\n", 10690 SCIPconsGetName(cons), consdata->capacity); 10691 10692 SCIP_CALL( SCIPallocBufferArray(scip, &starttimes, nvars) ); 10693 SCIP_CALL( SCIPallocBufferArray(scip, &endtimes, nvars) ); 10694 SCIP_CALL( SCIPallocBufferArray(scip, &startindices, nvars) ); 10695 SCIP_CALL( SCIPallocBufferArray(scip, &endindices, nvars) ); 10696 10697 /* create event point arrays */ 10698 createSortedEventpoints(scip, nvars, consdata->vars, consdata->durations, 10699 starttimes, endtimes, startindices, endindices, FALSE); 10700 10701 bestcapacity = 1; 10702 endindex = 0; 10703 freecapacity = consdata->capacity; 10704 10705 /* check each startpoint of a job whether the capacity is kept or not */ 10706 for( j = 0; j < nvars && bestcapacity < consdata->capacity; ++j ) 10707 { 10708 curtime = starttimes[j]; 10709 SCIPdebugMsg(scip, "look at %d-th job with start %d\n", j, curtime); 10710 10711 /* remove the capacity requirments for all job which start at the curtime */ 10712 subtractStartingJobDemands(consdata, curtime, starttimes, startindices, &freecapacity, &j, nvars); 10713 10714 /* add the capacity requirments for all job which end at the curtime */ 10715 addEndingJobDemands(consdata, curtime, endtimes, endindices, &freecapacity, &endindex, nvars); 10716 10717 assert(freecapacity <= consdata->capacity); 10718 assert(endindex <= nvars); 10719 10720 /* endindex - points to the next job which will finish */ 10721 /* j - points to the last job that has been released */ 10722 10723 /* check point in time when capacity is exceeded (here, a knapsack problem must be solved) */ 10724 if( freecapacity < 0 ) 10725 { 10726 int newcapacity; 10727 10728 newcapacity = 1; 10729 10730 /* get best possible upper bound on capacity usage */ 10731 SCIP_CALL( getHighestCapacityUsage(scip, cons, startindices, curtime, j+1, endindex, &newcapacity) ); 10732 10733 /* update bestcapacity */ 10734 bestcapacity = MAX(bestcapacity, newcapacity); 10735 SCIPdebugMsg(scip, "after highest cap usage: bestcapacity = %d\n", bestcapacity); 10736 } 10737 10738 /* also those points in time, where the capacity limit is not exceeded, must be taken into account */ 10739 if( freecapacity > 0 && freecapacity != consdata->capacity ) 10740 { 10741 bestcapacity = MAX(bestcapacity, consdata->capacity - freecapacity); 10742 SCIPdebugMsg(scip, "after peak < cap: bestcapacity = %d\n", bestcapacity); 10743 } 10744 10745 /* capacity cannot be decreased if the demand sum over more than one job equals the capacity */ 10746 if( freecapacity == 0 && consdata->demands[startindices[j]] < consdata->capacity) 10747 { 10748 /* if demands[startindices[j]] == cap then exactly that job is running */ 10749 SCIPdebugMsg(scip, "--> cannot decrease capacity since sum equals capacity\n"); 10750 bestcapacity = consdata->capacity; 10751 break; 10752 } 10753 } /*lint --e{850}*/ 10754 10755 /* free all buffer arrays */ 10756 SCIPfreeBufferArray(scip, &endindices); 10757 SCIPfreeBufferArray(scip, &startindices); 10758 SCIPfreeBufferArray(scip, &endtimes); 10759 SCIPfreeBufferArray(scip, &starttimes); 10760 10761 /* check whether capacity can be tightened and whether demands need to be adjusted */ 10762 if( bestcapacity < consdata->capacity ) 10763 { 10764 SCIPdebug( int oldnchgcoefs = *nchgcoefs; ) 10765 10766 SCIPdebugMsg(scip, "+-+-+-+-+-+ --> CHANGE capacity of cons<%s> from %d to %d\n", 10767 SCIPconsGetName(cons), consdata->capacity, bestcapacity); 10768 10769 for( j = 0; j < nvars; ++j ) 10770 { 10771 if( consdata->demands[j] == consdata->capacity ) 10772 { 10773 consdata->demands[j] = bestcapacity; 10774 (*nchgcoefs)++; 10775 } 10776 } 10777 10778 consdata->capacity = bestcapacity; 10779 (*nchgsides)++; 10780 10781 SCIPdebug( SCIPdebugMsg(scip, "; changed additionally %d coefficients\n", (*nchgcoefs) - oldnchgcoefs); ) 10782 10783 consdata->varbounds = FALSE; 10784 } 10785 10786 return SCIP_OKAY; 10787 } 10788 10789 /** tries to change coefficients: 10790 * demand_j < cap && all other parallel jobs in conflict 10791 * ==> set demand_j := cap 10792 */ 10793 static 10794 SCIP_RETCODE tightenCoefs( 10795 SCIP* scip, /**< SCIP data structure */ 10796 SCIP_CONS* cons, /**< cumulative constraint */ 10797 int* nchgcoefs /**< pointer to count total number of changed coefficients */ 10798 ) 10799 { 10800 SCIP_CONSDATA* consdata; 10801 int nvars; 10802 int j; 10803 int oldnchgcoefs; 10804 int mindemand; 10805 10806 assert(scip != NULL); 10807 assert(cons != NULL); 10808 assert(nchgcoefs != NULL); 10809 10810 /* get constraint data for some parameter testings only! */ 10811 consdata = SCIPconsGetData(cons); 10812 assert(consdata != NULL); 10813 10814 nvars = consdata->nvars; 10815 oldnchgcoefs = *nchgcoefs; 10816 10817 if( nvars <= 0 ) 10818 return SCIP_OKAY; 10819 10820 /* PRE1: 10821 * check all jobs j whether: r_j + r_min > capacity holds 10822 * if so: adjust r_j to capacity 10823 */ 10824 mindemand = consdata->demands[0]; 10825 for( j = 0; j < nvars; ++j ) 10826 { 10827 mindemand = MIN(mindemand, consdata->demands[j]); 10828 } 10829 10830 /*check each job */ 10831 for( j = 0; j < nvars; ++j ) 10832 { 10833 if( mindemand + consdata->demands[j] > consdata->capacity && consdata->demands[j] < consdata->capacity ) 10834 { 10835 SCIPdebugMsg(scip, "+-+-+-+-+-+change demand of var<%s> from %d to capacity %d\n", SCIPvarGetName(consdata->vars[j]), 10836 consdata->demands[j], consdata->capacity); 10837 consdata->demands[j] = consdata->capacity; 10838 (*nchgcoefs)++; 10839 } 10840 } 10841 10842 /* PRE2: 10843 * check for each job (with d_j < cap) 10844 * whether it is disjunctive to all others over the time horizon 10845 */ 10846 for( j = 0; j < nvars; ++j ) 10847 { 10848 SCIP_Bool chgcoef; 10849 int est_j; 10850 int lct_j; 10851 int i; 10852 10853 assert(consdata->demands[j] <= consdata->capacity); 10854 10855 if( consdata->demands[j] == consdata->capacity ) 10856 continue; 10857 10858 chgcoef = TRUE; 10859 10860 est_j = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(consdata->vars[j])); 10861 lct_j = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(consdata->vars[j])) + consdata->durations[j]; 10862 10863 for( i = 0; i < nvars; ++i ) 10864 { 10865 int est_i; 10866 int lct_i; 10867 10868 if( i == j ) 10869 continue; 10870 10871 est_i = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(consdata->vars[i])); 10872 lct_i = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(consdata->vars[i])) + consdata->durations[i]; 10873 10874 if( est_i >= lct_j || est_j >= lct_i ) 10875 continue; 10876 10877 if( consdata->demands[j] + consdata->demands[i] <= consdata->capacity ) 10878 { 10879 chgcoef = FALSE; 10880 break; 10881 } 10882 } 10883 10884 if( chgcoef ) 10885 { 10886 SCIPdebugMsg(scip, "+-+-+-+-+-+change demand of var<%s> from %d to capacity %d\n", SCIPvarGetName(consdata->vars[j]), 10887 consdata->demands[j], consdata->capacity); 10888 consdata->demands[j] = consdata->capacity; 10889 (*nchgcoefs)++; 10890 } 10891 } 10892 10893 if( (*nchgcoefs) > oldnchgcoefs ) 10894 { 10895 SCIPdebugMsg(scip, "+-+-+-+-+-+changed %d coefficients of variables of cumulative constraint<%s>\n", 10896 (*nchgcoefs) - oldnchgcoefs, SCIPconsGetName(cons)); 10897 } 10898 10899 return SCIP_OKAY; 10900 } 10901 10902 #if 0 10903 /** try to reformulate constraint by replacing certain jobs */ 10904 static 10905 SCIP_RETCODE reformulateCons( 10906 SCIP* scip, /**< SCIP data structure */ 10907 SCIP_CONS* cons, /**< cumulative constraint */ 10908 int* naggrvars /**< pointer to store the number of aggregated variables */ 10909 ) 10910 { 10911 SCIP_CONSDATA* consdata; 10912 int hmin; 10913 int hmax; 10914 int nvars; 10915 int v; 10916 10917 consdata = SCIPconsGetData(cons); 10918 assert(cons != NULL); 10919 10920 nvars = consdata->nvars; 10921 assert(nvars > 1); 10922 10923 hmin = consdata->hmin; 10924 hmax = consdata->hmax; 10925 assert(hmin < hmax); 10926 10927 for( v = 0; v < nvars; ++v ) 10928 { 10929 SCIP_VAR* var; 10930 int duration; 10931 int est; 10932 int ect; 10933 int lst; 10934 int lct; 10935 10936 var = consdata->vars[v]; 10937 assert(var != NULL); 10938 10939 duration = consdata->durations[v]; 10940 10941 est = SCIPconvertRealToInt(scip, SCIPvarGetLbGlobal(var)); 10942 ect = est + duration; 10943 lst = SCIPconvertRealToInt(scip, SCIPvarGetUbGlobal(var)); 10944 lct = lst + duration; 10945 10946 /* jobs for which the core [lst,ect) contains [hmin,hmax) should be removed already */ 10947 assert(lst > hmin || ect < hmax); 10948 10949 if( lst <= hmin && est < hmin - lct + MIN(hmin, ect) ) 10950 { 10951 SCIP_VAR* aggrvar; 10952 char name[SCIP_MAXSTRLEN]; 10953 SCIP_Bool infeasible; 10954 SCIP_Bool redundant; 10955 SCIP_Bool aggregated; 10956 int shift; 10957 10958 shift = est - (hmin - lct + MIN(hmin, ect)); 10959 assert(shift > 0); 10960 lst = hmin; 10961 duration = hmin - lct; 10962 10963 SCIPdebugMsg(scip, "replace variable <%s>[%g,%g] by [%d,%d]\n", 10964 SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var), est + shift, lst); 10965 10966 (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_aggr", SCIPvarGetName(var)); 10967 SCIP_CALL( SCIPcreateVar(scip, &aggrvar, name, (SCIP_Real)(est+shift), (SCIP_Real)lst, 0.0, SCIPvarGetType(var), 10968 SCIPvarIsInitial(var), SCIPvarIsRemovable(var), NULL, NULL, NULL, NULL, NULL) ); 10969 SCIP_CALL( SCIPaddVar(scip, var) ); 10970 SCIP_CALL( SCIPaggregateVars(scip, var, aggrvar, 1.0, -1.0, (SCIP_Real)shift, &infeasible, &redundant, &aggregated) ); 10971 10972 assert(!infeasible); 10973 assert(!redundant); 10974 assert(aggregated); 10975 10976 /* replace variable */ 10977 consdata->durations[v] = duration; 10978 consdata->vars[v] = aggrvar; 10979 10980 /* remove and add locks */ 10981 SCIP_CALL( SCIPunlockVarCons(scip, var, cons, consdata->downlocks[v], consdata->uplocks[v]) ); 10982 SCIP_CALL( SCIPlockVarCons(scip, var, cons, consdata->downlocks[v], consdata->uplocks[v]) ); 10983 10984 SCIP_CALL( SCIPreleaseVar(scip, &aggrvar) ); 10985 10986 (*naggrvars)++; 10987 } 10988 } 10989 10990 return SCIP_OKAY; 10991 } 10992 #endif 10993 10994 /** creare a disjunctive constraint which contains all jobs which cannot run in parallel */ 10995 static 10996 SCIP_RETCODE createDisjuctiveCons( 10997 SCIP* scip, /**< SCIP data structure */ 10998 SCIP_CONS* cons, /**< cumulative constraint */ 10999 int* naddconss /**< pointer to store the number of added constraints */ 11000 ) 11001 { 11002 SCIP_CONSDATA* consdata; 11003 SCIP_VAR** vars; 11004 int* durations; 11005 int* demands; 11006 int capacity; 11007 int halfcapacity; 11008 int mindemand; 11009 int nvars; 11010 int v; 11011 11012 consdata = SCIPconsGetData(cons); 11013 assert(consdata != NULL); 11014 11015 capacity = consdata->capacity; 11016 11017 if( capacity == 1 ) 11018 return SCIP_OKAY; 11019 11020 SCIP_CALL( SCIPallocBufferArray(scip, &vars, consdata->nvars) ); 11021 SCIP_CALL( SCIPallocBufferArray(scip, &durations, consdata->nvars) ); 11022 SCIP_CALL( SCIPallocBufferArray(scip, &demands, consdata->nvars) ); 11023 11024 halfcapacity = capacity / 2; 11025 mindemand = consdata->capacity; 11026 nvars = 0; 11027 11028 /* collect all jobs with demand larger than half of the capacity */ 11029 for( v = 0; v < consdata->nvars; ++v ) 11030 { 11031 if( consdata->demands[v] > halfcapacity ) 11032 { 11033 vars[nvars] = consdata->vars[v]; 11034 demands[nvars] = 1; 11035 durations[nvars] = consdata->durations[v]; 11036 nvars++; 11037 11038 mindemand = MIN(mindemand, consdata->demands[v]); 11039 } 11040 } 11041 11042 if( nvars > 0 ) 11043 { 11044 /* add all jobs which has a demand smaller than one half of the capacity but together with the smallest collected 11045 * job is still to large to be scheduled in parallel 11046 */ 11047 for( v = 0; v < consdata->nvars; ++v ) 11048 { 11049 if( consdata->demands[v] > halfcapacity ) 11050 continue; 11051 11052 if( mindemand + consdata->demands[v] > capacity ) 11053 { 11054 demands[nvars] = 1; 11055 durations[nvars] = consdata->durations[v]; 11056 vars[nvars] = consdata->vars[v]; 11057 nvars++; 11058 11059 /* @todo create one cumulative constraint and look for another small demand */ 11060 break; 11061 } 11062 } 11063 11064 /* creates cumulative constraint and adds it to problem */ 11065 SCIP_CALL( createConsCumulative(scip, SCIPconsGetName(cons), nvars, vars, durations, demands, 1, consdata->hmin, consdata->hmax, 11066 FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) ); 11067 (*naddconss)++; 11068 } 11069 11070 SCIPfreeBufferArray(scip, &demands); 11071 SCIPfreeBufferArray(scip, &durations); 11072 SCIPfreeBufferArray(scip, &vars); 11073 11074 return SCIP_OKAY; 11075 } 11076 11077 /** presolve given constraint */ 11078 static 11079 SCIP_RETCODE presolveCons( 11080 SCIP* scip, /**< SCIP data structure */ 11081 SCIP_CONS* cons, /**< cumulative constraint */ 11082 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */ 11083 SCIP_PRESOLTIMING presoltiming, /**< timing of presolving call */ 11084 int* nfixedvars, /**< pointer to store the number of fixed variables */ 11085 #if 0 11086 int* naggrvars, /**< pointer to counter which is increased by the number of deduced variable aggregations */ 11087 #endif 11088 int* nchgbds, /**< pointer to store the number of changed bounds */ 11089 int* ndelconss, /**< pointer to store the number of deleted constraints */ 11090 int* naddconss, /**< pointer to store the number of added constraints */ 11091 int* nchgcoefs, /**< pointer to store the number of changed coefficients */ 11092 int* nchgsides, /**< pointer to store the number of changed sides */ 11093 SCIP_Bool* cutoff, /**< pointer to store if a cutoff was detected */ 11094 SCIP_Bool* unbounded /**< pointer to store if the problem is unbounded */ 11095 ) 11096 { 11097 assert(!SCIPconsIsDeleted(cons)); 11098 11099 /* only perform dual reductions on model constraints */ 11100 if( conshdlrdata->dualpresolve && SCIPallowStrongDualReds(scip) ) 11101 { 11102 /* computes the effective horizon and checks if the constraint can be decomposed */ 11103 SCIP_CALL( computeEffectiveHorizon(scip, cons, ndelconss, naddconss, nchgsides) ); 11104 11105 if( SCIPconsIsDeleted(cons) ) 11106 return SCIP_OKAY; 11107 11108 /* in case the cumulative constraint is independent of every else, solve the cumulative problem and apply the 11109 * fixings (dual reductions) 11110 */ 11111 if( (presoltiming & SCIP_PRESOLTIMING_EXHAUSTIVE) != 0 ) 11112 { 11113 SCIP_CALL( solveIndependentCons(scip, cons, conshdlrdata->maxnodes, nchgbds, nfixedvars, ndelconss, cutoff, unbounded) ); 11114 11115 if( *cutoff || *unbounded || presoltiming == SCIP_PRESOLTIMING_EXHAUSTIVE ) 11116 return SCIP_OKAY; 11117 } 11118 11119 SCIP_CALL( presolveConsEffectiveHorizon(scip, cons, nfixedvars, nchgcoefs, nchgsides, cutoff) ); 11120 11121 if( *cutoff || SCIPconsIsDeleted(cons) ) 11122 return SCIP_OKAY; 11123 } 11124 11125 /* remove jobs which have a demand larger than the capacity */ 11126 SCIP_CALL( removeOversizedJobs(scip, cons, nchgbds, nchgcoefs, naddconss, cutoff) ); 11127 assert((*cutoff) || checkDemands(scip, cons)); 11128 11129 if( *cutoff ) 11130 return SCIP_OKAY; 11131 11132 if( conshdlrdata->normalize ) 11133 { 11134 /* divide demands by their greatest common divisor */ 11135 normalizeDemands(scip, cons, nchgcoefs, nchgsides); 11136 } 11137 11138 /* delete constraint with one job */ 11139 SCIP_CALL( deleteTrivilCons(scip, cons, ndelconss, cutoff) ); 11140 11141 if( *cutoff || SCIPconsIsDeleted(cons) ) 11142 return SCIP_OKAY; 11143 11144 if( conshdlrdata->coeftightening ) 11145 { 11146 /* try to tighten the capacity */ 11147 SCIP_CALL( tightenCapacity(scip, cons, nchgcoefs, nchgsides) ); 11148 11149 /* try to tighten the coefficients */ 11150 SCIP_CALL( tightenCoefs(scip, cons, nchgcoefs) ); 11151 } 11152 11153 assert(checkDemands(scip, cons) || *cutoff); 11154 11155 #if 0 11156 SCIP_CALL( reformulateCons(scip, cons, naggrvars) ); 11157 #endif 11158 11159 return SCIP_OKAY; 11160 } 11161 11162 /**@name TClique Graph callbacks 11163 * 11164 * @{ 11165 */ 11166 11167 /** tclique graph data */ 11168 struct TCLIQUE_Graph 11169 { 11170 SCIP_VAR** vars; /**< start time variables each of them is a node */ 11171 SCIP_HASHMAP* varmap; /**< variable map, mapping variable to indux in vars array */ 11172 SCIP_Bool** precedencematrix; /**< precedence adjacent matrix */ 11173 SCIP_Bool** demandmatrix; /**< demand adjacent matrix */ 11174 TCLIQUE_WEIGHT* weights; /**< weight of nodes */ 11175 int* ninarcs; /**< number if in arcs for the precedence graph */ 11176 int* noutarcs; /**< number if out arcs for the precedence graph */ 11177 int* durations; /**< for each node the duration of the corresponding job */ 11178 int nnodes; /**< number of nodes */ 11179 int size; /**< size of the array */ 11180 }; 11181 11182 /** gets number of nodes in the graph */ 11183 static 11184 TCLIQUE_GETNNODES(tcliqueGetnnodesClique) 11185 { 11186 assert(tcliquegraph != NULL); 11187 11188 return tcliquegraph->nnodes; 11189 } 11190 11191 /** gets weight of nodes in the graph */ 11192 static 11193 TCLIQUE_GETWEIGHTS(tcliqueGetweightsClique) 11194 { 11195 assert(tcliquegraph != NULL); 11196 11197 return tcliquegraph->weights; 11198 } 11199 11200 /** returns, whether the edge (node1, node2) is in the graph */ 11201 static 11202 TCLIQUE_ISEDGE(tcliqueIsedgeClique) 11203 { 11204 assert(tcliquegraph != NULL); 11205 assert(0 <= node1 && node1 < tcliquegraph->nnodes); 11206 assert(0 <= node2 && node2 < tcliquegraph->nnodes); 11207 11208 /* check if an arc exits in the precedence graph */ 11209 if( tcliquegraph->precedencematrix[node1][node2] || tcliquegraph->precedencematrix[node2][node1] ) 11210 return TRUE; 11211 11212 /* check if an edge exits in the non-overlapping graph */ 11213 if( tcliquegraph->demandmatrix[node1][node2] ) 11214 return TRUE; 11215 11216 return FALSE; 11217 } 11218 11219 /** selects all nodes from a given set of nodes which are adjacent to a given node 11220 * and returns the number of selected nodes 11221 */ 11222 static 11223 TCLIQUE_SELECTADJNODES(tcliqueSelectadjnodesClique) 11224 { 11225 int nadjnodes; 11226 int i; 11227 11228 assert(tcliquegraph != NULL); 11229 assert(0 <= node && node < tcliquegraph->nnodes); 11230 assert(nnodes == 0 || nodes != NULL); 11231 assert(adjnodes != NULL); 11232 11233 nadjnodes = 0; 11234 11235 for( i = 0; i < nnodes; i++ ) 11236 { 11237 /* check if the node is adjacent to the given node (nodes and adjacent nodes are ordered by node index) */ 11238 assert(0 <= nodes[i] && nodes[i] < tcliquegraph->nnodes); 11239 assert(i == 0 || nodes[i-1] < nodes[i]); 11240 11241 /* check if an edge exists */ 11242 if( tcliqueIsedgeClique(tcliquegraph, node, nodes[i]) ) 11243 { 11244 /* current node is adjacent to given node */ 11245 adjnodes[nadjnodes] = nodes[i]; 11246 nadjnodes++; 11247 } 11248 } 11249 11250 return nadjnodes; 11251 } 11252 11253 /** generates cuts using a clique found by algorithm for maximum weight clique 11254 * and decides whether to stop generating cliques with the algorithm for maximum weight clique 11255 */ 11256 static 11257 TCLIQUE_NEWSOL(tcliqueNewsolClique) 11258 { /*lint --e{715}*/ 11259 SCIPdebugMessage("####### max clique %d\n", cliqueweight); 11260 } 11261 11262 /** print the tclique graph */ 11263 #if 0 11264 static 11265 void tcliquePrint( 11266 SCIP* scip, /**< SCIP data structure */ 11267 TCLIQUE_GRAPH* tcliquegraph /**< tclique graph */ 11268 ) 11269 { 11270 int nnodes; 11271 int i; 11272 int j; 11273 11274 nnodes = tcliquegraph->nnodes; 11275 11276 for( i = 0; i < nnodes; ++i ) 11277 { 11278 for( j = 0; j < nnodes; ++j ) 11279 { 11280 SCIPinfoMessage(scip, NULL, "(%d/%d) ", tcliquegraph->precedencematrix[i][j], tcliquegraph->demandmatrix[i][j]); 11281 } 11282 SCIPinfoMessage(scip, NULL, "\n"); 11283 } 11284 } 11285 #endif 11286 11287 /** @} */ 11288 11289 /** analyzes if the given variable lower bound condition implies a precedence condition w.r.t. given duration for the 11290 * job corresponding to variable bound variable (vlbvar) 11291 * 11292 * variable lower bound is given as: var >= vlbcoef * vlbvar + vlbconst 11293 */ 11294 static 11295 SCIP_Bool impliesVlbPrecedenceCondition( 11296 SCIP* scip, /**< SCIP data structure */ 11297 SCIP_VAR* vlbvar, /**< variable which bounds the variable from below */ 11298 SCIP_Real vlbcoef, /**< variable bound coefficient */ 11299 SCIP_Real vlbconst, /**< variable bound constant */ 11300 int duration /**< duration of the variable bound variable */ 11301 ) 11302 { 11303 if( SCIPisEQ(scip, vlbcoef, 1.0) ) 11304 { 11305 if( SCIPisGE(scip, vlbconst, (SCIP_Real) duration) ) 11306 { 11307 /* if vlbcoef = 1 and vlbcoef >= duration -> precedence condition */ 11308 return TRUE; 11309 } 11310 } 11311 else 11312 { 11313 SCIP_Real bound; 11314 11315 bound = (duration - vlbcoef) / (vlbcoef - 1.0); 11316 11317 if( SCIPisLT(scip, vlbcoef, 1.0) ) 11318 { 11319 SCIP_Real ub; 11320 11321 ub = SCIPvarGetUbLocal(vlbvar); 11322 11323 /* if vlbcoef < 1 and ub(vlbvar) <= (duration - vlbconst)/(vlbcoef - 1) -> precedence condition */ 11324 if( SCIPisLE(scip, ub, bound) ) 11325 return TRUE; 11326 } 11327 else 11328 { 11329 SCIP_Real lb; 11330 11331 assert(SCIPisGT(scip, vlbcoef, 1.0)); 11332 11333 lb = SCIPvarGetLbLocal(vlbvar); 11334 11335 /* if vlbcoef > 1 and lb(vlbvar) >= (duration - vlbconst)/(vlbcoef - 1) -> precedence condition */ 11336 if( SCIPisGE(scip, lb, bound) ) 11337 return TRUE; 11338 } 11339 } 11340 11341 return FALSE; 11342 } 11343 11344 /** analyzes if the given variable upper bound condition implies a precedence condition w.r.t. given duration for the 11345 * job corresponding to variable which is bounded (var) 11346 * 11347 * variable upper bound is given as: var <= vubcoef * vubvar + vubconst 11348 */ 11349 static 11350 SCIP_Bool impliesVubPrecedenceCondition( 11351 SCIP* scip, /**< SCIP data structure */ 11352 SCIP_VAR* var, /**< variable which is bound from above */ 11353 SCIP_Real vubcoef, /**< variable bound coefficient */ 11354 SCIP_Real vubconst, /**< variable bound constant */ 11355 int duration /**< duration of the variable which is bounded from above */ 11356 ) 11357 { 11358 SCIP_Real vlbcoef; 11359 SCIP_Real vlbconst; 11360 11361 /* convert the variable upper bound into an variable lower bound */ 11362 vlbcoef = 1.0 / vubcoef; 11363 vlbconst = -vubconst / vubcoef; 11364 11365 return impliesVlbPrecedenceCondition(scip, var, vlbcoef, vlbconst, duration); 11366 } 11367 11368 /** get the corresponding index of the given variables; this in case of an active variable the problem index and for 11369 * others an index larger than the number if active variables 11370 */ 11371 static 11372 SCIP_RETCODE getNodeIdx( 11373 SCIP* scip, /**< SCIP data structure */ 11374 TCLIQUE_GRAPH* tcliquegraph, /**< incompatibility graph */ 11375 SCIP_VAR* var, /**< variable for which we want the index */ 11376 int* idx /**< pointer to store the index */ 11377 ) 11378 { 11379 (*idx) = SCIPvarGetProbindex(var); 11380 11381 if( (*idx) == -1 ) 11382 { 11383 if( SCIPhashmapExists(tcliquegraph->varmap, (void*)var) ) 11384 { 11385 (*idx) = SCIPhashmapGetImageInt(tcliquegraph->varmap, (void*)var); 11386 } 11387 else 11388 { 11389 int pos; 11390 int v; 11391 11392 /**@todo we might want to add the aggregation path to graph */ 11393 11394 /* check if we have to realloc memory */ 11395 if( tcliquegraph->size == tcliquegraph->nnodes ) 11396 { 11397 int size; 11398 11399 size = SCIPcalcMemGrowSize(scip, tcliquegraph->nnodes+1); 11400 tcliquegraph->size = size; 11401 11402 SCIP_CALL( SCIPreallocBufferArray(scip, &tcliquegraph->vars, size) ); 11403 SCIP_CALL( SCIPreallocBufferArray(scip, &tcliquegraph->precedencematrix, size) ); 11404 SCIP_CALL( SCIPreallocBufferArray(scip, &tcliquegraph->demandmatrix, size) ); 11405 SCIP_CALL( SCIPreallocBufferArray(scip, &tcliquegraph->durations, size) ); 11406 SCIP_CALL( SCIPreallocBufferArray(scip, &tcliquegraph->weights, size) ); 11407 11408 for( v = 0; v < tcliquegraph->nnodes; ++v ) 11409 { 11410 SCIP_CALL( SCIPreallocBufferArray(scip, &tcliquegraph->precedencematrix[v], size) ); /*lint !e866*/ 11411 SCIP_CALL( SCIPreallocBufferArray(scip, &tcliquegraph->demandmatrix[v], size) ); /*lint !e866*/ 11412 } 11413 } 11414 assert(tcliquegraph->nnodes < tcliquegraph->size); 11415 11416 pos = tcliquegraph->nnodes; 11417 assert(pos >= 0); 11418 11419 tcliquegraph->durations[pos] = 0; 11420 tcliquegraph->weights[pos] = 0; 11421 tcliquegraph->vars[pos] = var; 11422 11423 SCIP_CALL( SCIPallocBufferArray(scip, &tcliquegraph->precedencematrix[pos], tcliquegraph->size) ); /*lint !e866*/ 11424 BMSclearMemoryArray(tcliquegraph->precedencematrix[pos], tcliquegraph->nnodes); /*lint !e866*/ 11425 11426 SCIP_CALL( SCIPallocBufferArray(scip, &tcliquegraph->demandmatrix[pos], tcliquegraph->size) ); /*lint !e866*/ 11427 BMSclearMemoryArray(tcliquegraph->demandmatrix[pos], tcliquegraph->nnodes); /*lint !e866*/ 11428 11429 SCIP_CALL( SCIPhashmapInsertInt(tcliquegraph->varmap, (void*)var, pos) ); 11430 11431 tcliquegraph->nnodes++; 11432 11433 for( v = 0; v < tcliquegraph->nnodes; ++v ) 11434 { 11435 tcliquegraph->precedencematrix[v][pos] = 0; 11436 tcliquegraph->demandmatrix[v][pos] = 0; 11437 } 11438 11439 (*idx) = tcliquegraph->nnodes; 11440 } 11441 } 11442 else 11443 { 11444 assert(*idx == SCIPhashmapGetImageInt(tcliquegraph->varmap, (void*)var)); 11445 } 11446 11447 assert(SCIPhashmapExists(tcliquegraph->varmap, (void*)var)); 11448 11449 return SCIP_OKAY; 11450 } 11451 11452 /** use the variables bounds of SCIP to projected variables bound graph into a precedence garph 11453 * 11454 * Let d be the (assumed) duration of variable x and consider a variable bound of the form b * x + c <= y. This 11455 * variable bounds implies a precedence condition x -> y (meaning job y starts after job x is finished) if: 11456 * 11457 * (i) b = 1 and c >= d 11458 * (ii) b > 1 and lb(x) >= (d - c)/(b - 1) 11459 * (iii) b < 1 and ub(x) >= (d - c)/(b - 1) 11460 * 11461 */ 11462 static 11463 SCIP_RETCODE projectVbd( 11464 SCIP* scip, /**< SCIP data structure */ 11465 TCLIQUE_GRAPH* tcliquegraph /**< incompatibility graph */ 11466 ) 11467 { 11468 SCIP_VAR** vars; 11469 int nvars; 11470 int v; 11471 11472 vars = SCIPgetVars(scip); 11473 nvars = SCIPgetNVars(scip); 11474 11475 /* try to project each arc of the variable bound graph to precedence condition */ 11476 for( v = 0; v < nvars; ++v ) 11477 { 11478 SCIP_VAR** vbdvars; 11479 SCIP_VAR* var; 11480 SCIP_Real* vbdcoefs; 11481 SCIP_Real* vbdconsts; 11482 int nvbdvars; 11483 int idx1; 11484 int b; 11485 11486 var = vars[v]; 11487 assert(var != NULL); 11488 11489 SCIP_CALL( getNodeIdx(scip, tcliquegraph, var, &idx1) ); 11490 assert(idx1 >= 0); 11491 11492 if( tcliquegraph->durations[idx1] == 0 ) 11493 continue; 11494 11495 vbdvars = SCIPvarGetVlbVars(var); 11496 vbdcoefs = SCIPvarGetVlbCoefs(var); 11497 vbdconsts = SCIPvarGetVlbConstants(var); 11498 nvbdvars = SCIPvarGetNVlbs(var); 11499 11500 for( b = 0; b < nvbdvars; ++b ) 11501 { 11502 int idx2; 11503 11504 SCIP_CALL( getNodeIdx(scip, tcliquegraph, vbdvars[b], &idx2) ); 11505 assert(idx2 >= 0); 11506 11507 if( tcliquegraph->durations[idx2] == 0 ) 11508 continue; 11509 11510 if( impliesVlbPrecedenceCondition(scip, vbdvars[b], vbdcoefs[b], vbdconsts[b], tcliquegraph->durations[idx2]) ) 11511 tcliquegraph->precedencematrix[idx2][idx1] = TRUE; 11512 } 11513 11514 vbdvars = SCIPvarGetVubVars(var); 11515 vbdcoefs = SCIPvarGetVubCoefs(var); 11516 vbdconsts = SCIPvarGetVubConstants(var); 11517 nvbdvars = SCIPvarGetNVubs(var); 11518 11519 for( b = 0; b < nvbdvars; ++b ) 11520 { 11521 int idx2; 11522 11523 SCIP_CALL( getNodeIdx(scip, tcliquegraph, vbdvars[b], &idx2) ); 11524 assert(idx2 >= 0); 11525 11526 if( tcliquegraph->durations[idx2] == 0 ) 11527 continue; 11528 11529 if( impliesVubPrecedenceCondition(scip, var, vbdcoefs[b], vbdconsts[b], tcliquegraph->durations[idx1]) ) 11530 tcliquegraph->precedencematrix[idx1][idx2] = TRUE; 11531 } 11532 11533 for( b = v+1; b < nvars; ++b ) 11534 { 11535 int idx2; 11536 11537 SCIP_CALL( getNodeIdx(scip, tcliquegraph, vars[b], &idx2) ); 11538 assert(idx2 >= 0); 11539 11540 if( tcliquegraph->durations[idx2] == 0 ) 11541 continue; 11542 11543 /* check if the latest completion time of job1 is smaller than the earliest start time of job2 */ 11544 if( SCIPisLE(scip, SCIPvarGetUbLocal(var) + tcliquegraph->durations[idx1], SCIPvarGetLbLocal(vars[b])) ) 11545 tcliquegraph->precedencematrix[idx1][idx2] = TRUE; 11546 11547 /* check if the latest completion time of job2 is smaller than the earliest start time of job1 */ 11548 if( SCIPisLE(scip, SCIPvarGetUbLocal(vars[b]) + tcliquegraph->durations[idx2], SCIPvarGetLbLocal(var)) ) 11549 tcliquegraph->precedencematrix[idx2][idx1] = TRUE; 11550 } 11551 } 11552 11553 return SCIP_OKAY; 11554 } 11555 11556 /** compute the transitive closer of the given graph and the number of in and out arcs */ 11557 static 11558 void transitiveClosure( 11559 SCIP_Bool** adjmatrix, /**< adjacent matrix */ 11560 int* ninarcs, /**< array to store the number of in arcs */ 11561 int* noutarcs, /**< array to store the number of out arcs */ 11562 int nnodes /**< number if nodes */ 11563 ) 11564 { 11565 int i; 11566 int j; 11567 int k; 11568 11569 for( i = 0; i < nnodes; ++i ) 11570 { 11571 for( j = 0; j < nnodes; ++j ) 11572 { 11573 if( adjmatrix[i][j] ) 11574 { 11575 ninarcs[j]++; 11576 noutarcs[i]++; 11577 11578 for( k = 0; k < nnodes; ++k ) 11579 { 11580 if( adjmatrix[j][k] ) 11581 adjmatrix[i][k] = TRUE; 11582 } 11583 } 11584 } 11585 } 11586 } 11587 11588 /** constructs a non-overlapping graph w.r.t. given durations and available cumulative constraints */ 11589 static 11590 SCIP_RETCODE constraintNonOverlappingGraph( 11591 SCIP* scip, /**< SCIP data structure */ 11592 TCLIQUE_GRAPH* tcliquegraph, /**< incompatibility graph */ 11593 SCIP_CONS** conss, /**< array of cumulative constraints */ 11594 int nconss /**< number of cumulative constraints */ 11595 ) 11596 { 11597 int c; 11598 11599 /* use the cumulative constraints to initialize the none overlapping graph */ 11600 for( c = 0; c < nconss; ++c ) 11601 { 11602 SCIP_CONSDATA* consdata; 11603 SCIP_VAR** vars; 11604 int* demands; 11605 int capacity; 11606 int nvars; 11607 int i; 11608 11609 consdata = SCIPconsGetData(conss[c]); 11610 assert(consdata != NULL); 11611 11612 vars = consdata->vars; 11613 demands = consdata->demands; 11614 11615 nvars = consdata->nvars; 11616 capacity = consdata->capacity; 11617 11618 SCIPdebugMsg(scip, "constraint <%s>\n", SCIPconsGetName(conss[c])); 11619 11620 /* check pairwise if two jobs have a cumulative demand larger than the capacity */ 11621 for( i = 0; i < nvars; ++i ) 11622 { 11623 int idx1; 11624 int j; 11625 11626 SCIP_CALL( getNodeIdx(scip, tcliquegraph, vars[i], &idx1) ); 11627 assert(idx1 >= 0); 11628 11629 if( tcliquegraph->durations[idx1] == 0 || tcliquegraph->durations[idx1] > consdata->durations[i] ) 11630 continue; 11631 11632 for( j = i+1; j < nvars; ++j ) 11633 { 11634 assert(consdata->durations[j] > 0); 11635 11636 if( demands[i] + demands[j] > capacity ) 11637 { 11638 int idx2; 11639 int est1; 11640 int est2; 11641 int lct1; 11642 int lct2; 11643 11644 /* check if the effective horizon is large enough */ 11645 est1 = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(vars[i])); 11646 est2 = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(vars[j])); 11647 11648 /* at least one of the jobs needs to start at hmin or later */ 11649 if( est1 < consdata->hmin && est2 < consdata->hmin ) 11650 continue; 11651 11652 lct1 = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(vars[i])) + consdata->durations[i]; 11653 lct2 = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(vars[j])) + consdata->durations[j]; 11654 11655 /* at least one of the jobs needs to finish not later then hmin */ 11656 if( lct1 > consdata->hmax && lct2 > consdata->hmax ) 11657 continue; 11658 11659 SCIP_CALL( getNodeIdx(scip, tcliquegraph, vars[j], &idx2) ); 11660 assert(idx2 >= 0); 11661 assert(idx1 != idx2); 11662 11663 if( tcliquegraph->durations[idx2] == 0 || tcliquegraph->durations[idx2] > consdata->durations[j] ) 11664 continue; 11665 11666 SCIPdebugMsg(scip, " *** variable <%s> and variable <%s>\n", SCIPvarGetName(vars[i]), SCIPvarGetName(vars[j])); 11667 11668 assert(tcliquegraph->durations[idx1] > 0); 11669 assert(tcliquegraph->durations[idx2] > 0); 11670 11671 tcliquegraph->demandmatrix[idx1][idx2] = TRUE; 11672 tcliquegraph->demandmatrix[idx2][idx1] = TRUE; 11673 } 11674 } 11675 } 11676 } 11677 11678 return SCIP_OKAY; 11679 } 11680 11681 /** constructs a conflict set graph (undirected) which contains for each job a node and edge if the corresponding pair 11682 * of jobs cannot run in parallel 11683 */ 11684 static 11685 SCIP_RETCODE constructIncompatibilityGraph( 11686 SCIP* scip, /**< SCIP data structure */ 11687 TCLIQUE_GRAPH* tcliquegraph, /**< incompatibility graph */ 11688 SCIP_CONS** conss, /**< array of cumulative constraints */ 11689 int nconss /**< number of cumulative constraints */ 11690 ) 11691 { 11692 assert(scip != NULL); 11693 assert(tcliquegraph != NULL); 11694 11695 /* use the variables bounds of SCIP to project the variables bound graph inot a precedence graph */ 11696 SCIP_CALL( projectVbd(scip, tcliquegraph) ); 11697 11698 /* compute the transitive closure of the precedence graph and the number of in and out arcs */ 11699 transitiveClosure(tcliquegraph->precedencematrix, tcliquegraph->ninarcs, tcliquegraph->noutarcs, tcliquegraph->nnodes); 11700 11701 /* constraints non-overlapping graph */ 11702 SCIP_CALL( constraintNonOverlappingGraph(scip, tcliquegraph, conss, nconss) ); 11703 11704 return SCIP_OKAY; 11705 } 11706 11707 /** create cumulative constraint from conflict set */ 11708 static 11709 SCIP_RETCODE createCumulativeCons( 11710 SCIP* scip, /**< SCIP data structure */ 11711 const char* name, /**< constraint name */ 11712 TCLIQUE_GRAPH* tcliquegraph, /**< conflict set graph */ 11713 int* cliquenodes, /**< array storing the indecies of the nodes belonging to the clique */ 11714 int ncliquenodes /**< number of nodes in the clique */ 11715 ) 11716 { 11717 SCIP_CONS* cons; 11718 SCIP_VAR** vars; 11719 int* durations; 11720 int* demands; 11721 int v; 11722 11723 SCIP_CALL( SCIPallocBufferArray(scip, &vars, ncliquenodes) ); 11724 SCIP_CALL( SCIPallocBufferArray(scip, &durations, ncliquenodes) ); 11725 SCIP_CALL( SCIPallocBufferArray(scip, &demands, ncliquenodes) ); 11726 11727 SCIPsortInt(cliquenodes, ncliquenodes); 11728 11729 /* collect variables, durations, and demands */ 11730 for( v = 0; v < ncliquenodes; ++v ) 11731 { 11732 durations[v] = tcliquegraph->durations[cliquenodes[v]]; 11733 assert(durations[v] > 0); 11734 demands[v] = 1; 11735 vars[v] = tcliquegraph->vars[cliquenodes[v]]; 11736 } 11737 11738 /* create (unary) cumulative constraint */ 11739 SCIP_CALL( SCIPcreateConsCumulative(scip, &cons, name, ncliquenodes, vars, durations, demands, 1, 11740 FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) ); 11741 11742 SCIP_CALL( SCIPaddCons(scip, cons) ); 11743 SCIP_CALL( SCIPreleaseCons(scip, &cons) ); 11744 11745 /* free buffers */ 11746 SCIPfreeBufferArray(scip, &demands); 11747 SCIPfreeBufferArray(scip, &durations); 11748 SCIPfreeBufferArray(scip, &vars); 11749 11750 return SCIP_OKAY; 11751 } 11752 11753 /** search for cumulative constrainst */ 11754 static 11755 SCIP_RETCODE findCumulativeConss( 11756 SCIP* scip, /**< SCIP data structure */ 11757 TCLIQUE_GRAPH* tcliquegraph, /**< conflict set graph */ 11758 int* naddconss /**< pointer to store the number of added constraints */ 11759 ) 11760 { 11761 TCLIQUE_STATUS tcliquestatus; 11762 SCIP_Bool* precedencerow; 11763 SCIP_Bool* precedencecol; 11764 SCIP_Bool* demandrow; 11765 SCIP_Bool* demandcol; 11766 SCIP_HASHTABLE* covered; 11767 int* cliquenodes; 11768 int ncliquenodes; 11769 int cliqueweight; 11770 int ntreenodes; 11771 int nnodes; 11772 int nconss; 11773 int v; 11774 11775 nnodes = tcliquegraph->nnodes; 11776 nconss = 0; 11777 11778 /* initialize the weight of each job with its duration */ 11779 for( v = 0; v < nnodes; ++v ) 11780 { 11781 tcliquegraph->weights[v] = tcliquegraph->durations[v]; 11782 } 11783 11784 SCIP_CALL( SCIPallocBufferArray(scip, &cliquenodes, nnodes) ); 11785 SCIP_CALL( SCIPallocBufferArray(scip, &precedencerow, nnodes) ); 11786 SCIP_CALL( SCIPallocBufferArray(scip, &precedencecol, nnodes) ); 11787 SCIP_CALL( SCIPallocBufferArray(scip, &demandrow, nnodes) ); 11788 SCIP_CALL( SCIPallocBufferArray(scip, &demandcol, nnodes) ); 11789 11790 /* create a hash table to store all start time variables which are already covered by at least one clique */ 11791 SCIP_CALL( SCIPhashtableCreate(&covered, SCIPblkmem(scip), nnodes, 11792 SCIPvarGetHashkey, SCIPvarIsHashkeyEq, SCIPvarGetHashkeyVal, NULL) ); 11793 11794 /* for each variables/job we are ... */ 11795 for( v = 0; v < nnodes && !SCIPisStopped(scip); ++v ) 11796 { 11797 char name[SCIP_MAXSTRLEN]; 11798 int c; 11799 11800 /* jobs with zero durations are skipped */ 11801 if( tcliquegraph->durations[v] == 0 ) 11802 continue; 11803 11804 /* check if the start time variable is already covered by at least one clique */ 11805 if( SCIPhashtableExists(covered, tcliquegraph->vars[v]) ) 11806 continue; 11807 11808 SCIPdebugMsg(scip, "********** variable <%s>\n", SCIPvarGetName(tcliquegraph->vars[v])); 11809 11810 /* temporarily remove the connection via the precedence graph */ 11811 for( c = 0; c < nnodes; ++c ) 11812 { 11813 precedencerow[c] = tcliquegraph->precedencematrix[v][c]; 11814 precedencecol[c] = tcliquegraph->precedencematrix[c][v]; 11815 11816 demandrow[c] = tcliquegraph->demandmatrix[v][c]; 11817 demandcol[c] = tcliquegraph->demandmatrix[c][v]; 11818 11819 #if 0 11820 if( precedencerow[c] || precedencecol[c] ) 11821 { 11822 tcliquegraph->demandmatrix[v][c] = FALSE; 11823 tcliquegraph->demandmatrix[c][v] = FALSE; 11824 } 11825 #endif 11826 11827 tcliquegraph->precedencematrix[c][v] = FALSE; 11828 tcliquegraph->precedencematrix[v][c] = FALSE; 11829 } 11830 11831 /* find (heuristically) maximum cliques which includes node v */ 11832 tcliqueMaxClique(tcliqueGetnnodesClique, tcliqueGetweightsClique, tcliqueIsedgeClique, tcliqueSelectadjnodesClique, 11833 tcliquegraph, tcliqueNewsolClique, NULL, 11834 cliquenodes, &ncliquenodes, &cliqueweight, 1, 1, 11835 10000, 1000, 1000, v, &ntreenodes, &tcliquestatus); 11836 11837 SCIPdebugMsg(scip, "tree nodes %d clique size %d (weight %d, status %d)\n", ntreenodes, ncliquenodes, cliqueweight, tcliquestatus); 11838 11839 if( ncliquenodes == 1 ) 11840 continue; 11841 11842 /* construct constraint name */ 11843 (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "nooverlap_%d_%d", SCIPgetNRuns(scip), nconss); 11844 11845 SCIP_CALL( createCumulativeCons(scip, name, tcliquegraph, cliquenodes, ncliquenodes) ); 11846 nconss++; 11847 11848 /* all start time variable to covered hash table */ 11849 for( c = 0; c < ncliquenodes; ++c ) 11850 { 11851 SCIP_CALL( SCIPhashtableInsert(covered, tcliquegraph->vars[cliquenodes[c]]) ); 11852 } 11853 11854 /* copy the precedence relations back */ 11855 for( c = 0; c < nnodes; ++c ) 11856 { 11857 tcliquegraph->precedencematrix[v][c] = precedencerow[c]; 11858 tcliquegraph->precedencematrix[c][v] = precedencecol[c]; 11859 11860 tcliquegraph->demandmatrix[v][c] = demandrow[c]; 11861 tcliquegraph->demandmatrix[c][v] = demandcol[c]; 11862 } 11863 } 11864 11865 SCIPhashtableFree(&covered); 11866 11867 SCIPfreeBufferArray(scip, &demandcol); 11868 SCIPfreeBufferArray(scip, &demandrow); 11869 SCIPfreeBufferArray(scip, &precedencecol); 11870 SCIPfreeBufferArray(scip, &precedencerow); 11871 SCIPfreeBufferArray(scip, &cliquenodes); 11872 11873 (*naddconss) += nconss; 11874 11875 /* for the statistic we count the number added disjunctive constraints */ 11876 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->naddeddisjunctives += nconss ); 11877 11878 return SCIP_OKAY; 11879 } 11880 11881 /** create precedence constraint (as variable bound constraint */ 11882 static 11883 SCIP_RETCODE createPrecedenceCons( 11884 SCIP* scip, /**< SCIP data structure */ 11885 const char* name, /**< constraint name */ 11886 SCIP_VAR* var, /**< variable x that has variable bound */ 11887 SCIP_VAR* vbdvar, /**< binary, integer or implicit integer bounding variable y */ 11888 int distance /**< minimum distance between the start time of the job corresponding to var and the job corresponding to vbdvar */ 11889 ) 11890 { 11891 SCIP_CONS* cons; 11892 11893 /* create variable bound constraint */ 11894 SCIP_CALL( SCIPcreateConsVarbound(scip, &cons, name, var, vbdvar, -1.0, -SCIPinfinity(scip), -(SCIP_Real)distance, 11895 TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) ); 11896 11897 SCIPdebugPrintCons(scip, cons, NULL); 11898 11899 /* add constraint to problem and release it */ 11900 SCIP_CALL( SCIPaddCons(scip, cons) ); 11901 SCIP_CALL( SCIPreleaseCons(scip, &cons) ); 11902 11903 return SCIP_OKAY; 11904 } 11905 11906 /** compute a minimum distance between the start times of the two given jobs and post it as variable bound constraint */ 11907 static 11908 SCIP_RETCODE computeMinDistance( 11909 SCIP* scip, /**< SCIP data structure */ 11910 TCLIQUE_GRAPH* tcliquegraph, /**< conflict set graph */ 11911 int source, /**< index of the source node */ 11912 int sink, /**< index of the sink node */ 11913 int* naddconss /**< pointer to store the number of added constraints */ 11914 ) 11915 { 11916 TCLIQUE_WEIGHT cliqueweight; 11917 TCLIQUE_STATUS tcliquestatus; 11918 SCIP_VAR** vars; 11919 int* cliquenodes; 11920 int nnodes; 11921 int lct; 11922 int est; 11923 int i; 11924 11925 int ntreenodes; 11926 int ncliquenodes; 11927 11928 /* check if source and sink are connencted */ 11929 if( !tcliquegraph->precedencematrix[source][sink] ) 11930 return SCIP_OKAY; 11931 11932 nnodes = tcliquegraph->nnodes; 11933 vars = tcliquegraph->vars; 11934 11935 /* reset the weights to zero */ 11936 BMSclearMemoryArray(tcliquegraph->weights, nnodes); 11937 11938 /* get latest completion time (lct) of the source and the earliest start time (est) of sink */ 11939 lct = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(vars[source])) + tcliquegraph->durations[source]; 11940 est = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(vars[sink])); 11941 11942 /* weight all jobs which run for sure between source and sink with their duration */ 11943 for( i = 0; i < nnodes; ++i ) 11944 { 11945 SCIP_VAR* var; 11946 int duration; 11947 11948 var = vars[i]; 11949 assert(var != NULL); 11950 11951 duration = tcliquegraph->durations[i]; 11952 11953 if( i == source || i == sink ) 11954 { 11955 /* source and sink are not weighted */ 11956 tcliquegraph->weights[i] = 0; 11957 } 11958 else if( tcliquegraph->precedencematrix[source][i] && tcliquegraph->precedencematrix[i][sink] ) 11959 { 11960 /* job i runs after source and before sink */ 11961 tcliquegraph->weights[i] = duration; 11962 } 11963 else if( lct <= SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(var)) 11964 && est >= SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(var)) + duration ) 11965 { 11966 /* job i run in between due the bounds of the start time variables */ 11967 tcliquegraph->weights[i] = duration; 11968 } 11969 else 11970 tcliquegraph->weights[i] = 0; 11971 } 11972 11973 SCIP_CALL( SCIPallocBufferArray(scip, &cliquenodes, nnodes) ); 11974 11975 /* find (heuristically) maximum cliques */ 11976 tcliqueMaxClique(tcliqueGetnnodesClique, tcliqueGetweightsClique, tcliqueIsedgeClique, tcliqueSelectadjnodesClique, 11977 tcliquegraph, tcliqueNewsolClique, NULL, 11978 cliquenodes, &ncliquenodes, &cliqueweight, 1, 1, 11979 10000, 1000, 1000, -1, &ntreenodes, &tcliquestatus); 11980 11981 if( ncliquenodes > 1 ) 11982 { 11983 char name[SCIP_MAXSTRLEN]; 11984 int distance; 11985 11986 /* construct constraint name */ 11987 (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "varbound_%d_%d", SCIPgetNRuns(scip), *naddconss); 11988 11989 /* the minimum distance between the start times of source job and the sink job is the clique weight plus the 11990 * duration of the source job 11991 */ 11992 distance = cliqueweight + tcliquegraph->durations[source]; 11993 11994 SCIP_CALL( createPrecedenceCons(scip, name, vars[source], vars[sink], distance) ); 11995 (*naddconss)++; 11996 } 11997 11998 SCIPfreeBufferArray(scip, &cliquenodes); 11999 12000 return SCIP_OKAY; 12001 } 12002 12003 /** search for precedence constraints 12004 * 12005 * for each arc of the transitive closure of the precedence graph, we are computing a minimum distance between the 12006 * corresponding two jobs 12007 */ 12008 static 12009 SCIP_RETCODE findPrecedenceConss( 12010 SCIP* scip, /**< SCIP data structure */ 12011 TCLIQUE_GRAPH* tcliquegraph, /**< conflict set graph */ 12012 int* naddconss /**< pointer to store the number of added constraints */ 12013 ) 12014 { 12015 int* sources; 12016 int* sinks; 12017 int nconss; 12018 int nnodes; 12019 int nsources; 12020 int nsinks; 12021 int i; 12022 12023 nnodes = tcliquegraph->nnodes; 12024 nconss = 0; 12025 12026 nsources = 0; 12027 nsinks = 0; 12028 12029 SCIP_CALL( SCIPallocBufferArray(scip, &sources, nnodes) ); 12030 SCIP_CALL( SCIPallocBufferArray(scip, &sinks, nnodes) ); 12031 12032 /* first collect all sources and sinks */ 12033 for( i = 0; i < nnodes; ++i ) 12034 { 12035 if( tcliquegraph->ninarcs[i] == 0 ) 12036 { 12037 sources[nsources] = i; 12038 nsources++; 12039 } 12040 12041 if( tcliquegraph->ninarcs[i] == 0 ) 12042 { 12043 sinks[nsinks] = i; 12044 nsinks++; 12045 } 12046 } 12047 12048 /* compute for each node a minimum distance to each sources and each sink */ 12049 for( i = 0; i < nnodes && !SCIPisStopped(scip); ++i ) 12050 { 12051 int j; 12052 12053 for( j = 0; j < nsources && !SCIPisStopped(scip); ++j ) 12054 { 12055 SCIP_CALL( computeMinDistance(scip, tcliquegraph, sources[j], i, &nconss) ); 12056 } 12057 12058 for( j = 0; j < nsinks && !SCIPisStopped(scip); ++j ) 12059 { 12060 SCIP_CALL( computeMinDistance(scip, tcliquegraph, i, sinks[j], &nconss) ); 12061 } 12062 } 12063 12064 (*naddconss) += nconss; 12065 12066 /* for the statistic we count the number added variable constraints */ 12067 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->naddedvarbounds += nconss ); 12068 12069 SCIPfreeBufferArray(scip, &sinks); 12070 SCIPfreeBufferArray(scip, &sources); 12071 12072 return SCIP_OKAY; 12073 } 12074 12075 /** initialize the assumed durations for each variable */ 12076 static 12077 SCIP_RETCODE initializeDurations( 12078 SCIP* scip, /**< SCIP data structure */ 12079 TCLIQUE_GRAPH* tcliquegraph, /**< the incompatibility graph */ 12080 SCIP_CONS** conss, /**< cumulative constraints */ 12081 int nconss /**< number of cumulative constraints */ 12082 ) 12083 { 12084 int c; 12085 12086 /* use the cumulative structure to define the duration we are using for each job */ 12087 for( c = 0; c < nconss; ++c ) 12088 { 12089 SCIP_CONSDATA* consdata; 12090 SCIP_VAR** vars; 12091 int nvars; 12092 int v; 12093 12094 consdata = SCIPconsGetData(conss[c]); 12095 assert(consdata != NULL); 12096 12097 vars = consdata->vars; 12098 nvars = consdata->nvars; 12099 12100 for( v = 0; v < nvars; ++v ) 12101 { 12102 int idx; 12103 12104 SCIP_CALL( getNodeIdx(scip, tcliquegraph, vars[v], &idx) ); 12105 assert(idx >= 0); 12106 12107 /**@todo For the test sets, which we are considere, the durations are independent of the cumulative 12108 * constaints. Meaning each job has a fixed duration which is the same for all cumulative constraints. In 12109 * general this is not the case. Therefore, the question would be which duration should be used? 12110 */ 12111 tcliquegraph->durations[idx] = MAX(tcliquegraph->durations[idx], consdata->durations[v]); 12112 assert(tcliquegraph->durations[idx] > 0); 12113 } 12114 } 12115 12116 return SCIP_OKAY; 12117 } 12118 12119 /** create tclique graph */ 12120 static 12121 SCIP_RETCODE createTcliqueGraph( 12122 SCIP* scip, /**< SCIP data structure */ 12123 TCLIQUE_GRAPH** tcliquegraph /**< reference to the incompatibility graph */ 12124 ) 12125 { 12126 SCIP_VAR** vars; 12127 SCIP_HASHMAP* varmap; 12128 SCIP_Bool** precedencematrix; 12129 SCIP_Bool** demandmatrix; 12130 int* ninarcs; 12131 int* noutarcs; 12132 int* durations; 12133 int* weights; 12134 int nvars; 12135 int v; 12136 12137 vars = SCIPgetVars(scip); 12138 nvars = SCIPgetNVars(scip); 12139 12140 /* allocate memory for the tclique graph data structure */ 12141 SCIP_CALL( SCIPallocBuffer(scip, tcliquegraph) ); 12142 12143 /* create the variable mapping hash map */ 12144 SCIP_CALL( SCIPhashmapCreate(&varmap, SCIPblkmem(scip), nvars) ); 12145 12146 /* each active variables get a node in the graph */ 12147 SCIP_CALL( SCIPduplicateBufferArray(scip, &(*tcliquegraph)->vars, vars, nvars) ); 12148 12149 /* allocate memory for the projected variables bound graph and the none overlapping graph */ 12150 SCIP_CALL( SCIPallocBufferArray(scip, &precedencematrix, nvars) ); 12151 SCIP_CALL( SCIPallocBufferArray(scip, &demandmatrix, nvars) ); 12152 12153 /* array to buffer the weights of the nodes for the maximum weighted clique computation */ 12154 SCIP_CALL( SCIPallocBufferArray(scip, &weights, nvars) ); 12155 BMSclearMemoryArray(weights, nvars); 12156 12157 /* array to store the number of in arc of the precedence graph */ 12158 SCIP_CALL( SCIPallocBufferArray(scip, &ninarcs, nvars) ); 12159 BMSclearMemoryArray(ninarcs, nvars); 12160 12161 /* array to store the number of out arc of the precedence graph */ 12162 SCIP_CALL( SCIPallocBufferArray(scip, &noutarcs, nvars) ); 12163 BMSclearMemoryArray(noutarcs, nvars); 12164 12165 /* array to store the used duration for each node */ 12166 SCIP_CALL( SCIPallocBufferArray(scip, &durations, nvars) ); 12167 BMSclearMemoryArray(durations, nvars); 12168 12169 for( v = 0; v < nvars; ++v ) 12170 { 12171 SCIP_VAR* var; 12172 12173 var = vars[v]; 12174 assert(var != NULL); 12175 12176 SCIP_CALL( SCIPallocBufferArray(scip, &precedencematrix[v], nvars) ); /*lint !e866*/ 12177 BMSclearMemoryArray(precedencematrix[v], nvars); /*lint !e866*/ 12178 12179 SCIP_CALL( SCIPallocBufferArray(scip, &demandmatrix[v], nvars) ); /*lint !e866*/ 12180 BMSclearMemoryArray(demandmatrix[v], nvars); /*lint !e866*/ 12181 12182 /* insert all active variables into the garph */ 12183 assert(SCIPvarGetProbindex(var) == v); 12184 SCIP_CALL( SCIPhashmapInsertInt(varmap, (void*)var, v) ); 12185 } 12186 12187 (*tcliquegraph)->nnodes = nvars; 12188 (*tcliquegraph)->varmap = varmap; 12189 (*tcliquegraph)->precedencematrix = precedencematrix; 12190 (*tcliquegraph)->demandmatrix = demandmatrix; 12191 (*tcliquegraph)->weights = weights; 12192 (*tcliquegraph)->ninarcs = ninarcs; 12193 (*tcliquegraph)->noutarcs = noutarcs; 12194 (*tcliquegraph)->durations = durations; 12195 (*tcliquegraph)->size = nvars; 12196 12197 return SCIP_OKAY; 12198 } 12199 12200 /** frees the tclique graph */ 12201 static 12202 void freeTcliqueGraph( 12203 SCIP* scip, /**< SCIP data structure */ 12204 TCLIQUE_GRAPH** tcliquegraph /**< reference to the incompatibility graph */ 12205 ) 12206 { 12207 int v; 12208 12209 for( v = (*tcliquegraph)->nnodes-1; v >= 0; --v ) 12210 { 12211 SCIPfreeBufferArray(scip, &(*tcliquegraph)->demandmatrix[v]); 12212 SCIPfreeBufferArray(scip, &(*tcliquegraph)->precedencematrix[v]); 12213 } 12214 12215 SCIPfreeBufferArray(scip, &(*tcliquegraph)->durations); 12216 SCIPfreeBufferArray(scip, &(*tcliquegraph)->noutarcs); 12217 SCIPfreeBufferArray(scip, &(*tcliquegraph)->ninarcs); 12218 SCIPfreeBufferArray(scip, &(*tcliquegraph)->weights); 12219 SCIPfreeBufferArray(scip, &(*tcliquegraph)->demandmatrix); 12220 SCIPfreeBufferArray(scip, &(*tcliquegraph)->precedencematrix); 12221 SCIPfreeBufferArray(scip, &(*tcliquegraph)->vars); 12222 SCIPhashmapFree(&(*tcliquegraph)->varmap); 12223 12224 SCIPfreeBuffer(scip, tcliquegraph); 12225 } 12226 12227 /** construct an incompatibility graph and search for precedence constraints (variables bounds) and unary cumulative 12228 * constrains (disjunctive constraint) 12229 */ 12230 static 12231 SCIP_RETCODE detectRedundantConss( 12232 SCIP* scip, /**< SCIP data structure */ 12233 SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */ 12234 SCIP_CONS** conss, /**< array of cumulative constraints */ 12235 int nconss, /**< number of cumulative constraints */ 12236 int* naddconss /**< pointer to store the number of added constraints */ 12237 ) 12238 { 12239 TCLIQUE_GRAPH* tcliquegraph; 12240 12241 /* create tclique graph */ 12242 SCIP_CALL( createTcliqueGraph(scip, &tcliquegraph) ); 12243 12244 /* define for each job a duration */ 12245 SCIP_CALL( initializeDurations(scip, tcliquegraph, conss, nconss) ); 12246 12247 /* constuct incompatibility graph */ 12248 SCIP_CALL( constructIncompatibilityGraph(scip, tcliquegraph, conss, nconss) ); 12249 12250 /* search for new precedence constraints */ 12251 if( conshdlrdata->detectvarbounds ) 12252 { 12253 SCIP_CALL( findPrecedenceConss(scip, tcliquegraph, naddconss) ); 12254 } 12255 12256 /* search for new cumulative constraints */ 12257 if( conshdlrdata->detectdisjunctive ) 12258 { 12259 SCIP_CALL( findCumulativeConss(scip, tcliquegraph, naddconss) ); 12260 } 12261 12262 /* free tclique graph data structure */ 12263 freeTcliqueGraph(scip, &tcliquegraph); 12264 12265 return SCIP_OKAY; 12266 } 12267 12268 /** compute the constraint signature which is used to detect constraints which contain potentially the same set of variables */ 12269 static 12270 void consdataCalcSignature( 12271 SCIP_CONSDATA* consdata /**< cumulative constraint data */ 12272 ) 12273 { 12274 SCIP_VAR** vars; 12275 int nvars; 12276 int v; 12277 12278 if( consdata->validsignature ) 12279 return; 12280 12281 vars = consdata->vars; 12282 nvars = consdata->nvars; 12283 12284 for( v = 0; v < nvars; ++v ) 12285 { 12286 consdata->signature |= ((unsigned int)1 << ((unsigned int)SCIPvarGetIndex(vars[v]) % (sizeof(unsigned int) * 8))); 12287 } 12288 12289 consdata->validsignature = TRUE; 12290 } 12291 12292 /** index comparison method of linear constraints: compares two indices of the variable set in the linear constraint */ 12293 static 12294 SCIP_DECL_SORTINDCOMP(consdataCompVar) 12295 { /*lint --e{715}*/ 12296 SCIP_CONSDATA* consdata = (SCIP_CONSDATA*)dataptr; 12297 12298 assert(consdata != NULL); 12299 assert(0 <= ind1 && ind1 < consdata->nvars); 12300 assert(0 <= ind2 && ind2 < consdata->nvars); 12301 12302 return SCIPvarCompare(consdata->vars[ind1], consdata->vars[ind2]); 12303 } 12304 12305 /** run a pairwise comparison */ 12306 static 12307 SCIP_RETCODE removeRedundantConss( 12308 SCIP* scip, /**< SCIP data structure */ 12309 SCIP_CONS** conss, /**< array of cumulative constraints */ 12310 int nconss, /**< number of cumulative constraints */ 12311 int* ndelconss /**< pointer to store the number of deletedconstraints */ 12312 ) 12313 { 12314 int i; 12315 int j; 12316 12317 for( i = 0; i < nconss; ++i ) 12318 { 12319 SCIP_CONSDATA* consdata0; 12320 SCIP_CONS* cons0; 12321 12322 cons0 = conss[i]; 12323 assert(cons0 != NULL); 12324 12325 consdata0 = SCIPconsGetData(cons0); 12326 assert(consdata0 != NULL); 12327 12328 consdataCalcSignature(consdata0); 12329 assert(consdata0->validsignature); 12330 12331 for( j = i+1; j < nconss; ++j ) 12332 { 12333 SCIP_CONSDATA* consdata1; 12334 SCIP_CONS* cons1; 12335 12336 cons1 = conss[j]; 12337 assert(cons1 != NULL); 12338 12339 consdata1 = SCIPconsGetData(cons1); 12340 assert(consdata1 != NULL); 12341 12342 if( consdata0->capacity != consdata1->capacity ) 12343 continue; 12344 12345 consdataCalcSignature(consdata1); 12346 assert(consdata1->validsignature); 12347 12348 if( (consdata1->signature & (~consdata0->signature)) == 0 ) 12349 { 12350 SCIPswapPointers((void**)&consdata0, (void**)&consdata1); 12351 SCIPswapPointers((void**)&cons0, (void**)&cons1); 12352 assert((consdata0->signature & (~consdata1->signature)) == 0); 12353 } 12354 12355 if( (consdata0->signature & (~consdata1->signature)) == 0 ) 12356 { 12357 int* perm0; 12358 int* perm1; 12359 int v0; 12360 int v1; 12361 12362 if( consdata0->nvars > consdata1->nvars ) 12363 continue; 12364 12365 if( consdata0->hmin < consdata1->hmin ) 12366 continue; 12367 12368 if( consdata0->hmax > consdata1->hmax ) 12369 continue; 12370 12371 SCIP_CALL( SCIPallocBufferArray(scip, &perm0, consdata0->nvars) ); 12372 SCIP_CALL( SCIPallocBufferArray(scip, &perm1, consdata1->nvars) ); 12373 12374 /* call sorting method */ 12375 SCIPsort(perm0, consdataCompVar, (void*)consdata0, consdata0->nvars); 12376 SCIPsort(perm1, consdataCompVar, (void*)consdata1, consdata1->nvars); 12377 12378 for( v0 = 0, v1 = 0; v0 < consdata0->nvars && v1 < consdata1->nvars; ) 12379 { 12380 SCIP_VAR* var0; 12381 SCIP_VAR* var1; 12382 int idx0; 12383 int idx1; 12384 int comp; 12385 12386 idx0 = perm0[v0]; 12387 idx1 = perm1[v1]; 12388 12389 var0 = consdata0->vars[idx0]; 12390 12391 var1 = consdata1->vars[idx1]; 12392 12393 comp = SCIPvarCompare(var0, var1); 12394 12395 if( comp == 0 ) 12396 { 12397 int duration0; 12398 int duration1; 12399 int demand0; 12400 int demand1; 12401 12402 demand0 = consdata0->demands[idx0]; 12403 duration0 = consdata0->durations[idx0]; 12404 12405 demand1 = consdata1->demands[idx1]; 12406 duration1 = consdata1->durations[idx1]; 12407 12408 if( demand0 != demand1 ) 12409 break; 12410 12411 if( duration0 != duration1 ) 12412 break; 12413 12414 v0++; 12415 v1++; 12416 } 12417 else if( comp > 0 ) 12418 v1++; 12419 else 12420 break; 12421 } 12422 12423 if( v0 == consdata0->nvars ) 12424 { 12425 if( SCIPconsIsChecked(cons0) && !SCIPconsIsChecked(cons1) ) 12426 { 12427 initializeLocks(consdata1, TRUE); 12428 } 12429 12430 /* coverity[swapped_arguments] */ 12431 SCIP_CALL( SCIPupdateConsFlags(scip, cons1, cons0) ); 12432 12433 SCIP_CALL( SCIPdelCons(scip, cons0) ); 12434 (*ndelconss)++; 12435 } 12436 12437 SCIPfreeBufferArray(scip, &perm1); 12438 SCIPfreeBufferArray(scip, &perm0); 12439 } 12440 } 12441 } 12442 12443 return SCIP_OKAY; 12444 } 12445 12446 /** strengthen the variable bounds using the cumulative condition */ 12447 static 12448 SCIP_RETCODE strengthenVarbounds( 12449 SCIP* scip, /**< SCIP data structure */ 12450 SCIP_CONS* cons, /**< constraint to propagate */ 12451 int* nchgbds, /**< pointer to store the number of changed bounds */ 12452 int* naddconss /**< pointer to store the number of added constraints */ 12453 ) 12454 { 12455 SCIP_CONSDATA* consdata; 12456 SCIP_VAR** vars; 12457 int* durations; 12458 int* demands; 12459 int capacity; 12460 int nvars; 12461 int nconss; 12462 int i; 12463 12464 consdata = SCIPconsGetData(cons); 12465 assert(consdata != NULL); 12466 12467 /* check if the variable bounds got already strengthen by the cumulative constraint */ 12468 if( consdata->varbounds ) 12469 return SCIP_OKAY; 12470 12471 vars = consdata->vars; 12472 durations = consdata->durations; 12473 demands = consdata->demands; 12474 capacity = consdata->capacity; 12475 nvars = consdata->nvars; 12476 12477 nconss = 0; 12478 12479 for( i = 0; i < nvars && !SCIPisStopped(scip); ++i ) 12480 { 12481 SCIP_VAR** vbdvars; 12482 SCIP_VAR* var; 12483 SCIP_Real* vbdcoefs; 12484 SCIP_Real* vbdconsts; 12485 int nvbdvars; 12486 int b; 12487 int j; 12488 12489 var = consdata->vars[i]; 12490 assert(var != NULL); 12491 12492 vbdvars = SCIPvarGetVlbVars(var); 12493 vbdcoefs = SCIPvarGetVlbCoefs(var); 12494 vbdconsts = SCIPvarGetVlbConstants(var); 12495 nvbdvars = SCIPvarGetNVlbs(var); 12496 12497 for( b = 0; b < nvbdvars; ++b ) 12498 { 12499 if( SCIPisEQ(scip, vbdcoefs[b], 1.0) ) 12500 { 12501 if( SCIPconvertRealToInt(scip, vbdconsts[b]) > -durations[i] ) 12502 { 12503 for( j = 0; j < nvars; ++j ) 12504 { 12505 if( vars[j] == vbdvars[b] ) 12506 break; 12507 } 12508 if( j == nvars ) 12509 continue; 12510 12511 if( demands[i] + demands[j] > capacity && SCIPconvertRealToInt(scip, vbdconsts[b]) < durations[j] ) 12512 { 12513 SCIP_Bool infeasible; 12514 char name[SCIP_MAXSTRLEN]; 12515 int nlocalbdchgs; 12516 12517 SCIPdebugMsg(scip, "<%s>[%d] + %g <= <%s>[%d]\n", SCIPvarGetName(vbdvars[b]), durations[j], vbdconsts[b], SCIPvarGetName(var), durations[i]); 12518 12519 /* construct constraint name */ 12520 (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "varbound_%d_%d", SCIPgetNRuns(scip), nconss); 12521 12522 SCIP_CALL( createPrecedenceCons(scip, name, vars[j], vars[i], durations[j]) ); 12523 nconss++; 12524 12525 SCIP_CALL( SCIPaddVarVlb(scip, var, vbdvars[b], 1.0, (SCIP_Real) durations[j], &infeasible, &nlocalbdchgs) ); 12526 assert(!infeasible); 12527 12528 (*nchgbds) += nlocalbdchgs; 12529 } 12530 } 12531 } 12532 } 12533 } 12534 12535 (*naddconss) += nconss; 12536 12537 consdata->varbounds = TRUE; 12538 12539 return SCIP_OKAY; 12540 } 12541 12542 /** helper function to enforce constraints */ 12543 static 12544 SCIP_RETCODE enforceConstraint( 12545 SCIP* scip, /**< SCIP data structure */ 12546 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 12547 SCIP_CONS** conss, /**< constraints to process */ 12548 int nconss, /**< number of constraints */ 12549 int nusefulconss, /**< number of useful (non-obsolete) constraints to process */ 12550 SCIP_SOL* sol, /**< solution to enforce (NULL for the LP solution) */ 12551 SCIP_Bool solinfeasible, /**< was the solution already declared infeasible by a constraint handler? */ 12552 SCIP_RESULT* result /**< pointer to store the result of the enforcing call */ 12553 ) 12554 { 12555 SCIP_CONSHDLRDATA* conshdlrdata; 12556 12557 assert(conshdlr != NULL); 12558 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0); 12559 assert(nconss == 0 || conss != NULL); 12560 assert(result != NULL); 12561 12562 if( solinfeasible ) 12563 { 12564 *result = SCIP_INFEASIBLE; 12565 return SCIP_OKAY; 12566 } 12567 12568 SCIPdebugMsg(scip, "constraint enforcing %d useful cumulative constraints of %d constraints for %s solution\n", nusefulconss, nconss, 12569 sol == NULL ? "LP" : "relaxation"); 12570 12571 conshdlrdata = SCIPconshdlrGetData(conshdlr); 12572 assert(conshdlrdata != NULL); 12573 12574 (*result) = SCIP_FEASIBLE; 12575 12576 if( conshdlrdata->usebinvars ) 12577 { 12578 SCIP_Bool separated; 12579 SCIP_Bool cutoff; 12580 int c; 12581 12582 separated = FALSE; 12583 12584 /* first check if a constraints is violated */ 12585 for( c = 0; c < nusefulconss; ++c ) 12586 { 12587 SCIP_CONS* cons; 12588 SCIP_Bool violated; 12589 12590 cons = conss[c]; 12591 assert(cons != NULL); 12592 12593 SCIP_CALL( checkCons(scip, cons, sol, &violated, FALSE) ); 12594 12595 if( !violated ) 12596 continue; 12597 12598 SCIP_CALL( separateConsBinaryRepresentation(scip, cons, sol, &separated, &cutoff) ); 12599 if ( cutoff ) 12600 { 12601 *result = SCIP_CUTOFF; 12602 return SCIP_OKAY; 12603 } 12604 } 12605 12606 for( ; c < nconss && !separated; ++c ) 12607 { 12608 SCIP_CONS* cons; 12609 SCIP_Bool violated; 12610 12611 cons = conss[c]; 12612 assert(cons != NULL); 12613 12614 SCIP_CALL( checkCons(scip, cons, sol, &violated, FALSE) ); 12615 12616 if( !violated ) 12617 continue; 12618 12619 SCIP_CALL( separateConsBinaryRepresentation(scip, cons, sol, &separated, &cutoff) ); 12620 if ( cutoff ) 12621 { 12622 *result = SCIP_CUTOFF; 12623 return SCIP_OKAY; 12624 } 12625 } 12626 12627 if( separated ) 12628 (*result) = SCIP_SEPARATED; 12629 } 12630 else 12631 { 12632 SCIP_CALL( enforceSolution(scip, conss, nconss, sol, conshdlrdata->fillbranchcands, result) ); 12633 } 12634 12635 return SCIP_OKAY; 12636 } 12637 12638 /**@} */ 12639 12640 12641 /**@name Callback methods of constraint handler 12642 * 12643 * @{ 12644 */ 12645 12646 /** copy method for constraint handler plugins (called when SCIP copies plugins) */ 12647 static 12648 SCIP_DECL_CONSHDLRCOPY(conshdlrCopyCumulative) 12649 { /*lint --e{715}*/ 12650 assert(scip != NULL); 12651 assert(conshdlr != NULL); 12652 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0); 12653 12654 /* call inclusion method of constraint handler */ 12655 SCIP_CALL( SCIPincludeConshdlrCumulative(scip) ); 12656 12657 SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->iscopy = TRUE ); 12658 12659 *valid = TRUE; 12660 12661 return SCIP_OKAY; 12662 } 12663 12664 /** destructor of constraint handler to free constraint handler data (called when SCIP is exiting) */ 12665 static 12666 SCIP_DECL_CONSFREE(consFreeCumulative) 12667 { /*lint --e{715}*/ 12668 SCIP_CONSHDLRDATA* conshdlrdata; 12669 12670 assert(conshdlr != NULL); 12671 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0); 12672 12673 conshdlrdata = SCIPconshdlrGetData(conshdlr); 12674 assert(conshdlrdata != NULL); 12675 12676 #ifdef SCIP_STATISTIC 12677 if( !conshdlrdata->iscopy ) 12678 { 12679 /* statisitc output if SCIP_STATISTIC is defined */ 12680 SCIPstatisticPrintf("time-table: lb=%" SCIP_LONGINT_FORMAT ", ub=%" SCIP_LONGINT_FORMAT ", cutoff=%" SCIP_LONGINT_FORMAT "\n", 12681 conshdlrdata->nlbtimetable, conshdlrdata->nubtimetable, conshdlrdata->ncutofftimetable); 12682 SCIPstatisticPrintf("edge-finder: lb=%" SCIP_LONGINT_FORMAT ", ub=%" SCIP_LONGINT_FORMAT ", cutoff=%" SCIP_LONGINT_FORMAT "\n", 12683 conshdlrdata->nlbedgefinder, conshdlrdata->nubedgefinder, conshdlrdata->ncutoffedgefinder); 12684 SCIPstatisticPrintf("overload: time-table=%" SCIP_LONGINT_FORMAT " time-time edge-finding=%" SCIP_LONGINT_FORMAT "\n", 12685 conshdlrdata->ncutoffoverload, conshdlrdata->ncutoffoverloadTTEF); 12686 } 12687 #endif 12688 12689 conshdlrdataFree(scip, &conshdlrdata); 12690 12691 SCIPconshdlrSetData(conshdlr, NULL); 12692 12693 return SCIP_OKAY; 12694 } 12695 12696 12697 /** presolving initialization method of constraint handler (called when presolving is about to begin) */ 12698 static 12699 SCIP_DECL_CONSINITPRE(consInitpreCumulative) 12700 { /*lint --e{715}*/ 12701 SCIP_CONSHDLRDATA* conshdlrdata; 12702 int c; 12703 12704 conshdlrdata = SCIPconshdlrGetData(conshdlr); 12705 assert(conshdlrdata != NULL); 12706 12707 conshdlrdata->detectedredundant = FALSE; 12708 12709 for( c = 0; c < nconss; ++c ) 12710 { 12711 /* remove jobs which have a duration or demand of zero (zero energy) or lay outside the effective horizon [hmin, 12712 * hmax) 12713 */ 12714 SCIP_CALL( removeIrrelevantJobs(scip, conss[c]) ); 12715 } 12716 12717 return SCIP_OKAY; 12718 } 12719 12720 12721 /** presolving deinitialization method of constraint handler (called after presolving has been finished) */ 12722 #ifdef SCIP_STATISTIC 12723 static 12724 SCIP_DECL_CONSEXITPRE(consExitpreCumulative) 12725 { /*lint --e{715}*/ 12726 SCIP_CONSHDLRDATA* conshdlrdata; 12727 int c; 12728 12729 conshdlrdata = SCIPconshdlrGetData(conshdlr); 12730 assert(conshdlrdata != NULL); 12731 12732 for( c = 0; c < nconss; ++c ) 12733 { 12734 SCIP_CALL( evaluateCumulativeness(scip, conss[c]) ); 12735 12736 #if 0 12737 SCIP_CALL( SCIPvisualizeConsCumulative(scip, conss[c]) ); 12738 #endif 12739 } 12740 12741 if( !conshdlrdata->iscopy ) 12742 { 12743 SCIPstatisticPrintf("@11 added variables bounds constraints %d\n", conshdlrdata->naddedvarbounds); 12744 SCIPstatisticPrintf("@22 added disjunctive constraints %d\n", conshdlrdata->naddeddisjunctives); 12745 SCIPstatisticPrintf("@33 irrelevant %d\n", conshdlrdata->nirrelevantjobs); 12746 SCIPstatisticPrintf("@44 dual %d\n", conshdlrdata->ndualfixs); 12747 SCIPstatisticPrintf("@55 locks %d\n", conshdlrdata->nremovedlocks); 12748 SCIPstatisticPrintf("@66 decomp %d\n", conshdlrdata->ndecomps); 12749 SCIPstatisticPrintf("@77 allconsdual %d\n", conshdlrdata->nallconsdualfixs); 12750 SCIPstatisticPrintf("@88 alwaysruns %d\n", conshdlrdata->nalwaysruns); 12751 SCIPstatisticPrintf("@99 dualbranch %d\n", conshdlrdata->ndualbranchs); 12752 } 12753 12754 return SCIP_OKAY; 12755 } 12756 #endif 12757 12758 12759 /** solving process deinitialization method of constraint handler (called before branch and bound process data is freed) */ 12760 static 12761 SCIP_DECL_CONSEXITSOL(consExitsolCumulative) 12762 { /*lint --e{715}*/ 12763 SCIP_CONSDATA* consdata; 12764 int c; 12765 12766 assert(conshdlr != NULL); 12767 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0); 12768 12769 /* release the rows of all constraints */ 12770 for( c = 0; c < nconss; ++c ) 12771 { 12772 consdata = SCIPconsGetData(conss[c]); 12773 assert(consdata != NULL); 12774 12775 /* free rows */ 12776 SCIP_CALL( consdataFreeRows(scip, &consdata) ); 12777 } 12778 12779 return SCIP_OKAY; 12780 } 12781 12782 /** frees specific constraint data */ 12783 static 12784 SCIP_DECL_CONSDELETE(consDeleteCumulative) 12785 { /*lint --e{715}*/ 12786 assert(conshdlr != NULL); 12787 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0); 12788 assert(consdata != NULL ); 12789 assert(*consdata != NULL ); 12790 12791 /* if constraint belongs to transformed problem space, drop bound change events on variables */ 12792 if( (*consdata)->nvars > 0 && SCIPvarIsTransformed((*consdata)->vars[0]) ) 12793 { 12794 SCIP_CONSHDLRDATA* conshdlrdata; 12795 12796 conshdlrdata = SCIPconshdlrGetData(conshdlr); 12797 assert(conshdlrdata != NULL); 12798 12799 SCIP_CALL( consdataDropAllEvents(scip, *consdata, conshdlrdata->eventhdlr) ); 12800 } 12801 12802 /* free cumulative constraint data */ 12803 SCIP_CALL( consdataFree(scip, consdata) ); 12804 12805 return SCIP_OKAY; 12806 } 12807 12808 /** transforms constraint data into data belonging to the transformed problem */ 12809 static 12810 SCIP_DECL_CONSTRANS(consTransCumulative) 12811 { /*lint --e{715}*/ 12812 SCIP_CONSHDLRDATA* conshdlrdata; 12813 SCIP_CONSDATA* sourcedata; 12814 SCIP_CONSDATA* targetdata; 12815 12816 assert(conshdlr != NULL); 12817 assert(SCIPgetStage(scip) == SCIP_STAGE_TRANSFORMING); 12818 assert(sourcecons != NULL); 12819 assert(targetcons != NULL); 12820 12821 sourcedata = SCIPconsGetData(sourcecons); 12822 assert(sourcedata != NULL); 12823 assert(sourcedata->demandrows == NULL); 12824 12825 SCIPdebugMsg(scip, "transform cumulative constraint <%s>\n", SCIPconsGetName(sourcecons)); 12826 12827 /* get event handler */ 12828 conshdlrdata = SCIPconshdlrGetData(conshdlr); 12829 assert(conshdlrdata != NULL); 12830 assert(conshdlrdata->eventhdlr != NULL); 12831 12832 /* create constraint data for target constraint */ 12833 SCIP_CALL( consdataCreate(scip, &targetdata, sourcedata->vars, sourcedata->linkingconss, 12834 sourcedata->durations, sourcedata->demands, sourcedata->nvars, sourcedata->capacity, 12835 sourcedata->hmin, sourcedata->hmax, SCIPconsIsChecked(sourcecons)) ); 12836 12837 /* create target constraint */ 12838 SCIP_CALL( SCIPcreateCons(scip, targetcons, SCIPconsGetName(sourcecons), conshdlr, targetdata, 12839 SCIPconsIsInitial(sourcecons), SCIPconsIsSeparated(sourcecons), SCIPconsIsEnforced(sourcecons), 12840 SCIPconsIsChecked(sourcecons), SCIPconsIsPropagated(sourcecons), 12841 SCIPconsIsLocal(sourcecons), SCIPconsIsModifiable(sourcecons), 12842 SCIPconsIsDynamic(sourcecons), SCIPconsIsRemovable(sourcecons), SCIPconsIsStickingAtNode(sourcecons)) ); 12843 12844 /* catch bound change events of variables */ 12845 SCIP_CALL( consdataCatchEvents(scip, targetdata, conshdlrdata->eventhdlr) ); 12846 12847 return SCIP_OKAY; 12848 } 12849 12850 /** LP initialization method of constraint handler */ 12851 static 12852 SCIP_DECL_CONSINITLP(consInitlpCumulative) 12853 { 12854 SCIP_CONSHDLRDATA* conshdlrdata; 12855 int c; 12856 12857 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0); 12858 assert(conshdlr != NULL); 12859 conshdlrdata = SCIPconshdlrGetData(conshdlr); 12860 assert(conshdlrdata != NULL); 12861 12862 *infeasible = FALSE; 12863 12864 SCIPdebugMsg(scip, "initialize LP relaxation for %d cumulative constraints\n", nconss); 12865 12866 if( conshdlrdata->usebinvars ) 12867 { 12868 /* add rows to LP */ 12869 for( c = 0; c < nconss && !(*infeasible); ++c ) 12870 { 12871 assert(SCIPconsIsInitial(conss[c])); 12872 SCIP_CALL( addRelaxation(scip, conss[c], conshdlrdata->cutsasconss, infeasible) ); 12873 12874 if( conshdlrdata->cutsasconss ) 12875 { 12876 SCIP_CALL( SCIPrestartSolve(scip) ); 12877 } 12878 } 12879 } 12880 12881 /**@todo if we want to use only the integer variables; only these will be in cuts 12882 * create some initial cuts, currently these are only separated */ 12883 12884 return SCIP_OKAY; 12885 } 12886 12887 /** separation method of constraint handler for LP solutions */ 12888 static 12889 SCIP_DECL_CONSSEPALP(consSepalpCumulative) 12890 { 12891 SCIP_CONSHDLRDATA* conshdlrdata; 12892 SCIP_Bool cutoff; 12893 SCIP_Bool separated; 12894 int c; 12895 12896 SCIPdebugMsg(scip, "consSepalpCumulative\n"); 12897 12898 assert(conshdlr != NULL); 12899 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0); 12900 assert(nconss == 0 || conss != NULL); 12901 assert(result != NULL); 12902 conshdlrdata = SCIPconshdlrGetData(conshdlr); 12903 assert(conshdlrdata != NULL); 12904 12905 SCIPdebugMsg(scip, "separating %d/%d cumulative constraints\n", nusefulconss, nconss); 12906 12907 cutoff = FALSE; 12908 separated = FALSE; 12909 (*result) = SCIP_DIDNOTRUN; 12910 12911 if( !conshdlrdata->localcuts && SCIPgetDepth(scip) > 0 ) 12912 return SCIP_OKAY; 12913 12914 (*result) = SCIP_DIDNOTFIND; 12915 12916 if( conshdlrdata->usebinvars ) 12917 { 12918 /* check all useful cumulative constraints for feasibility */ 12919 for( c = 0; c < nusefulconss && !cutoff; ++c ) 12920 { 12921 SCIP_CALL( separateConsBinaryRepresentation(scip, conss[c], NULL, &separated, &cutoff) ); 12922 } 12923 12924 if( !cutoff && conshdlrdata->usecovercuts ) 12925 { 12926 for( c = 0; c < nusefulconss; ++c ) 12927 { 12928 SCIP_CALL( separateCoverCutsCons(scip, conss[c], NULL, &separated, &cutoff) ); 12929 } 12930 } 12931 } 12932 12933 if( conshdlrdata->sepaold ) 12934 { 12935 /* separate cuts containing only integer variables */ 12936 for( c = 0; c < nusefulconss; ++c ) 12937 { 12938 SCIP_CALL( separateConsOnIntegerVariables(scip, conss[c], NULL, TRUE, &separated, &cutoff) ); 12939 SCIP_CALL( separateConsOnIntegerVariables(scip, conss[c], NULL, FALSE, &separated, &cutoff) ); 12940 } 12941 } 12942 12943 if( cutoff ) 12944 *result = SCIP_CUTOFF; 12945 else if( separated ) 12946 *result = SCIP_SEPARATED; 12947 12948 return SCIP_OKAY; 12949 } 12950 12951 /** separation method of constraint handler for arbitrary primal solutions */ 12952 static 12953 SCIP_DECL_CONSSEPASOL(consSepasolCumulative) 12954 { /*lint --e{715}*/ 12955 SCIP_CONSHDLRDATA* conshdlrdata; 12956 SCIP_Bool cutoff; 12957 SCIP_Bool separated; 12958 int c; 12959 12960 assert(conshdlr != NULL); 12961 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0); 12962 assert(nconss == 0 || conss != NULL); 12963 assert(result != NULL); 12964 12965 conshdlrdata = SCIPconshdlrGetData(conshdlr); 12966 assert(conshdlrdata != NULL); 12967 12968 if( !conshdlrdata->localcuts && SCIPgetDepth(scip) > 0 ) 12969 return SCIP_OKAY; 12970 12971 SCIPdebugMsg(scip, "separating %d/%d cumulative constraints\n", nusefulconss, nconss); 12972 12973 cutoff = FALSE; 12974 separated = FALSE; 12975 (*result) = SCIP_DIDNOTFIND; 12976 12977 if( conshdlrdata->usebinvars ) 12978 { 12979 /* check all useful cumulative constraints for feasibility */ 12980 for( c = 0; c < nusefulconss && !cutoff; ++c ) 12981 { 12982 SCIP_CALL( separateConsBinaryRepresentation(scip, conss[c], NULL, &separated, &cutoff) ); 12983 } 12984 12985 if( !cutoff && conshdlrdata->usecovercuts ) 12986 { 12987 for( c = 0; c < nusefulconss; ++c ) 12988 { 12989 SCIP_CALL( separateCoverCutsCons(scip, conss[c], sol, &separated, &cutoff) ); 12990 } 12991 } 12992 } 12993 if( conshdlrdata->sepaold ) 12994 { 12995 /* separate cuts containing only integer variables */ 12996 for( c = 0; c < nusefulconss; ++c ) 12997 { 12998 SCIP_CALL( separateConsOnIntegerVariables(scip, conss[c], NULL, TRUE, &separated, &cutoff) ); 12999 SCIP_CALL( separateConsOnIntegerVariables(scip, conss[c], NULL, FALSE, &separated, &cutoff) ); 13000 } 13001 } 13002 13003 if( cutoff ) 13004 *result = SCIP_CUTOFF; 13005 else if( separated ) 13006 *result = SCIP_SEPARATED; 13007 13008 return SCIP_OKAY; 13009 } 13010 13011 /** constraint enforcing method of constraint handler for LP solutions */ 13012 static 13013 SCIP_DECL_CONSENFOLP(consEnfolpCumulative) 13014 { /*lint --e{715}*/ 13015 SCIP_CALL( enforceConstraint(scip, conshdlr, conss, nconss, nusefulconss, NULL, solinfeasible, result) ); 13016 13017 return SCIP_OKAY; 13018 } 13019 13020 /** constraint enforcing method of constraint handler for relaxation solutions */ 13021 static 13022 SCIP_DECL_CONSENFORELAX(consEnforelaxCumulative) 13023 { /*lint --e{715}*/ 13024 SCIP_CALL( enforceConstraint(scip, conshdlr, conss, nconss, nusefulconss, sol, solinfeasible, result) ); 13025 13026 return SCIP_OKAY; 13027 } 13028 13029 /** constraint enforcing method of constraint handler for pseudo solutions */ 13030 static 13031 SCIP_DECL_CONSENFOPS(consEnfopsCumulative) 13032 { /*lint --e{715}*/ 13033 SCIP_CONSHDLRDATA* conshdlrdata; 13034 13035 SCIPdebugMsg(scip, "method: enforce pseudo solution\n"); 13036 13037 assert(conshdlr != NULL); 13038 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0); 13039 assert(nconss == 0 || conss != NULL); 13040 assert(result != NULL); 13041 13042 if( objinfeasible ) 13043 { 13044 *result = SCIP_DIDNOTRUN; 13045 return SCIP_OKAY; 13046 } 13047 13048 (*result) = SCIP_FEASIBLE; 13049 13050 conshdlrdata = SCIPconshdlrGetData(conshdlr); 13051 assert(conshdlrdata != NULL); 13052 13053 SCIP_CALL( enforceSolution(scip, conss, nconss, NULL, conshdlrdata->fillbranchcands, result) ); 13054 13055 return SCIP_OKAY; 13056 } 13057 13058 /** feasibility check method of constraint handler for integral solutions */ 13059 static 13060 SCIP_DECL_CONSCHECK(consCheckCumulative) 13061 { /*lint --e{715}*/ 13062 int c; 13063 13064 assert(conshdlr != NULL); 13065 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0); 13066 assert(nconss == 0 || conss != NULL); 13067 assert(result != NULL); 13068 13069 *result = SCIP_FEASIBLE; 13070 13071 SCIPdebugMsg(scip, "check %d cumulative constraints\n", nconss); 13072 13073 for( c = 0; c < nconss && (*result == SCIP_FEASIBLE || completely); ++c ) 13074 { 13075 SCIP_Bool violated = FALSE; 13076 13077 SCIP_CALL( checkCons(scip, conss[c], sol, &violated, printreason) ); 13078 13079 if( violated ) 13080 *result = SCIP_INFEASIBLE; 13081 } 13082 13083 return SCIP_OKAY; 13084 } 13085 13086 /** domain propagation method of constraint handler */ 13087 static 13088 SCIP_DECL_CONSPROP(consPropCumulative) 13089 { /*lint --e{715}*/ 13090 SCIP_CONSHDLRDATA* conshdlrdata; 13091 SCIP_Bool cutoff; 13092 int nchgbds; 13093 int ndelconss; 13094 int c; 13095 #if 0 13096 int naggrvars = 0; 13097 #endif 13098 13099 SCIPdebugMsg(scip, "propagate %d of %d useful cumulative constraints\n", nusefulconss, nconss); 13100 13101 assert(conshdlr != NULL); 13102 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0); 13103 assert(nconss == 0 || conss != NULL); 13104 assert(result != NULL); 13105 13106 conshdlrdata = SCIPconshdlrGetData(conshdlr); 13107 assert(conshdlrdata != NULL); 13108 13109 nchgbds = 0; 13110 ndelconss = 0; 13111 cutoff = FALSE; 13112 (*result) = SCIP_DIDNOTRUN; 13113 13114 /* propgate all useful constraints */ 13115 for( c = 0; c < nusefulconss && !cutoff; ++c ) 13116 { 13117 SCIP_CONS* cons; 13118 13119 cons = conss[c]; 13120 assert(cons != NULL); 13121 13122 if( SCIPgetDepth(scip) == 0 ) 13123 { 13124 #if 0 13125 SCIP_CALL( presolveCons(scip, cons, conshdlrdata, SCIP_PRESOLTIMING_ALWAYS, 13126 &nchgbds, &naggrvars, &nchgbds, &ndelconss, &nchgbds, &nchgbds, &nchgbds, &cutoff, &cutoff) ); 13127 #else 13128 SCIP_CALL( presolveCons(scip, cons, conshdlrdata, SCIP_PRESOLTIMING_ALWAYS, 13129 &nchgbds, &nchgbds, &ndelconss, &nchgbds, &nchgbds, &nchgbds, &cutoff, &cutoff) ); 13130 #endif 13131 if( cutoff ) 13132 break; 13133 13134 if( SCIPconsIsDeleted(cons) ) 13135 continue; 13136 } 13137 13138 SCIP_CALL( propagateCons(scip, cons, conshdlrdata, SCIP_PRESOLTIMING_ALWAYS, &nchgbds, &ndelconss, &cutoff) ); 13139 } 13140 13141 if( !cutoff && nchgbds == 0 ) 13142 { 13143 /* propgate all other constraints */ 13144 for( c = nusefulconss; c < nconss && !cutoff; ++c ) 13145 { 13146 SCIP_CALL( propagateCons(scip, conss[c], conshdlrdata, SCIP_PRESOLTIMING_ALWAYS, &nchgbds, &ndelconss, &cutoff) ); 13147 } 13148 } 13149 13150 #if 0 13151 if( !cutoff && conshdlrdata->dualpresolve && SCIPallowStrongDualReds(scip) && nconss > 1 ) 13152 { 13153 SCIP_CALL( propagateAllConss(scip, conss, nconss, TRUE, &nchgbds, &cutoff, NULL) ); 13154 } 13155 #endif 13156 13157 if( cutoff ) 13158 { 13159 SCIPdebugMsg(scip, "detected infeasible\n"); 13160 *result = SCIP_CUTOFF; 13161 } 13162 else if( nchgbds > 0 ) 13163 { 13164 SCIPdebugMsg(scip, "delete (locally) %d constraints and changed %d variable bounds\n", ndelconss, nchgbds); 13165 *result = SCIP_REDUCEDDOM; 13166 } 13167 else 13168 *result = SCIP_DIDNOTFIND; 13169 13170 return SCIP_OKAY; 13171 } 13172 13173 /** presolving method of constraint handler */ 13174 static 13175 SCIP_DECL_CONSPRESOL(consPresolCumulative) 13176 { /*lint --e{715}*/ 13177 SCIP_CONSHDLRDATA* conshdlrdata; 13178 SCIP_CONS* cons; 13179 SCIP_Bool cutoff; 13180 SCIP_Bool unbounded; 13181 int oldnfixedvars; 13182 int oldnchgbds; 13183 int oldndelconss; 13184 int oldnaddconss; 13185 int oldnupgdconss; 13186 int oldnchgsides; 13187 int oldnchgcoefs; 13188 int c; 13189 13190 assert(conshdlr != NULL); 13191 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0); 13192 assert(scip != NULL); 13193 assert(result != NULL); 13194 13195 SCIPdebugMsg(scip, "presolve %d cumulative constraints\n", nconss); 13196 13197 conshdlrdata = SCIPconshdlrGetData(conshdlr); 13198 assert(conshdlrdata != NULL); 13199 13200 *result = SCIP_DIDNOTRUN; 13201 13202 oldnfixedvars = *nfixedvars; 13203 oldnchgbds = *nchgbds; 13204 oldnchgsides = *nchgsides; 13205 oldnchgcoefs = *nchgcoefs; 13206 oldnupgdconss = *nupgdconss; 13207 oldndelconss = *ndelconss; 13208 oldnaddconss = *naddconss; 13209 cutoff = FALSE; 13210 unbounded = FALSE; 13211 13212 /* process constraints */ 13213 for( c = 0; c < nconss && !cutoff; ++c ) 13214 { 13215 cons = conss[c]; 13216 13217 /* remove jobs which have a duration or demand of zero (zero energy) or lay outside the effective horizon [hmin, 13218 * hmax) 13219 */ 13220 SCIP_CALL( removeIrrelevantJobs(scip, conss[c]) ); 13221 13222 if( presoltiming != SCIP_PRESOLTIMING_MEDIUM ) 13223 { 13224 #if 0 13225 SCIP_CALL( presolveCons(scip, cons, conshdlrdata, presoltiming, 13226 nfixedvars, naggrvars, nchgbds, ndelconss, naddconss, nchgcoefs, nchgsides, &cutoff, &unbounded) ); 13227 #else 13228 SCIP_CALL( presolveCons(scip, cons, conshdlrdata, presoltiming, 13229 nfixedvars, nchgbds, ndelconss, naddconss, nchgcoefs, nchgsides, &cutoff, &unbounded) ); 13230 #endif 13231 13232 if( cutoff || unbounded ) 13233 break; 13234 13235 if( SCIPconsIsDeleted(cons) ) 13236 continue; 13237 } 13238 13239 /* in the first round we create a disjunctive constraint containing those jobs which cannot run in parallel */ 13240 if( nrounds == 1 && SCIPgetNRuns(scip) == 1 && conshdlrdata->disjunctive ) 13241 { 13242 SCIP_CALL( createDisjuctiveCons(scip, cons, naddconss) ); 13243 } 13244 13245 /* strengthen existing variable bounds using the cumulative condition */ 13246 if( (presoltiming & SCIP_PRESOLTIMING_MEDIUM) != 0 ) 13247 { 13248 SCIP_CALL( strengthenVarbounds(scip, cons, nchgbds, naddconss) ); 13249 } 13250 13251 /* propagate cumulative constraint */ 13252 SCIP_CALL( propagateCons(scip, cons, conshdlrdata, presoltiming, nchgbds, ndelconss, &cutoff) ); 13253 assert(checkDemands(scip, cons) || cutoff); 13254 } 13255 13256 if( !cutoff && !unbounded && conshdlrdata->dualpresolve && SCIPallowStrongDualReds(scip) && nconss > 1 && (presoltiming & SCIP_PRESOLTIMING_FAST) != 0 ) 13257 { 13258 SCIP_CALL( propagateAllConss(scip, conss, nconss, FALSE, nfixedvars, &cutoff, NULL) ); 13259 } 13260 13261 /* only perform the detection of variable bounds and disjunctive constraint once */ 13262 if( !cutoff && SCIPgetNRuns(scip) == 1 && !conshdlrdata->detectedredundant 13263 && (conshdlrdata->detectvarbounds || conshdlrdata->detectdisjunctive) 13264 && (presoltiming & SCIP_PRESOLTIMING_EXHAUSTIVE) != 0 ) 13265 { 13266 /* combine different source and detect disjunctive constraints and variable bound constraints to improve the 13267 * propagation 13268 */ 13269 SCIP_CALL( detectRedundantConss(scip, conshdlrdata, conss, nconss, naddconss) ); 13270 conshdlrdata->detectedredundant = TRUE; 13271 } 13272 13273 if( !cutoff && conshdlrdata->presolpairwise && (presoltiming & SCIP_PRESOLTIMING_MEDIUM) != 0 ) 13274 { 13275 SCIP_CALL( removeRedundantConss(scip, conss, nconss, ndelconss) ); 13276 } 13277 13278 SCIPdebugMsg(scip, "delete %d constraints and changed %d variable bounds (cutoff %u)\n", 13279 *ndelconss - oldndelconss, *nchgbds - oldnchgbds, cutoff); 13280 13281 if( cutoff ) 13282 *result = SCIP_CUTOFF; 13283 else if( unbounded ) 13284 *result = SCIP_UNBOUNDED; 13285 else if( *nchgbds > oldnchgbds || *nfixedvars > oldnfixedvars || *nchgsides > oldnchgsides 13286 || *nchgcoefs > oldnchgcoefs || *nupgdconss > oldnupgdconss || *ndelconss > oldndelconss || *naddconss > oldnaddconss ) 13287 *result = SCIP_SUCCESS; 13288 else 13289 *result = SCIP_DIDNOTFIND; 13290 13291 return SCIP_OKAY; 13292 } 13293 13294 /** propagation conflict resolving method of constraint handler */ 13295 static 13296 SCIP_DECL_CONSRESPROP(consRespropCumulative) 13297 { /*lint --e{715}*/ 13298 SCIP_CONSHDLRDATA* conshdlrdata; 13299 SCIP_CONSDATA* consdata; 13300 13301 assert(conshdlr != NULL); 13302 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0); 13303 assert(scip != NULL); 13304 assert(result != NULL); 13305 assert(infervar != NULL); 13306 assert(bdchgidx != NULL); 13307 13308 conshdlrdata = SCIPconshdlrGetData(conshdlr); 13309 assert(conshdlrdata != NULL); 13310 13311 /* process constraint */ 13312 assert(cons != NULL); 13313 13314 consdata = SCIPconsGetData(cons); 13315 assert(consdata != NULL); 13316 13317 SCIPdebugMsg(scip, "resolve propagation: variable <%s>, cumulative constraint <%s> (capacity %d, propagation %d, H=[%d,%d))\n", 13318 SCIPvarGetName(infervar), SCIPconsGetName(cons), consdata->capacity, inferInfoGetProprule(intToInferInfo(inferinfo)), 13319 SCIPgetHminCumulative(scip, cons), SCIPgetHmaxCumulative(scip, cons)); 13320 13321 SCIP_CALL( respropCumulativeCondition(scip, consdata->nvars, consdata->vars, 13322 consdata->durations, consdata->demands, consdata->capacity, consdata->hmin, consdata->hmax, 13323 infervar, intToInferInfo(inferinfo), boundtype, bdchgidx, relaxedbd, conshdlrdata->usebdwidening, NULL, result) ); 13324 13325 return SCIP_OKAY; 13326 } 13327 13328 /** variable rounding lock method of constraint handler */ 13329 static 13330 SCIP_DECL_CONSLOCK(consLockCumulative) 13331 { /*lint --e{715}*/ 13332 SCIP_CONSDATA* consdata; 13333 SCIP_VAR** vars; 13334 int v; 13335 13336 SCIPdebugMsg(scip, "lock cumulative constraint <%s> with nlockspos = %d, nlocksneg = %d\n", SCIPconsGetName(cons), nlockspos, nlocksneg); 13337 13338 assert(scip != NULL); 13339 assert(cons != NULL); 13340 assert(locktype == SCIP_LOCKTYPE_MODEL); 13341 13342 consdata = SCIPconsGetData(cons); 13343 assert(consdata != NULL); 13344 13345 vars = consdata->vars; 13346 assert(vars != NULL); 13347 13348 for( v = 0; v < consdata->nvars; ++v ) 13349 { 13350 if( consdata->downlocks[v] && consdata->uplocks[v] ) 13351 { 13352 /* the integer start variable should not get rounded in both direction */ 13353 SCIP_CALL( SCIPaddVarLocksType(scip, vars[v], locktype, nlockspos + nlocksneg, nlockspos + nlocksneg) ); 13354 } 13355 else if( consdata->downlocks[v] ) 13356 { 13357 SCIP_CALL( SCIPaddVarLocksType(scip, vars[v], locktype, nlockspos, nlocksneg) ); 13358 } 13359 else if( consdata->uplocks[v] ) 13360 { 13361 SCIP_CALL( SCIPaddVarLocksType(scip, vars[v], locktype, nlocksneg, nlockspos) ); 13362 } 13363 } 13364 13365 return SCIP_OKAY; 13366 } 13367 13368 13369 /** constraint display method of constraint handler */ 13370 static 13371 SCIP_DECL_CONSPRINT(consPrintCumulative) 13372 { /*lint --e{715}*/ 13373 assert(scip != NULL); 13374 assert(conshdlr != NULL); 13375 assert(cons != NULL); 13376 13377 consdataPrint(scip, SCIPconsGetData(cons), file); 13378 13379 return SCIP_OKAY; 13380 } 13381 13382 /** constraint copying method of constraint handler */ 13383 static 13384 SCIP_DECL_CONSCOPY(consCopyCumulative) 13385 { /*lint --e{715}*/ 13386 SCIP_CONSDATA* sourceconsdata; 13387 SCIP_VAR** sourcevars; 13388 SCIP_VAR** vars; 13389 const char* consname; 13390 13391 int nvars; 13392 int v; 13393 13394 sourceconsdata = SCIPconsGetData(sourcecons); 13395 assert(sourceconsdata != NULL); 13396 13397 /* get variables of the source constraint */ 13398 nvars = sourceconsdata->nvars; 13399 sourcevars = sourceconsdata->vars; 13400 13401 (*valid) = TRUE; 13402 13403 if( nvars == 0 ) 13404 return SCIP_OKAY; 13405 13406 /* allocate buffer array */ 13407 SCIP_CALL( SCIPallocBufferArray(scip, &vars, nvars) ); 13408 13409 for( v = 0; v < nvars && *valid; ++v ) 13410 { 13411 SCIP_CALL( SCIPgetVarCopy(sourcescip, scip, sourcevars[v], &vars[v], varmap, consmap, global, valid) ); 13412 assert(!(*valid) || vars[v] != NULL); 13413 } 13414 13415 /* only create the target constraint, if all variables could be copied */ 13416 if( *valid ) 13417 { 13418 if( name != NULL ) 13419 consname = name; 13420 else 13421 consname = SCIPconsGetName(sourcecons); 13422 13423 /* create a copy of the cumulative constraint */ 13424 SCIP_CALL( SCIPcreateConsCumulative(scip, cons, consname, nvars, vars, 13425 sourceconsdata->durations, sourceconsdata->demands, sourceconsdata->capacity, 13426 initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode) ); 13427 13428 /* adjust left side if the time axis if needed */ 13429 if( sourceconsdata->hmin > 0 ) 13430 { 13431 SCIP_CALL( SCIPsetHminCumulative(scip, *cons, sourceconsdata->hmin) ); 13432 } 13433 13434 /* adjust right side if the time axis if needed */ 13435 if( sourceconsdata->hmax < INT_MAX ) 13436 { 13437 SCIP_CALL( SCIPsetHmaxCumulative(scip, *cons, sourceconsdata->hmax) ); 13438 } 13439 } 13440 13441 /* free buffer array */ 13442 SCIPfreeBufferArray(scip, &vars); 13443 13444 return SCIP_OKAY; 13445 } 13446 13447 13448 /** constraint parsing method of constraint handler */ 13449 static 13450 SCIP_DECL_CONSPARSE(consParseCumulative) 13451 { /*lint --e{715}*/ 13452 SCIP_VAR** vars; 13453 SCIP_VAR* var; 13454 SCIP_Real value; 13455 char strvalue[SCIP_MAXSTRLEN]; 13456 char* endptr; 13457 int* demands; 13458 int* durations; 13459 int capacity; 13460 int duration; 13461 int demand; 13462 int hmin; 13463 int hmax; 13464 int varssize; 13465 int nvars; 13466 13467 SCIPdebugMsg(scip, "parse <%s> as cumulative constraint\n", str); 13468 13469 *success = TRUE; 13470 13471 /* cutoff "cumulative" form the constraint string */ 13472 SCIPstrCopySection(str, 'c', '(', strvalue, SCIP_MAXSTRLEN, &endptr); 13473 str = endptr; 13474 13475 varssize = 100; 13476 nvars = 0; 13477 13478 /* allocate buffer array for variables */ 13479 SCIP_CALL( SCIPallocBufferArray(scip, &vars, varssize) ); 13480 SCIP_CALL( SCIPallocBufferArray(scip, &demands, varssize) ); 13481 SCIP_CALL( SCIPallocBufferArray(scip, &durations, varssize) ); 13482 13483 do 13484 { 13485 SCIP_CALL( SCIPparseVarName(scip, str, &var, &endptr) ); 13486 13487 if( var == NULL ) 13488 { 13489 endptr = strchr(endptr, ')'); 13490 13491 if( endptr == NULL ) 13492 *success = FALSE; 13493 else 13494 str = endptr; 13495 13496 break; 13497 } 13498 13499 str = endptr; 13500 SCIPstrCopySection(str, '(', ')', strvalue, SCIP_MAXSTRLEN, &endptr); 13501 duration = atoi(strvalue); 13502 str = endptr; 13503 13504 SCIPstrCopySection(str, '[', ']', strvalue, SCIP_MAXSTRLEN, &endptr); 13505 demand = atoi(strvalue); 13506 str = endptr; 13507 13508 SCIPdebugMsg(scip, "parse job <%s>, duration %d, demand %d\n", SCIPvarGetName(var), duration, demand); 13509 13510 vars[nvars] = var; 13511 demands[nvars] = demand; 13512 durations[nvars] = duration; 13513 nvars++; 13514 } 13515 while( *str != ')' ); 13516 13517 if( *success ) 13518 { 13519 /* parse effective time window */ 13520 SCIPstrCopySection(str, '[', ',', strvalue, SCIP_MAXSTRLEN, &endptr); 13521 hmin = atoi(strvalue); 13522 str = endptr; 13523 13524 if( SCIPparseReal(scip, str, &value, &endptr) ) 13525 { 13526 hmax = (int)(value); 13527 str = endptr; 13528 13529 /* parse capacity */ 13530 SCIPstrCopySection(str, ')', '=', strvalue, SCIP_MAXSTRLEN, &endptr); 13531 str = endptr; 13532 if( SCIPparseReal(scip, str, &value, &endptr) ) 13533 { 13534 capacity = (int)value; 13535 13536 /* create cumulative constraint */ 13537 SCIP_CALL( SCIPcreateConsCumulative(scip, cons, name, nvars, vars, durations, demands, capacity, 13538 initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode) ); 13539 13540 SCIP_CALL( SCIPsetHminCumulative(scip, *cons, hmin) ); 13541 SCIP_CALL( SCIPsetHmaxCumulative(scip, *cons, hmax) ); 13542 } 13543 } 13544 } 13545 13546 /* free buffer arrays */ 13547 SCIPfreeBufferArray(scip, &durations); 13548 SCIPfreeBufferArray(scip, &demands); 13549 SCIPfreeBufferArray(scip, &vars); 13550 13551 return SCIP_OKAY; 13552 } 13553 13554 13555 /** constraint method of constraint handler which returns the variables (if possible) */ 13556 static 13557 SCIP_DECL_CONSGETVARS(consGetVarsCumulative) 13558 { /*lint --e{715}*/ 13559 SCIP_CONSDATA* consdata; 13560 13561 consdata = SCIPconsGetData(cons); 13562 assert(consdata != NULL); 13563 13564 if( varssize < consdata->nvars ) 13565 (*success) = FALSE; 13566 else 13567 { 13568 assert(vars != NULL); 13569 13570 BMScopyMemoryArray(vars, consdata->vars, consdata->nvars); 13571 (*success) = TRUE; 13572 } 13573 13574 return SCIP_OKAY; 13575 } 13576 13577 /** constraint method of constraint handler which returns the number of variables (if possible) */ 13578 static 13579 SCIP_DECL_CONSGETNVARS(consGetNVarsCumulative) 13580 { /*lint --e{715}*/ 13581 SCIP_CONSDATA* consdata; 13582 13583 consdata = SCIPconsGetData(cons); 13584 assert(consdata != NULL); 13585 13586 (*nvars) = consdata->nvars; 13587 (*success) = TRUE; 13588 13589 return SCIP_OKAY; 13590 } 13591 13592 /**@} */ 13593 13594 /**@name Callback methods of event handler 13595 * 13596 * @{ 13597 */ 13598 13599 13600 /** execution method of event handler */ 13601 static 13602 SCIP_DECL_EVENTEXEC(eventExecCumulative) 13603 { /*lint --e{715}*/ 13604 SCIP_CONSDATA* consdata; 13605 13606 assert(scip != NULL); 13607 assert(eventhdlr != NULL); 13608 assert(eventdata != NULL); 13609 assert(strcmp(SCIPeventhdlrGetName(eventhdlr), EVENTHDLR_NAME) == 0); 13610 assert(event != NULL); 13611 13612 consdata = (SCIP_CONSDATA*)eventdata; 13613 assert(consdata != NULL); 13614 13615 /* mark the constraint to be not propagated */ 13616 consdata->propagated = FALSE; 13617 13618 return SCIP_OKAY; 13619 } 13620 13621 /**@} */ 13622 13623 /* 13624 * constraint specific interface methods 13625 */ 13626 13627 /** creates the handler for cumulative constraints and includes it in SCIP */ 13628 SCIP_RETCODE SCIPincludeConshdlrCumulative( 13629 SCIP* scip /**< SCIP data structure */ 13630 ) 13631 { 13632 SCIP_CONSHDLRDATA* conshdlrdata; 13633 SCIP_CONSHDLR* conshdlr; 13634 SCIP_EVENTHDLR* eventhdlr; 13635 13636 /* create event handler for bound change events */ 13637 SCIP_CALL( SCIPincludeEventhdlrBasic(scip, &eventhdlr, EVENTHDLR_NAME, EVENTHDLR_DESC, eventExecCumulative, NULL) ); 13638 13639 /* create cumulative constraint handler data */ 13640 SCIP_CALL( conshdlrdataCreate(scip, &conshdlrdata, eventhdlr) ); 13641 13642 /* include constraint handler */ 13643 SCIP_CALL( SCIPincludeConshdlrBasic(scip, &conshdlr, CONSHDLR_NAME, CONSHDLR_DESC, 13644 CONSHDLR_ENFOPRIORITY, CONSHDLR_CHECKPRIORITY, CONSHDLR_EAGERFREQ, CONSHDLR_NEEDSCONS, 13645 consEnfolpCumulative, consEnfopsCumulative, consCheckCumulative, consLockCumulative, 13646 conshdlrdata) ); 13647 13648 assert(conshdlr != NULL); 13649 13650 /* set non-fundamental callbacks via specific setter functions */ 13651 SCIP_CALL( SCIPsetConshdlrCopy(scip, conshdlr, conshdlrCopyCumulative, consCopyCumulative) ); 13652 SCIP_CALL( SCIPsetConshdlrDelete(scip, conshdlr, consDeleteCumulative) ); 13653 #ifdef SCIP_STATISTIC 13654 SCIP_CALL( SCIPsetConshdlrExitpre(scip, conshdlr, consExitpreCumulative) ); 13655 #endif 13656 SCIP_CALL( SCIPsetConshdlrExitsol(scip, conshdlr, consExitsolCumulative) ); 13657 SCIP_CALL( SCIPsetConshdlrFree(scip, conshdlr, consFreeCumulative) ); 13658 SCIP_CALL( SCIPsetConshdlrGetVars(scip, conshdlr, consGetVarsCumulative) ); 13659 SCIP_CALL( SCIPsetConshdlrGetNVars(scip, conshdlr, consGetNVarsCumulative) ); 13660 SCIP_CALL( SCIPsetConshdlrInitpre(scip, conshdlr, consInitpreCumulative) ); 13661 SCIP_CALL( SCIPsetConshdlrInitlp(scip, conshdlr, consInitlpCumulative) ); 13662 SCIP_CALL( SCIPsetConshdlrParse(scip, conshdlr, consParseCumulative) ); 13663 SCIP_CALL( SCIPsetConshdlrPresol(scip, conshdlr, consPresolCumulative, CONSHDLR_MAXPREROUNDS, 13664 CONSHDLR_PRESOLTIMING) ); 13665 SCIP_CALL( SCIPsetConshdlrPrint(scip, conshdlr, consPrintCumulative) ); 13666 SCIP_CALL( SCIPsetConshdlrProp(scip, conshdlr, consPropCumulative, CONSHDLR_PROPFREQ, CONSHDLR_DELAYPROP, 13667 CONSHDLR_PROP_TIMING) ); 13668 SCIP_CALL( SCIPsetConshdlrResprop(scip, conshdlr, consRespropCumulative) ); 13669 SCIP_CALL( SCIPsetConshdlrSepa(scip, conshdlr, consSepalpCumulative, consSepasolCumulative, CONSHDLR_SEPAFREQ, 13670 CONSHDLR_SEPAPRIORITY, CONSHDLR_DELAYSEPA) ); 13671 SCIP_CALL( SCIPsetConshdlrTrans(scip, conshdlr, consTransCumulative) ); 13672 SCIP_CALL( SCIPsetConshdlrEnforelax(scip, conshdlr, consEnforelaxCumulative) ); 13673 13674 /* add cumulative constraint handler parameters */ 13675 SCIP_CALL( SCIPaddBoolParam(scip, 13676 "constraints/" CONSHDLR_NAME "/ttinfer", 13677 "should time-table (core-times) propagator be used to infer bounds?", 13678 &conshdlrdata->ttinfer, FALSE, DEFAULT_TTINFER, NULL, NULL) ); 13679 SCIP_CALL( SCIPaddBoolParam(scip, 13680 "constraints/" CONSHDLR_NAME "/efcheck", 13681 "should edge-finding be used to detect an overload?", 13682 &conshdlrdata->efcheck, FALSE, DEFAULT_EFCHECK, NULL, NULL) ); 13683 SCIP_CALL( SCIPaddBoolParam(scip, 13684 "constraints/" CONSHDLR_NAME "/efinfer", 13685 "should edge-finding be used to infer bounds?", 13686 &conshdlrdata->efinfer, FALSE, DEFAULT_EFINFER, NULL, NULL) ); 13687 SCIP_CALL( SCIPaddBoolParam(scip, 13688 "constraints/" CONSHDLR_NAME "/useadjustedjobs", "should edge-finding be executed?", 13689 &conshdlrdata->useadjustedjobs, TRUE, DEFAULT_USEADJUSTEDJOBS, NULL, NULL) ); 13690 SCIP_CALL( SCIPaddBoolParam(scip, 13691 "constraints/" CONSHDLR_NAME "/ttefcheck", 13692 "should time-table edge-finding be used to detect an overload?", 13693 &conshdlrdata->ttefcheck, FALSE, DEFAULT_TTEFCHECK, NULL, NULL) ); 13694 SCIP_CALL( SCIPaddBoolParam(scip, 13695 "constraints/" CONSHDLR_NAME "/ttefinfer", 13696 "should time-table edge-finding be used to infer bounds?", 13697 &conshdlrdata->ttefinfer, FALSE, DEFAULT_TTEFINFER, NULL, NULL) ); 13698 13699 SCIP_CALL( SCIPaddBoolParam(scip, 13700 "constraints/" CONSHDLR_NAME "/usebinvars", "should the binary representation be used?", 13701 &conshdlrdata->usebinvars, FALSE, DEFAULT_USEBINVARS, NULL, NULL) ); 13702 SCIP_CALL( SCIPaddBoolParam(scip, 13703 "constraints/" CONSHDLR_NAME "/localcuts", "should cuts be added only locally?", 13704 &conshdlrdata->localcuts, FALSE, DEFAULT_LOCALCUTS, NULL, NULL) ); 13705 SCIP_CALL( SCIPaddBoolParam(scip, 13706 "constraints/" CONSHDLR_NAME "/usecovercuts", "should covering cuts be added every node?", 13707 &conshdlrdata->usecovercuts, FALSE, DEFAULT_USECOVERCUTS, NULL, NULL) ); 13708 SCIP_CALL( SCIPaddBoolParam(scip, 13709 "constraints/" CONSHDLR_NAME "/cutsasconss", 13710 "should the cumulative constraint create cuts as knapsack constraints?", 13711 &conshdlrdata->cutsasconss, FALSE, DEFAULT_CUTSASCONSS, NULL, NULL) ); 13712 SCIP_CALL( SCIPaddBoolParam(scip, 13713 "constraints/" CONSHDLR_NAME "/sepaold", 13714 "shall old sepa algo be applied?", 13715 &conshdlrdata->sepaold, FALSE, DEFAULT_SEPAOLD, NULL, NULL) ); 13716 13717 SCIP_CALL( SCIPaddBoolParam(scip, 13718 "constraints/" CONSHDLR_NAME "/fillbranchcands", "should branching candidates be added to storage?", 13719 &conshdlrdata->fillbranchcands, FALSE, DEFAULT_FILLBRANCHCANDS, NULL, NULL) ); 13720 13721 /* presolving parameters */ 13722 SCIP_CALL( SCIPaddBoolParam(scip, 13723 "constraints/" CONSHDLR_NAME "/dualpresolve", "should dual presolving be applied?", 13724 &conshdlrdata->dualpresolve, FALSE, DEFAULT_DUALPRESOLVE, NULL, NULL) ); 13725 SCIP_CALL( SCIPaddBoolParam(scip, 13726 "constraints/" CONSHDLR_NAME "/coeftightening", "should coefficient tightening be applied?", 13727 &conshdlrdata->coeftightening, FALSE, DEFAULT_COEFTIGHTENING, NULL, NULL) ); 13728 SCIP_CALL( SCIPaddBoolParam(scip, 13729 "constraints/" CONSHDLR_NAME "/normalize", "should demands and capacity be normalized?", 13730 &conshdlrdata->normalize, FALSE, DEFAULT_NORMALIZE, NULL, NULL) ); 13731 SCIP_CALL( SCIPaddBoolParam(scip, 13732 "constraints/" CONSHDLR_NAME "/presolpairwise", 13733 "should pairwise constraint comparison be performed in presolving?", 13734 &conshdlrdata->presolpairwise, TRUE, DEFAULT_PRESOLPAIRWISE, NULL, NULL) ); 13735 SCIP_CALL( SCIPaddBoolParam(scip, 13736 "constraints/" CONSHDLR_NAME "/disjunctive", "extract disjunctive constraints?", 13737 &conshdlrdata->disjunctive, FALSE, DEFAULT_DISJUNCTIVE, NULL, NULL) ); 13738 13739 SCIP_CALL( SCIPaddLongintParam(scip, 13740 "constraints/" CONSHDLR_NAME "/maxnodes", 13741 "number of branch-and-bound nodes to solve an independent cumulative constraint (-1: no limit)?", 13742 &conshdlrdata->maxnodes, FALSE, DEFAULT_MAXNODES, -1LL, SCIP_LONGINT_MAX, NULL, NULL) ); 13743 SCIP_CALL( SCIPaddBoolParam(scip, 13744 "constraints/" CONSHDLR_NAME "/detectdisjunctive", "search for conflict set via maximal cliques to detect disjunctive constraints", 13745 &conshdlrdata->detectdisjunctive, FALSE, DEFAULT_DETECTDISJUNCTIVE, NULL, NULL) ); 13746 SCIP_CALL( SCIPaddBoolParam(scip, 13747 "constraints/" CONSHDLR_NAME "/detectvarbounds", "search for conflict set via maximal cliques to detect variable bound constraints", 13748 &conshdlrdata->detectvarbounds, FALSE, DEFAULT_DETECTVARBOUNDS, NULL, NULL) ); 13749 13750 /* conflict analysis parameters */ 13751 SCIP_CALL( SCIPaddBoolParam(scip, 13752 "constraints/" CONSHDLR_NAME "/usebdwidening", "should bound widening be used during the conflict analysis?", 13753 &conshdlrdata->usebdwidening, FALSE, DEFAULT_USEBDWIDENING, NULL, NULL) ); 13754 13755 return SCIP_OKAY; 13756 } 13757 13758 /** creates and captures a cumulative constraint */ 13759 SCIP_RETCODE SCIPcreateConsCumulative( 13760 SCIP* scip, /**< SCIP data structure */ 13761 SCIP_CONS** cons, /**< pointer to hold the created constraint */ 13762 const char* name, /**< name of constraint */ 13763 int nvars, /**< number of variables (jobs) */ 13764 SCIP_VAR** vars, /**< array of integer variable which corresponds to starting times for a job */ 13765 int* durations, /**< array containing corresponding durations */ 13766 int* demands, /**< array containing corresponding demands */ 13767 int capacity, /**< available cumulative capacity */ 13768 SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP? 13769 * Usually set to TRUE. Set to FALSE for 'lazy constraints'. */ 13770 SCIP_Bool separate, /**< should the constraint be separated during LP processing? 13771 * Usually set to TRUE. */ 13772 SCIP_Bool enforce, /**< should the constraint be enforced during node processing? 13773 * TRUE for model constraints, FALSE for additional, redundant constraints. */ 13774 SCIP_Bool check, /**< should the constraint be checked for feasibility? 13775 * TRUE for model constraints, FALSE for additional, redundant constraints. */ 13776 SCIP_Bool propagate, /**< should the constraint be propagated during node processing? 13777 * Usually set to TRUE. */ 13778 SCIP_Bool local, /**< is constraint only valid locally? 13779 * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */ 13780 SCIP_Bool modifiable, /**< is constraint modifiable (subject to column generation)? 13781 * Usually set to FALSE. In column generation applications, set to TRUE if pricing 13782 * adds coefficients to this constraint. */ 13783 SCIP_Bool dynamic, /**< is constraint subject to aging? 13784 * Usually set to FALSE. Set to TRUE for own cuts which 13785 * are seperated as constraints. */ 13786 SCIP_Bool removable, /**< should the relaxation be removed from the LP due to aging or cleanup? 13787 * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */ 13788 SCIP_Bool stickingatnode /**< should the constraint always be kept at the node where it was added, even 13789 * if it may be moved to a more global node? 13790 * Usually set to FALSE. Set to TRUE to for constraints that represent node data. */ 13791 ) 13792 { 13793 SCIP_CONSHDLR* conshdlr; 13794 SCIP_CONSDATA* consdata; 13795 13796 assert(scip != NULL); 13797 13798 /* find the cumulative constraint handler */ 13799 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME); 13800 if( conshdlr == NULL ) 13801 { 13802 SCIPerrorMessage("" CONSHDLR_NAME " constraint handler not found\n"); 13803 return SCIP_PLUGINNOTFOUND; 13804 } 13805 13806 SCIPdebugMsg(scip, "create cumulative constraint <%s> with %d jobs\n", name, nvars); 13807 13808 /* create constraint data */ 13809 SCIP_CALL( consdataCreate(scip, &consdata, vars, NULL, durations, demands, nvars, capacity, 0, INT_MAX, check) ); 13810 13811 /* create constraint */ 13812 SCIP_CALL( SCIPcreateCons(scip, cons, name, conshdlr, consdata, 13813 initial, separate, enforce, check, propagate, 13814 local, modifiable, dynamic, removable, stickingatnode) ); 13815 13816 if( SCIPgetStage(scip) != SCIP_STAGE_PROBLEM ) 13817 { 13818 SCIP_CONSHDLRDATA* conshdlrdata; 13819 13820 /* get event handler */ 13821 conshdlrdata = SCIPconshdlrGetData(conshdlr); 13822 assert(conshdlrdata != NULL); 13823 assert(conshdlrdata->eventhdlr != NULL); 13824 13825 /* catch bound change events of variables */ 13826 SCIP_CALL( consdataCatchEvents(scip, consdata, conshdlrdata->eventhdlr) ); 13827 } 13828 13829 return SCIP_OKAY; 13830 } 13831 13832 /** creates and captures a cumulative constraint 13833 * in its most basic version, i. e., all constraint flags are set to their basic value as explained for the 13834 * method SCIPcreateConsCumulative(); all flags can be set via SCIPsetConsFLAGNAME-methods in scip.h 13835 * 13836 * @see SCIPcreateConsCumulative() for information about the basic constraint flag configuration 13837 * 13838 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons() 13839 */ 13840 SCIP_RETCODE SCIPcreateConsBasicCumulative( 13841 SCIP* scip, /**< SCIP data structure */ 13842 SCIP_CONS** cons, /**< pointer to hold the created constraint */ 13843 const char* name, /**< name of constraint */ 13844 int nvars, /**< number of variables (jobs) */ 13845 SCIP_VAR** vars, /**< array of integer variable which corresponds to starting times for a job */ 13846 int* durations, /**< array containing corresponding durations */ 13847 int* demands, /**< array containing corresponding demands */ 13848 int capacity /**< available cumulative capacity */ 13849 ) 13850 { 13851 assert(scip != NULL); 13852 13853 SCIP_CALL( SCIPcreateConsCumulative(scip, cons, name, nvars, vars, durations, demands, capacity, 13854 TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) ); 13855 13856 return SCIP_OKAY; 13857 } 13858 13859 /** set the left bound of the time axis to be considered (including hmin) */ /*lint -e{715}*/ 13860 SCIP_RETCODE SCIPsetHminCumulative( 13861 SCIP* scip, /**< SCIP data structure */ 13862 SCIP_CONS* cons, /**< constraint data */ 13863 int hmin /**< left bound of time axis to be considered */ 13864 ) 13865 { 13866 SCIP_CONSDATA* consdata; 13867 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 ) 13868 { 13869 SCIPerrorMessage("constraint is not a cumulative constraint\n"); 13870 return SCIP_INVALIDCALL; 13871 } 13872 13873 consdata = SCIPconsGetData(cons); 13874 assert(consdata != NULL); 13875 assert(hmin >= 0); 13876 assert(hmin <= consdata->hmax); 13877 13878 consdata->hmin = hmin; 13879 13880 return SCIP_OKAY; 13881 } 13882 13883 /** returns the left bound of the time axis to be considered */ /*lint -e{715}*/ 13884 int SCIPgetHminCumulative( 13885 SCIP* scip, /**< SCIP data structure */ 13886 SCIP_CONS* cons /**< constraint */ 13887 ) 13888 { 13889 SCIP_CONSDATA* consdata; 13890 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 ) 13891 { 13892 SCIPerrorMessage("constraint is not a cumulative constraint\n"); 13893 SCIPABORT(); 13894 return 0; /*lint !e527*/ 13895 } 13896 13897 consdata = SCIPconsGetData(cons); 13898 assert(consdata != NULL); 13899 13900 return consdata->hmin; 13901 } 13902 13903 /** set the right bound of the time axis to be considered (not including hmax) */ /*lint -e{715}*/ 13904 SCIP_RETCODE SCIPsetHmaxCumulative( 13905 SCIP* scip, /**< SCIP data structure */ 13906 SCIP_CONS* cons, /**< constraint data */ 13907 int hmax /**< right bound of time axis to be considered */ 13908 ) 13909 { 13910 SCIP_CONSDATA* consdata; 13911 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 ) 13912 { 13913 SCIPerrorMessage("constraint is not a cumulative constraint\n"); 13914 SCIPABORT(); 13915 return SCIP_INVALIDCALL; /*lint !e527*/ 13916 } 13917 13918 consdata = SCIPconsGetData(cons); 13919 assert(consdata != NULL); 13920 assert(hmax >= consdata->hmin); 13921 13922 consdata->hmax = hmax; 13923 13924 return SCIP_OKAY; 13925 } 13926 13927 /** returns the right bound of the time axis to be considered */ /*lint -e{715}*/ 13928 int SCIPgetHmaxCumulative( 13929 SCIP* scip, /**< SCIP data structure */ 13930 SCIP_CONS* cons /**< constraint */ 13931 ) 13932 { 13933 SCIP_CONSDATA* consdata; 13934 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 ) 13935 { 13936 SCIPerrorMessage("constraint is not a cumulative constraint\n"); 13937 SCIPABORT(); 13938 return 0; /*lint !e527*/ 13939 } 13940 13941 consdata = SCIPconsGetData(cons); 13942 assert(consdata != NULL); 13943 13944 return consdata->hmax; 13945 } 13946 13947 /** returns the activities of the cumulative constraint */ /*lint -e{715}*/ 13948 SCIP_VAR** SCIPgetVarsCumulative( 13949 SCIP* scip, /**< SCIP data structure */ 13950 SCIP_CONS* cons /**< constraint data */ 13951 ) 13952 { 13953 SCIP_CONSDATA* consdata; 13954 13955 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 ) 13956 { 13957 SCIPerrorMessage("constraint is not a cumulative constraint\n"); 13958 SCIPABORT(); 13959 return NULL; /*lint !e527*/ 13960 } 13961 13962 consdata = SCIPconsGetData(cons); 13963 assert(consdata != NULL); 13964 13965 return consdata->vars; 13966 } 13967 13968 /** returns the activities of the cumulative constraint */ /*lint -e{715}*/ 13969 int SCIPgetNVarsCumulative( 13970 SCIP* scip, /**< SCIP data structure */ 13971 SCIP_CONS* cons /**< constraint data */ 13972 ) 13973 { 13974 SCIP_CONSDATA* consdata; 13975 13976 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 ) 13977 { 13978 SCIPerrorMessage("constraint is not a cumulative constraint\n"); 13979 SCIPABORT(); 13980 return -1; /*lint !e527*/ 13981 } 13982 13983 consdata = SCIPconsGetData(cons); 13984 assert(consdata != NULL); 13985 13986 return consdata->nvars; 13987 } 13988 13989 /** returns the capacity of the cumulative constraint */ /*lint -e{715}*/ 13990 int SCIPgetCapacityCumulative( 13991 SCIP* scip, /**< SCIP data structure */ 13992 SCIP_CONS* cons /**< constraint data */ 13993 ) 13994 { 13995 SCIP_CONSDATA* consdata; 13996 13997 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 ) 13998 { 13999 SCIPerrorMessage("constraint is not a cumulative constraint\n"); 14000 SCIPABORT(); 14001 return -1; /*lint !e527*/ 14002 } 14003 14004 consdata = SCIPconsGetData(cons); 14005 assert(consdata != NULL); 14006 14007 return consdata->capacity; 14008 } 14009 14010 /** returns the durations of the cumulative constraint */ /*lint -e{715}*/ 14011 int* SCIPgetDurationsCumulative( 14012 SCIP* scip, /**< SCIP data structure */ 14013 SCIP_CONS* cons /**< constraint data */ 14014 ) 14015 { 14016 SCIP_CONSDATA* consdata; 14017 14018 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 ) 14019 { 14020 SCIPerrorMessage("constraint is not a cumulative constraint\n"); 14021 SCIPABORT(); 14022 return NULL; /*lint !e527*/ 14023 } 14024 14025 consdata = SCIPconsGetData(cons); 14026 assert(consdata != NULL); 14027 14028 return consdata->durations; 14029 } 14030 14031 /** returns the demands of the cumulative constraint */ /*lint -e{715}*/ 14032 int* SCIPgetDemandsCumulative( 14033 SCIP* scip, /**< SCIP data structure */ 14034 SCIP_CONS* cons /**< constraint data */ 14035 ) 14036 { 14037 SCIP_CONSDATA* consdata; 14038 14039 if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 ) 14040 { 14041 SCIPerrorMessage("constraint is not a cumulative constraint\n"); 14042 SCIPABORT(); 14043 return NULL; /*lint !e527*/ 14044 } 14045 14046 consdata = SCIPconsGetData(cons); 14047 assert(consdata != NULL); 14048 14049 return consdata->demands; 14050 } 14051 14052 /** check for the given starting time variables with their demands and durations if the cumulative conditions for the 14053 * given solution is satisfied 14054 */ 14055 SCIP_RETCODE SCIPcheckCumulativeCondition( 14056 SCIP* scip, /**< SCIP data structure */ 14057 SCIP_SOL* sol, /**< primal solution, or NULL for current LP/pseudo solution */ 14058 int nvars, /**< number of variables (jobs) */ 14059 SCIP_VAR** vars, /**< array of integer variable which corresponds to starting times for a job */ 14060 int* durations, /**< array containing corresponding durations */ 14061 int* demands, /**< array containing corresponding demands */ 14062 int capacity, /**< available cumulative capacity */ 14063 int hmin, /**< left bound of time axis to be considered (including hmin) */ 14064 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 14065 SCIP_Bool* violated, /**< pointer to store if the cumulative condition is violated */ 14066 SCIP_CONS* cons, /**< constraint which is checked */ 14067 SCIP_Bool printreason /**< should the reason for the violation be printed? */ 14068 ) 14069 { 14070 assert(scip != NULL); 14071 assert(violated != NULL); 14072 14073 SCIP_CALL( checkCumulativeCondition(scip, sol, nvars, vars, durations, demands, capacity, hmin, hmax, 14074 violated, cons, printreason) ); 14075 14076 return SCIP_OKAY; 14077 } 14078 14079 /** normalize cumulative condition */ /*lint -e{715}*/ 14080 SCIP_RETCODE SCIPnormalizeCumulativeCondition( 14081 SCIP* scip, /**< SCIP data structure */ 14082 int nvars, /**< number of start time variables (activities) */ 14083 SCIP_VAR** vars, /**< array of start time variables */ 14084 int* durations, /**< array of durations */ 14085 int* demands, /**< array of demands */ 14086 int* capacity, /**< pointer to store the changed cumulative capacity */ 14087 int* nchgcoefs, /**< pointer to count total number of changed coefficients */ 14088 int* nchgsides /**< pointer to count number of side changes */ 14089 ) 14090 { /*lint --e{715}*/ 14091 normalizeCumulativeCondition(scip, nvars, demands, capacity, nchgcoefs, nchgsides); 14092 14093 return SCIP_OKAY; 14094 } 14095 14096 /** searches for a time point within the cumulative condition were the cumulative condition can be split */ 14097 SCIP_RETCODE SCIPsplitCumulativeCondition( 14098 SCIP* scip, /**< SCIP data structure */ 14099 int nvars, /**< number of variables (jobs) */ 14100 SCIP_VAR** vars, /**< array of integer variable which corresponds to starting times for a job */ 14101 int* durations, /**< array containing corresponding durations */ 14102 int* demands, /**< array containing corresponding demands */ 14103 int capacity, /**< available cumulative capacity */ 14104 int* hmin, /**< pointer to store the left bound of the effective horizon */ 14105 int* hmax, /**< pointer to store the right bound of the effective horizon */ 14106 int* split /**< point were the cumulative condition can be split */ 14107 ) 14108 { 14109 SCIP_CALL( computeEffectiveHorizonCumulativeCondition(scip, nvars, vars, durations, demands, capacity, 14110 hmin, hmax, split) ); 14111 14112 return SCIP_OKAY; 14113 } 14114 14115 /** presolve cumulative condition w.r.t. effective horizon by detecting irrelevant variables */ 14116 SCIP_RETCODE SCIPpresolveCumulativeCondition( 14117 SCIP* scip, /**< SCIP data structure */ 14118 int nvars, /**< number of start time variables (activities) */ 14119 SCIP_VAR** vars, /**< array of start time variables */ 14120 int* durations, /**< array of durations */ 14121 int hmin, /**< left bound of time axis to be considered */ 14122 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 14123 SCIP_Bool* downlocks, /**< array storing if the variable has a down lock, or NULL */ 14124 SCIP_Bool* uplocks, /**< array storing if the variable has an up lock, or NULL */ 14125 SCIP_CONS* cons, /**< constraint which gets propagated, or NULL */ 14126 SCIP_Bool* irrelevants, /**< array mark those variables which are irrelevant for the cumulative condition */ 14127 int* nfixedvars, /**< pointer to store the number of fixed variables */ 14128 int* nchgsides, /**< pointer to store the number of changed sides */ 14129 SCIP_Bool* cutoff /**< buffer to store whether a cutoff is detected */ 14130 ) 14131 { 14132 if( nvars <= 1 ) 14133 return SCIP_OKAY; 14134 14135 /* presolve constraint form the earlier start time point of view */ 14136 SCIP_CALL( presolveConsEst(scip, nvars, vars, durations, hmin, hmax, downlocks, uplocks, cons, 14137 irrelevants, nfixedvars, nchgsides, cutoff) ); 14138 14139 /* presolve constraint form the latest completion time point of view */ 14140 SCIP_CALL( presolveConsLct(scip, nvars, vars, durations, hmin, hmax, downlocks, uplocks, cons, 14141 irrelevants, nfixedvars, nchgsides, cutoff) ); 14142 14143 return SCIP_OKAY; 14144 } 14145 14146 /** propagate the given cumulative condition */ 14147 SCIP_RETCODE SCIPpropCumulativeCondition( 14148 SCIP* scip, /**< SCIP data structure */ 14149 SCIP_PRESOLTIMING presoltiming, /**< current presolving timing */ 14150 int nvars, /**< number of variables (jobs) */ 14151 SCIP_VAR** vars, /**< array of integer variable which corresponds to starting times for a job */ 14152 int* durations, /**< array containing corresponding durations */ 14153 int* demands, /**< array containing corresponding demands */ 14154 int capacity, /**< available cumulative capacity */ 14155 int hmin, /**< left bound of time axis to be considered (including hmin) */ 14156 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 14157 SCIP_CONS* cons, /**< constraint which gets propagated */ 14158 int* nchgbds, /**< pointer to store the number of variable bound changes */ 14159 SCIP_Bool* initialized, /**< was conflict analysis initialized */ 14160 SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */ 14161 SCIP_Bool* cutoff /**< pointer to store if the cumulative condition is violated */ 14162 ) 14163 { 14164 SCIP_CONSHDLR* conshdlr; 14165 SCIP_CONSHDLRDATA* conshdlrdata; 14166 SCIP_Bool redundant; 14167 14168 assert(scip != NULL); 14169 assert(cons != NULL); 14170 assert(initialized != NULL); 14171 assert(*initialized == FALSE); 14172 assert(cutoff != NULL); 14173 assert(*cutoff == FALSE); 14174 14175 /* find the cumulative constraint handler */ 14176 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME); 14177 if( conshdlr == NULL ) 14178 { 14179 SCIPerrorMessage("" CONSHDLR_NAME " constraint handler not found\n"); 14180 return SCIP_PLUGINNOTFOUND; 14181 } 14182 14183 conshdlrdata = SCIPconshdlrGetData(conshdlr); 14184 assert(conshdlrdata != NULL); 14185 14186 redundant = FALSE; 14187 14188 SCIP_CALL( propagateCumulativeCondition(scip, conshdlrdata, presoltiming, 14189 nvars, vars, durations, demands, capacity, hmin, hmax, cons, 14190 nchgbds, &redundant, initialized, explanation, cutoff) ); 14191 14192 return SCIP_OKAY; 14193 } 14194 14195 /** resolve propagation w.r.t. the cumulative condition */ 14196 SCIP_RETCODE SCIPrespropCumulativeCondition( 14197 SCIP* scip, /**< SCIP data structure */ 14198 int nvars, /**< number of start time variables (activities) */ 14199 SCIP_VAR** vars, /**< array of start time variables */ 14200 int* durations, /**< array of durations */ 14201 int* demands, /**< array of demands */ 14202 int capacity, /**< cumulative capacity */ 14203 int hmin, /**< left bound of time axis to be considered (including hmin) */ 14204 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 14205 SCIP_VAR* infervar, /**< the conflict variable whose bound change has to be resolved */ 14206 int inferinfo, /**< the user information */ 14207 SCIP_BOUNDTYPE boundtype, /**< the type of the changed bound (lower or upper bound) */ 14208 SCIP_BDCHGIDX* bdchgidx, /**< the index of the bound change, representing the point of time where the change took place */ 14209 SCIP_Real relaxedbd, /**< the relaxed bound which is sufficient to be explained */ 14210 SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */ 14211 SCIP_RESULT* result /**< pointer to store the result of the propagation conflict resolving call */ 14212 ) 14213 { 14214 SCIP_CALL( respropCumulativeCondition(scip, nvars, vars, durations, demands, capacity, hmin, hmax, 14215 infervar, intToInferInfo(inferinfo), boundtype, bdchgidx, relaxedbd, TRUE, explanation, result) ); 14216 14217 return SCIP_OKAY; 14218 } 14219 14220 /** this method visualizes the cumulative structure in GML format */ 14221 SCIP_RETCODE SCIPvisualizeConsCumulative( 14222 SCIP* scip, /**< SCIP data structure */ 14223 SCIP_CONS* cons /**< cumulative constraint */ 14224 ) 14225 { 14226 SCIP_CONSDATA* consdata; 14227 SCIP_HASHTABLE* vars; 14228 FILE* file; 14229 SCIP_VAR* var; 14230 char filename[SCIP_MAXSTRLEN]; 14231 int nvars; 14232 int v; 14233 14234 SCIP_RETCODE retcode = SCIP_OKAY; 14235 14236 /* open file */ 14237 (void)SCIPsnprintf(filename, SCIP_MAXSTRLEN, "%s.gml", SCIPconsGetName(cons)); 14238 file = fopen(filename, "w"); 14239 14240 /* check if the file was open */ 14241 if( file == NULL ) 14242 { 14243 SCIPerrorMessage("cannot create file <%s> for writing\n", filename); 14244 SCIPprintSysError(filename); 14245 return SCIP_FILECREATEERROR; 14246 } 14247 14248 consdata = SCIPconsGetData(cons); 14249 assert(consdata != NULL); 14250 14251 nvars = consdata->nvars; 14252 14253 SCIP_CALL_TERMINATE( retcode, SCIPhashtableCreate(&vars, SCIPblkmem(scip), nvars, 14254 SCIPvarGetHashkey, SCIPvarIsHashkeyEq, SCIPvarGetHashkeyVal, NULL), TERMINATE ); 14255 14256 /* create opening of the GML format */ 14257 SCIPgmlWriteOpening(file, TRUE); 14258 14259 for( v = 0; v < nvars; ++v ) 14260 { 14261 char color[SCIP_MAXSTRLEN]; 14262 14263 var = consdata->vars[v]; 14264 assert(var != NULL); 14265 14266 SCIP_CALL_TERMINATE( retcode, SCIPhashtableInsert(vars, (void*)var) , TERMINATE ); 14267 14268 if( SCIPvarGetUbGlobal(var) - SCIPvarGetLbGlobal(var) < 0.5 ) 14269 (void)SCIPsnprintf(color, SCIP_MAXSTRLEN, "%s", "#0000ff"); 14270 else if( !consdata->downlocks[v] || !consdata->uplocks[v] ) 14271 (void)SCIPsnprintf(color, SCIP_MAXSTRLEN, "%s", "#00ff00"); 14272 else 14273 (void)SCIPsnprintf(color, SCIP_MAXSTRLEN, "%s", "#ff0000"); 14274 14275 SCIPgmlWriteNode(file, (unsigned int)(size_t)var, SCIPvarGetName(var), "rectangle", color, NULL); 14276 } 14277 14278 for( v = 0; v < nvars; ++v ) 14279 { 14280 SCIP_VAR** vbdvars; 14281 int nvbdvars; 14282 int b; 14283 14284 var = consdata->vars[v]; 14285 assert(var != NULL); 14286 14287 vbdvars = SCIPvarGetVlbVars(var); 14288 nvbdvars = SCIPvarGetNVlbs(var); 14289 14290 for( b = 0; b < nvbdvars; ++b ) 14291 { 14292 if( SCIPhashtableExists(vars, (void*)vbdvars[b]) ) 14293 { 14294 SCIPgmlWriteArc(file, (unsigned int)(size_t)vbdvars[b], (unsigned int)(size_t)var, NULL, NULL); 14295 } 14296 } 14297 14298 #if 0 14299 vbdvars = SCIPvarGetVubVars(var); 14300 nvbdvars = SCIPvarGetNVubs(var); 14301 14302 for( b = 0; b < nvbdvars; ++b ) 14303 { 14304 if( SCIPhashtableExists(vars, vbdvars[b]) ) 14305 { 14306 SCIPgmlWriteArc(file, (unsigned int)(size_t)var, (unsigned int)(size_t)vbdvars[b], NULL, NULL); 14307 } 14308 } 14309 #endif 14310 } 14311 14312 /* create closing of the GML format */ 14313 SCIPgmlWriteClosing(file); 14314 TERMINATE: 14315 /* close file */ 14316 fclose(file); 14317 14318 SCIPhashtableFree(&vars); 14319 14320 return retcode; 14321 } 14322 14323 /** sets method to solve an individual cumulative condition */ 14324 SCIP_RETCODE SCIPsetSolveCumulative( 14325 SCIP* scip, /**< SCIP data structure */ 14326 SCIP_DECL_SOLVECUMULATIVE((*solveCumulative)) /**< method to use an individual cumulative condition */ 14327 ) 14328 { 14329 SCIP_CONSHDLR* conshdlr; 14330 SCIP_CONSHDLRDATA* conshdlrdata; 14331 14332 /* find the cumulative constraint handler */ 14333 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME); 14334 if( conshdlr == NULL ) 14335 { 14336 SCIPerrorMessage("" CONSHDLR_NAME " constraint handler not found\n"); 14337 return SCIP_PLUGINNOTFOUND; 14338 } 14339 14340 conshdlrdata = SCIPconshdlrGetData(conshdlr); 14341 assert(conshdlrdata != NULL); 14342 14343 conshdlrdata->solveCumulative = solveCumulative; 14344 14345 return SCIP_OKAY; 14346 } 14347 14348 /** solves given cumulative condition as independent sub problem 14349 * 14350 * @note If the problem was solved to the earliest start times (ests) and latest start times (lsts) array contain the 14351 * solution values; If the problem was not solved these two arrays contain the global bounds at the time the sub 14352 * solver was interrupted. 14353 */ 14354 SCIP_RETCODE SCIPsolveCumulative( 14355 SCIP* scip, /**< SCIP data structure */ 14356 int njobs, /**< number of jobs (activities) */ 14357 SCIP_Real* ests, /**< array with the earlier start time for each job */ 14358 SCIP_Real* lsts, /**< array with the latest start time for each job */ 14359 SCIP_Real* objvals, /**< array of objective coefficients for each job (linear objective function), or NULL if none */ 14360 int* durations, /**< array of durations */ 14361 int* demands, /**< array of demands */ 14362 int capacity, /**< cumulative capacity */ 14363 int hmin, /**< left bound of time axis to be considered (including hmin) */ 14364 int hmax, /**< right bound of time axis to be considered (not including hmax) */ 14365 SCIP_Real timelimit, /**< time limit for solving in seconds */ 14366 SCIP_Real memorylimit, /**< memory limit for solving in mega bytes (MB) */ 14367 SCIP_Longint maxnodes, /**< maximum number of branch-and-bound nodes to solve the single cumulative constraint (-1: no limit) */ 14368 SCIP_Bool* solved, /**< pointer to store if the problem is solved (to optimality) */ 14369 SCIP_Bool* infeasible, /**< pointer to store if the problem is infeasible */ 14370 SCIP_Bool* unbounded, /**< pointer to store if the problem is unbounded */ 14371 SCIP_Bool* error /**< pointer to store if an error occurred */ 14372 ) 14373 { 14374 SCIP_CONSHDLR* conshdlr; 14375 SCIP_CONSHDLRDATA* conshdlrdata; 14376 14377 (*solved) = TRUE; 14378 (*infeasible) = FALSE; 14379 (*unbounded) = FALSE; 14380 (*error) = FALSE; 14381 14382 if( njobs == 0 ) 14383 return SCIP_OKAY; 14384 14385 /* find the cumulative constraint handler */ 14386 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME); 14387 if( conshdlr == NULL ) 14388 { 14389 SCIPerrorMessage("" CONSHDLR_NAME " constraint handler not found\n"); 14390 (*error) = TRUE; 14391 return SCIP_PLUGINNOTFOUND; 14392 } 14393 14394 conshdlrdata = SCIPconshdlrGetData(conshdlr); 14395 assert(conshdlrdata != NULL); 14396 14397 /* abort if no time is left or not enough memory to create a copy of SCIP, including external memory usage */ 14398 if( timelimit > 0.0 && memorylimit > 10 ) 14399 { 14400 SCIP_CALL( conshdlrdata->solveCumulative(njobs, ests, lsts, objvals, durations, demands, capacity, 14401 hmin, hmax, timelimit, memorylimit, maxnodes, solved, infeasible, unbounded, error) ); 14402 } 14403 14404 return SCIP_OKAY; 14405 } 14406 14407 /** creates the worst case resource profile, that is, all jobs are inserted with the earliest start and latest 14408 * completion time 14409 */ 14410 SCIP_RETCODE SCIPcreateWorstCaseProfile( 14411 SCIP* scip, /**< SCIP data structure */ 14412 SCIP_PROFILE* profile, /**< resource profile */ 14413 int nvars, /**< number of variables (jobs) */ 14414 SCIP_VAR** vars, /**< array of integer variable which corresponds to starting times for a job */ 14415 int* durations, /**< array containing corresponding durations */ 14416 int* demands /**< array containing corresponding demands */ 14417 ) 14418 { 14419 SCIP_VAR* var; 14420 SCIP_HASHMAP* addedvars; 14421 int* copydemands; 14422 int* perm; 14423 int duration; 14424 int impliedest; 14425 int est; 14426 int impliedlct; 14427 int lct; 14428 int v; 14429 14430 /* create hash map for variables which are added, mapping to their duration */ 14431 SCIP_CALL( SCIPhashmapCreate(&addedvars, SCIPblkmem(scip), nvars) ); 14432 14433 SCIP_CALL( SCIPallocBufferArray(scip, &perm, nvars) ); 14434 SCIP_CALL( SCIPallocBufferArray(scip, ©demands, nvars) ); 14435 14436 /* sort variables w.r.t. job demands */ 14437 for( v = 0; v < nvars; ++v ) 14438 { 14439 copydemands[v] = demands[v]; 14440 perm[v] = v; 14441 } 14442 SCIPsortDownIntInt(copydemands, perm, nvars); 14443 14444 /* add each job with its earliest start and latest completion time into the resource profile */ 14445 for( v = 0; v < nvars; ++v ) 14446 { 14447 int idx; 14448 14449 idx = perm[v]; 14450 assert(idx >= 0 && idx < nvars); 14451 14452 var = vars[idx]; 14453 assert(var != NULL); 14454 14455 duration = durations[idx]; 14456 assert(duration > 0); 14457 14458 est = SCIPconvertRealToInt(scip, SCIPvarGetLbLocal(var)); 14459 SCIP_CALL( computeImpliedEst(scip, var, addedvars, &impliedest) ); 14460 14461 lct = SCIPconvertRealToInt(scip, SCIPvarGetUbLocal(var)) + duration; 14462 SCIP_CALL( computeImpliedLct(scip, var, duration, addedvars, &impliedlct) ); 14463 14464 if( impliedest < impliedlct ) 14465 { 14466 SCIP_Bool infeasible; 14467 int pos; 14468 14469 SCIP_CALL( SCIPprofileInsertCore(profile, impliedest, impliedlct, copydemands[v], &pos, &infeasible) ); 14470 assert(!infeasible); 14471 assert(pos == -1); 14472 } 14473 14474 if( est == impliedest && lct == impliedlct ) 14475 { 14476 SCIP_CALL( SCIPhashmapInsertInt(addedvars, (void*)var, duration) ); 14477 } 14478 } 14479 14480 SCIPfreeBufferArray(scip, ©demands); 14481 SCIPfreeBufferArray(scip, &perm); 14482 14483 SCIPhashmapFree(&addedvars); 14484 14485 return SCIP_OKAY; 14486 } 14487 14488 /** computes w.r.t. the given worst case resource profile the first time point where the given capacity can be violated */ /*lint -e{715}*/ 14489 int SCIPcomputeHmin( 14490 SCIP* scip, /**< SCIP data structure */ 14491 SCIP_PROFILE* profile, /**< worst case resource profile */ 14492 int capacity /**< capacity to check */ 14493 ) 14494 { 14495 int* timepoints; 14496 int* loads; 14497 int ntimepoints; 14498 int t; 14499 14500 ntimepoints = SCIPprofileGetNTimepoints(profile); 14501 timepoints = SCIPprofileGetTimepoints(profile); 14502 loads = SCIPprofileGetLoads(profile); 14503 14504 /* find first time point which potentially violates the capacity restriction */ 14505 for( t = 0; t < ntimepoints - 1; ++t ) 14506 { 14507 /* check if the time point exceed w.r.t. worst case profile the capacity */ 14508 if( loads[t] > capacity ) 14509 { 14510 assert(t == 0 || loads[t-1] <= capacity); 14511 return timepoints[t]; 14512 } 14513 } 14514 14515 return INT_MAX; 14516 } 14517 14518 /** computes w.r.t. the given worst case resource profile the first time point where the given capacity is satisfied for sure */ /*lint -e{715}*/ 14519 int SCIPcomputeHmax( 14520 SCIP* scip, /**< SCIP data structure */ 14521 SCIP_PROFILE* profile, /**< worst case profile */ 14522 int capacity /**< capacity to check */ 14523 ) 14524 { 14525 int* timepoints; 14526 int* loads; 14527 int ntimepoints; 14528 int t; 14529 14530 ntimepoints = SCIPprofileGetNTimepoints(profile); 14531 timepoints = SCIPprofileGetTimepoints(profile); 14532 loads = SCIPprofileGetLoads(profile); 14533 14534 /* find last time point which potentially violates the capacity restriction */ 14535 for( t = ntimepoints - 1; t >= 0; --t ) 14536 { 14537 /* check if at time point t the worst case resource profile exceeds the capacity */ 14538 if( loads[t] > capacity ) 14539 { 14540 assert(t == ntimepoints-1 || loads[t+1] <= capacity); 14541 return timepoints[t+1]; 14542 } 14543 } 14544 14545 return INT_MIN; 14546 } 14547