Scippy

SCIP

Solving Constraint Integer Programs

cons_cumulative.c
Go to the documentation of this file.
1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program and library */
4 /* SCIP --- Solving Constraint Integer Programs */
5 /* */
6 /* Copyright (C) 2002-2014 Konrad-Zuse-Zentrum */
7 /* fuer Informationstechnik Berlin */
8 /* */
9 /* SCIP is distributed under the terms of the ZIB Academic License. */
10 /* */
11 /* You should have received a copy of the ZIB Academic License */
12 /* along with SCIP; see the file COPYING. If not email to scip@zib.de. */
13 /* */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15 
16 /**@file cons_cumulative.c
17  * @brief constraint handler for cumulative constraints
18  * @author Timo Berthold
19  * @author Stefan Heinz
20  * @author Jens Schulz
21  *
22  * Given:
23  * - 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
24  * their demands \f$d_j\f$.
25  * - an integer resource capacity \f$C\f$
26  *
27  * 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.
28  *
29  * Separation:
30  * - can be done using binary start time model, see Pritskers, Watters and Wolfe
31  * - or by just separating relatively weak cuts on the integer start time variables
32  *
33  * Propagation:
34  * - time tabling, Klein & Scholl (1999)
35  * - Edge-finding from Petr Vilim, adjusted and simplified for dynamic repropagation
36  * (2009)
37  * - energetic reasoning, see Baptiste, Le Pape, Nuijten (2001)
38  *
39  */
40 
41 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
42 
43 #include <assert.h>
44 #include <string.h>
45 
46 #include "tclique/tclique.h"
47 #include "scip/cons_cumulative.h"
48 #include "scip/cons_linking.h"
49 #include "scip/cons_knapsack.h"
50 #include "scip/scipdefplugins.h"
51 
52 /**@name Constraint handler properties
53  *
54  * @{
55  */
56 
57 /* constraint handler properties */
58 #define CONSHDLR_NAME "cumulative"
59 #define CONSHDLR_DESC "cumulative constraint handler"
60 #define CONSHDLR_SEPAPRIORITY 2100000 /**< priority of the constraint handler for separation */
61 #define CONSHDLR_ENFOPRIORITY -2040000 /**< priority of the constraint handler for constraint enforcing */
62 #define CONSHDLR_CHECKPRIORITY -3030000 /**< priority of the constraint handler for checking feasibility */
63 #define CONSHDLR_SEPAFREQ 1 /**< frequency for separating cuts; zero means to separate only in the root node */
64 #define CONSHDLR_PROPFREQ 1 /**< frequency for propagating domains; zero means only preprocessing propagation */
65 #define CONSHDLR_EAGERFREQ 100 /**< frequency for using all instead of only the useful constraints in separation,
66  * propagation and enforcement, -1 for no eager evaluations, 0 for first only */
67 #define CONSHDLR_MAXPREROUNDS -1 /**< maximal number of presolving rounds the constraint handler participates in (-1: no limit) */
68 #define CONSHDLR_DELAYSEPA FALSE /**< should separation method be delayed, if other separators found cuts? */
69 #define CONSHDLR_DELAYPROP FALSE /**< should propagation method be delayed, if other propagators found reductions? */
70 #define CONSHDLR_DELAYPRESOL FALSE /**< should presolving method be delayed, if other presolvers found reductions? */
71 #define CONSHDLR_NEEDSCONS TRUE /**< should the constraint handler be skipped, if no constraints are available? */
72 
73 #define CONSHDLR_PROP_TIMING SCIP_PROPTIMING_BEFORELP
74 
75 /**@} */
76 
77 /**@name Default parameter values
78  *
79  * @{
80  */
81 
82 /* default parameter values */
83 
84 /* separation */
85 #define DEFAULT_USEBINVARS FALSE /**< should the binary representation be used? */
86 #define DEFAULT_LOCALCUTS FALSE /**< should cuts be added only locally? */
87 #define DEFAULT_USECOVERCUTS TRUE /**< should covering cuts be added? */
88 #define DEFAULT_CUTSASCONSS TRUE /**< should the cuts be created as knapsack constraints? */
89 #define DEFAULT_SEPAOLD TRUE /**< shall old sepa algo be applied? */
90 
91 /* propagation */
92 #define DEFAULT_TTINFER TRUE /**< should time-table (core-times) propagator be used to infer bounds? */
93 #define DEFAULT_EFCHECK FALSE /**< should edge-finding be used to detect an overload? */
94 #define DEFAULT_EFINFER FALSE /**< should edge-finding be used to infer bounds? */
95 #define DEFAULT_USEADJUSTEDJOBS FALSE /**< should during edge-finding jobs be adusted which run on the border of the effective time horizon? */
96 #define DEFAULT_TTEFCHECK TRUE /**< should time-table edge-finding be used to detect an overload? */
97 #define DEFAULT_TTEFINFER TRUE /**< should time-table edge-finding be used to infer bounds? */
98 
99 /* presolving */
100 #define DEFAULT_DUALPRESOLVE TRUE /**< should dual presolving be applied? */
101 #define DEFAULT_COEFTIGHTENING FALSE /**< should coeffisient tightening be applied? */
102 #define DEFAULT_NORMALIZE TRUE /**< should demands and capacity be normalized? */
103 #define DEFAULT_PRESOLPAIRWISE TRUE /**< should pairwise constraint comparison be performed in presolving? */
104 #define DEFAULT_DISJUNCTIVE TRUE /**< extract disjunctive constraints? */
105 #define DEFAULT_DETECTDISJUNCTIVE TRUE /**< search for conflict set via maximal cliques to detect disjunctive constraints */
106 #define DEFAULT_DETECTVARBOUNDS TRUE /**< search for conflict set via maximal cliques to detect variable bound constraints */
107 #define DEFAULT_MAXNODES 10000LL /**< number of branch-and-bound nodes to solve an independent cumulative constraint (-1: no limit) */
108 
109 /* enforcement */
110 #define DEFAULT_FILLBRANCHCANDS FALSE /**< should branching candidates be added to storage? */
111 
112 /* conflict analysis */
113 #define DEFAULT_USEBDWIDENING TRUE /**< should bound widening be used during conflict analysis? */
114 
115 /**@} */
116 
117 /**@name Event handler properties
118  *
119  * @{
120  */
121 
122 #define EVENTHDLR_NAME "cumulative"
123 #define EVENTHDLR_DESC "bound change event handler for cumulative constraints"
124 
125 /**@} */
126 
127 /*
128  * Data structures
129  */
130 
131 /** constraint data for cumulative constraints */
132 struct SCIP_ConsData
133 {
134  SCIP_VAR** vars; /**< array of variable representing the start time of each job */
135  SCIP_Bool* downlocks; /**< array to store if the variable has a down lock */
136  SCIP_Bool* uplocks; /**< array to store if the variable has an uplock */
137  SCIP_CONS** linkingconss; /**< array of linking constraints for the integer variables */
138  SCIP_ROW** demandrows; /**< array of rows of linear relaxation of this problem */
139  SCIP_ROW** scoverrows; /**< array of rows of small cover cuts of this problem */
140  SCIP_ROW** bcoverrows; /**< array of rows of big cover cuts of this problem */
141  int* demands; /**< array containing corresponding demands */
142  int* durations; /**< array containing corresponding durations */
143  SCIP_Real resstrength1; /**< stores the resource strength 1*/
144  SCIP_Real resstrength2; /**< stores the resource strength 2 */
145  SCIP_Real cumfactor1; /**< stroes the cumulativeness of the constraint */
146  SCIP_Real disjfactor1; /**< stores the disjunctiveness of the constraint */
147  SCIP_Real disjfactor2; /**< stores the disjunctiveness of the constraint */
148  SCIP_Real estimatedstrength;
149  int nvars; /**< number of variables */
150  int varssize; /**< size of the arrays */
151  int ndemandrows; /**< number of rows of cumulative constrint for linear relaxation */
152  int demandrowssize; /**< size of array rows of demand rows */
153  int nscoverrows; /**< number of rows of small cover cuts */
154  int scoverrowssize; /**< size of array of small cover cuts */
155  int nbcoverrows; /**< number of rows of big cover cuts */
156  int bcoverrowssize; /**< size of array of big cover cuts */
157  int capacity; /**< available cumulative capacity */
158 
159  int hmin; /**< left bound of time axis to be considered (including hmin) */
160  int hmax; /**< right bound of time axis to be considered (not including hmax) */
161 
162  unsigned int signature; /**< constraint signature which is need for pairwise comparison */
163 
164  unsigned int validsignature:1; /**< is the signature valid */
165  unsigned int normalized:1; /**< is the constraint normalized */
166  unsigned int covercuts:1; /**< cover cuts are created? */
167  unsigned int propagated:1; /**< is constraint propagted */
168  unsigned int varbounds:1; /**< bool to store if variable bound strengthening was already preformed */
169  unsigned int triedsolving:1; /**< bool to store if we tried already to solve that constraint as independent subproblem */
170 
171 #ifdef SCIP_STATISTIC
172  int maxpeak;
173 #endif
174 };
175 
176 /** constraint handler data */
177 struct SCIP_ConshdlrData
178 {
179  SCIP_EVENTHDLR* eventhdlr; /**< event handler for bound change events */
180 
181  SCIP_Bool usebinvars; /**< should the binary variables be used? */
182  SCIP_Bool cutsasconss; /**< should the cumulative constraint create cuts as knapsack constraints? */
183  SCIP_Bool ttinfer; /**< should time-table (core-times) propagator be used to infer bounds? */
184  SCIP_Bool efcheck; /**< should edge-finding be used to detect an overload? */
185  SCIP_Bool efinfer; /**< should edge-finding be used to infer bounds? */
186  SCIP_Bool useadjustedjobs; /**< should during edge-finding jobs be adusted which run on the border of the effective time horizon? */
187  SCIP_Bool ttefcheck; /**< should time-table edge-finding be used to detect an overload? */
188  SCIP_Bool ttefinfer; /**< should time-table edge-finding be used to infer bounds? */
189  SCIP_Bool localcuts; /**< should cuts be added only locally? */
190  SCIP_Bool usecovercuts; /**< should covering cuts be added? */
191  SCIP_Bool sepaold; /**< shall old sepa algo be applied? */
192 
193 
194  SCIP_Bool fillbranchcands; /**< should branching candidates be added to storage? */
195 
196  SCIP_Bool dualpresolve; /**< should dual presolving be applied? */
197  SCIP_Bool coeftightening; /**< should coeffisient tightening be applied? */
198  SCIP_Bool normalize; /**< should demands and capacity be normalized? */
199  SCIP_Bool disjunctive; /**< extract disjunctive constraints? */
200  SCIP_Bool detectdisjunctive; /**< search for conflict set via maximal cliques to detect disjunctive constraints */
201  SCIP_Bool detectvarbounds; /**< search for conflict set via maximal cliques to detect variable bound constraints */
202  SCIP_Bool usebdwidening; /**< should bound widening be used during conflict analysis? */
203  SCIP_Bool presolpairwise; /**< should pairwise constraint comparison be performed in presolving? */
204 
205  SCIP_Longint maxnodes; /**< number of branch-and-bound nodes to solve an independent cumulative constraint (-1: no limit) */
206 
207  SCIP_DECL_SOLVECUMULATIVE((*solveCumulative)); /**< method to use a single cumulative condition */
208 
209  /* statistic values which are collected if SCIP_STATISTIC is defined */
210 #ifdef SCIP_STATISTIC
211  SCIP_Longint nlbtimetable; /**< number of times the lower bound was tightened by the time-table propagator */
212  SCIP_Longint nubtimetable; /**< number of times the upper bound was tightened by the time-table propagator */
213  SCIP_Longint ncutofftimetable; /**< number of times the a cutoff was detected due to time-table propagator */
214  SCIP_Longint nlbedgefinder; /**< number of times the lower bound was tightened by the edge-finder propagator */
215  SCIP_Longint nubedgefinder; /**< number of times the upper bound was tightened by the edge-finder propagator */
216  SCIP_Longint ncutoffedgefinder; /**< number of times the a cutoff was detected due to edge-finder propagator */
217  SCIP_Longint ncutoffoverload; /**< number of times the a cutoff was detected due to overload checking via edge-finding */
218  SCIP_Longint nlbTTEF; /**< number of times the lower bound was tightened by time-table edge-finding */
219  SCIP_Longint nubTTEF; /**< number of times the upper bound was tightened by time-table edge-finding */
220  SCIP_Longint ncutoffoverloadTTEF;/**< number of times the a cutoff was detected due to overload checking via time-table edge-finding */
221 
222  int nirrelevantjobs; /**< number of time a irrelevant/redundant jobs was removed form a constraint */
223  int nalwaysruns; /**< number of time a job removed form a constraint which run completely during the effective horizon */
224  int nremovedlocks; /**< number of times a up or down lock was removed */
225  int ndualfixs; /**< number of times a dual fix was performed by a single constraint */
226  int ndecomps; /**< number of times a constraint was decomposed */
227  int ndualbranchs; /**< number of times a dual branch was discoverd and applicable via probing */
228  int nallconsdualfixs; /**< number of times a dual fix was performed due to knowledge of all cumulative constraints */
229  int naddedvarbounds; /**< number of added variable bounds constraints */
230  int naddeddisjunctives; /**< number of added disjunctive constraints */
231 
232  SCIP_Bool iscopy; /**< Boolean to store if constraint handler is part of a copy */
233 #endif
234 };
235 
236 /**@name Inference Information Methods
237  *
238  * An inference information can be passed with each domain reduction to SCIP. This information is passed back to the
239  * constraint handler if the corresponding bound change has to be explained. It can be used to store information which
240  * help to construct a reason/explanation for a bound change. The inference information is limited to size of integer.
241  *
242  * In case of the cumulative constraint handler we store the used propagation algorithms for that particular bound
243  * change and the earliest start and latest completion time of all jobs in the conflict set.
244  *
245  * @{
246  */
247 
248 /** Propagation rules */
250 {
251  PROPRULE_1_CORETIMES = 1, /**< core-time propagator */
252  PROPRULE_2_EDGEFINDING = 2, /**< edge-finder */
253  PROPRULE_3_TTEF = 3 /**< time-table edeg-finding */
254 };
255 typedef enum Proprule PROPRULE;
256 
257 /** inference information */
258 struct InferInfo
259 {
260  union
261  {
262  /** struct to use the inference information */
263  struct
264  {
265  unsigned int proprule:2; /**< propagation rule that was applied */
266  unsigned int data1:15; /**< data field one */
267  unsigned int data2:15; /**< data field two */
268  } asbits;
269  int asint; /**< inference information as a single int value */
270  } val;
271 };
272 typedef struct InferInfo INFERINFO;
273 
274 /** converts an integer into an inference information */
275 static
277  int i /**< integer to convert */
278  )
279 {
280  INFERINFO inferinfo;
281 
282  inferinfo.val.asint = i;
283 
284  return inferinfo;
285 }
286 
287 /** converts an inference information into an int */
288 static
290  INFERINFO inferinfo /**< inference information to convert */
291  )
292 {
293  return inferinfo.val.asint;
294 }
295 
296 /** returns the propagation rule stored in the inference information */
297 static
299  INFERINFO inferinfo /**< inference information to convert */
300  )
301 {
302  return (PROPRULE) inferinfo.val.asbits.proprule;
303 }
304 
305 /** returns data field one of the inference information */
306 static
308  INFERINFO inferinfo /**< inference information to convert */
309  )
310 {
311  return (int) inferinfo.val.asbits.data1;
312 }
313 
314 /** returns data field two of the inference information */
315 static
317  INFERINFO inferinfo /**< inference information to convert */
318  )
319 {
320  return (int) inferinfo.val.asbits.data2;
321 }
322 
323 
324 /** constructs an inference information out of a propagation rule, an earliest start and a latest completion time */
325 static
327  PROPRULE proprule, /**< propagation rule that deduced the value */
328  int data1, /**< data field one */
329  int data2 /**< data field two */
330  )
331 {
332  INFERINFO inferinfo;
333 
334  /* check that the data menber are in the range of the available bits */
335  assert((int)proprule < (1<<2));
336  assert(data1 >= 0 && data1 < (1<<15));
337  assert(data2 >= 0 && data2 < (1<<15));
338 
339  inferinfo.val.asbits.proprule = proprule; /*lint !e641*/
340  inferinfo.val.asbits.data1 = (unsigned int) data1; /*lint !e732*/
341  inferinfo.val.asbits.data2 = (unsigned int) data2; /*lint !e732*/
342 
343  return inferinfo;
344 }
345 
346 /**@} */
347 
348 /*
349  * Local methods
350  */
351 
352 /**@name Miscellaneous Methods
353  *
354  * @{
355  */
356 
357 #ifndef NDEBUG
358 /** converts the given double bound which is integral to an int; in optimized mode the function gets inlined for
359  * performance; in debug mode we check some additional conditions
360  */
361 static
363  SCIP* scip, /**< SCIP data structure */
364  SCIP_Real bound /**< double bound to convert */
365  )
366 {
367  assert(SCIPisFeasIntegral(scip, bound));
368  assert(SCIPisFeasEQ(scip, bound, (SCIP_Real)(int)(bound < 0 ? bound - 0.5 : bound + 0.5)));
369  assert(bound < INT_MAX);
370  assert(bound > INT_MIN);
371 
372  return (int)(bound < 0 ? (bound - 0.5) : (bound + 0.5));
373 }
374 
375 /** compute the core of a job which lies in certain interval [begin, end) */
376 static
378  int begin, /**< begin of the interval */
379  int end, /**< end of the interval */
380  int ect, /**< earliest completion time */
381  int lst /**< latest start time */
382  )
383 {
384  int core;
385 
386  core = MAX(0, MIN(end, ect) - MAX(lst, begin));
387 
388  return core;
389 }
390 #else
391 #define convertBoundToInt(x, y) ((int)((y) < 0 ? (y) - 0.5 : (y) + 0.5))
392 #define computeCoreWithInterval(begin, end, ect, lst) (MAX(0, MIN((end), (ect)) - MAX((lst), (begin))))
393 #endif
394 
395 /** returns the implied earliest start time */
396 static
398  SCIP* scip, /**< SCIP data structure */
399  SCIP_VAR* var, /**< variable for which the implied est should be returned */
400  SCIP_HASHMAP* addedvars, /**< hash map containig the variable which are already added */
401  int* est /**< pointer to store the implied earliest start time */
402  )
403 { /*lint --e{715}*/
404 #if 0
405  SCIP_VAR** vbdvars;
406  SCIP_VAR* vbdvar;
407  SCIP_Real* vbdcoefs;
408  SCIP_Real* vbdconsts;
409  void* image;
410  int nvbdvars;
411  int v;
412 #endif
413 
414  (*est) = convertBoundToInt(scip, SCIPvarGetLbLocal(var));
415 
416 #if 0
417  /* the code contains a bug; we need to check if an implication forces that the jobs do not run in parallel */
418 
419  nvbdvars = SCIPvarGetNVlbs(var);
420  vbdvars = SCIPvarGetVlbVars(var);
421  vbdcoefs = SCIPvarGetVlbCoefs(var);
422  vbdconsts = SCIPvarGetVlbConstants(var);
423 
424  for( v = 0; v < nvbdvars; ++v )
425  {
426  vbdvar = vbdvars[v];
427  assert(vbdvar != NULL);
428 
429  image = SCIPhashmapGetImage(addedvars, (void*)vbdvar);
430 
431  if( image != NULL && SCIPisEQ(scip, vbdcoefs[v], 1.0 ) )
432  {
433  int duration;
434  int vbdconst;
435 
436  duration = (int)(size_t)image;
437  vbdconst = convertBoundToInt(scip, vbdconsts[v]);
438 
439  SCIPdebugMessage("check implication <%s>[%g,%g] >= <%s>[%g,%g] + <%g>\n",
441  SCIPvarGetName(vbdvar), SCIPvarGetLbLocal(vbdvar), SCIPvarGetUbLocal(vbdvar), vbdconsts[v]);
442 
443  if( duration >= vbdconst )
444  {
445  int impliedest;
446 
447  impliedest = convertBoundToInt(scip, SCIPvarGetUbLocal(vbdvar)) + duration;
448 
449  if( (*est) < impliedest )
450  {
451  (*est) = impliedest;
452 
453  SCIP_CALL( SCIPhashmapRemove(addedvars, (void*)vbdvar) );
454  }
455  }
456  }
457  }
458 #endif
459 
460  return SCIP_OKAY;
461 }
462 
463 /** returns the implied latest completion time */
464 static
466  SCIP* scip, /**< SCIP data structure */
467  SCIP_VAR* var, /**< variable for which the implied est should be returned */
468  int duration, /**< duration of the given job */
469  SCIP_HASHMAP* addedvars, /**< hash map containig the variable which are already added */
470  int* lct /**< pointer to store the implied latest completion time */
471  )
472 { /*lint --e{715}*/
473 #if 0
474  SCIP_VAR** vbdvars;
475  SCIP_VAR* vbdvar;
476  SCIP_Real* vbdcoefs;
477  SCIP_Real* vbdconsts;
478  int nvbdvars;
479  int v;
480 #endif
481 
482  (*lct) = convertBoundToInt(scip, SCIPvarGetUbLocal(var)) + duration;
483 
484 #if 0
485  /* the code contains a bug; we need to check if an implication forces that the jobs do not run in parallel */
486 
487  nvbdvars = SCIPvarGetNVubs(var);
488  vbdvars = SCIPvarGetVubVars(var);
489  vbdcoefs = SCIPvarGetVubCoefs(var);
490  vbdconsts = SCIPvarGetVubConstants(var);
491 
492  for( v = 0; v < nvbdvars; ++v )
493  {
494  vbdvar = vbdvars[v];
495  assert(vbdvar != NULL);
496 
497  if( SCIPhashmapExists(addedvars, (void*)vbdvar) && SCIPisEQ(scip, vbdcoefs[v], 1.0 ) )
498  {
499  int vbdconst;
500 
501  vbdconst = convertBoundToInt(scip, -vbdconsts[v]);
502 
503  SCIPdebugMessage("check implication <%s>[%g,%g] <= <%s>[%g,%g] + <%g>\n",
505  SCIPvarGetName(vbdvar), SCIPvarGetLbLocal(vbdvar), SCIPvarGetUbLocal(vbdvar), vbdconsts[v]);
506 
507  if( duration >= -vbdconst )
508  {
509  int impliedlct;
510 
511  impliedlct = convertBoundToInt(scip, SCIPvarGetLbLocal(vbdvar));
512 
513  if( (*lct) > impliedlct )
514  {
515  (*lct) = impliedlct;
516 
517  SCIP_CALL( SCIPhashmapRemove(addedvars, (void*)vbdvar) );
518  }
519  }
520  }
521  }
522 #endif
523 
524  return SCIP_OKAY;
525 }
526 
527 /** collects all necessary binary variables to represent the jobs which can be active at time point of interest */
528 static
530  SCIP* scip, /**< SCIP data structure */
531  SCIP_CONSDATA* consdata, /**< constraint data */
532  SCIP_VAR*** vars, /**< pointer to the array to store the binary variables */
533  int** coefs, /**< pointer to store the coefficients */
534  int* nvars, /**< number if collect binary variables */
535  int* startindices, /**< permutation with rspect to the start times */
536  int curtime, /**< current point in time */
537  int nstarted, /**< number of jobs that start before the curtime or at curtime */
538  int nfinished /**< number of jobs that finished before curtime or at curtime */
539  )
540 {
541  int nrowvars;
542  int startindex;
543  int size;
544 
545  size = 10;
546  nrowvars = 0;
547  startindex = nstarted - 1;
548 
549  SCIP_CALL( SCIPallocBufferArray(scip, vars, size) );
550  SCIP_CALL( SCIPallocBufferArray(scip, coefs, size) );
551 
552  /* search for the (nstarted - nfinished) jobs which are active at curtime */
553  while( nstarted - nfinished > nrowvars )
554  {
555  SCIP_VAR* var;
556  int endtime;
557  int duration;
558  int demand;
559  int varidx;
560 
561  /* collect job information */
562  varidx = startindices[startindex];
563  assert(varidx >= 0 && varidx < consdata->nvars);
564 
565  var = consdata->vars[varidx];
566  duration = consdata->durations[varidx];
567  demand = consdata->demands[varidx];
568  assert(var != NULL);
569 
570  endtime = convertBoundToInt(scip, SCIPvarGetUbGlobal(var)) + duration;
571 
572  /* check the end time of this job is larger than the curtime; in this case the job is still running */
573  if( endtime > curtime )
574  {
575  SCIP_VAR** binvars;
576  int* vals;
577  int nbinvars;
578  int start;
579  int end;
580  int b;
581 
582  /* check if the linking constraints exists */
583  assert(SCIPexistsConsLinking(scip, var));
584  assert(SCIPgetConsLinking(scip, var) != NULL);
585  assert(SCIPgetConsLinking(scip, var) == consdata->linkingconss[varidx]);
586 
587  /* collect linking constraint information */
588  SCIP_CALL( SCIPgetBinvarsLinking(scip, consdata->linkingconss[varidx], &binvars, &nbinvars) );
589  vals = SCIPgetValsLinking(scip, consdata->linkingconss[varidx]);
590 
591  start = curtime - duration + 1;
592  end = MIN(curtime, endtime - duration);
593 
594  for( b = 0; b < nbinvars; ++b )
595  {
596  if( vals[b] < start )
597  continue;
598 
599  if( vals[b] > end )
600  break;
601 
602  assert(binvars[b] != NULL);
603 
604  /* ensure array proper array size */
605  if( size == *nvars )
606  {
607  size *= 2;
608  SCIP_CALL( SCIPreallocBufferArray(scip, vars, size) );
609  SCIP_CALL( SCIPreallocBufferArray(scip, coefs, size) );
610  }
611 
612  (*vars)[*nvars] = binvars[b];
613  (*coefs)[*nvars] = demand;
614  (*nvars)++;
615  }
616  nrowvars++;
617  }
618 
619  startindex--;
620  }
621 
622  return SCIP_OKAY;
623 }
624 
625 /** collect all integer variable which belong to jobs which can run at the point of interest */
626 static
628  SCIP* scip, /**< SCIP data structure */
629  SCIP_CONSDATA* consdata, /**< constraint data */
630  SCIP_VAR*** activevars, /**< jobs that are currently running */
631  int* startindices, /**< permutation with rspect to the start times */
632  int curtime, /**< current point in time */
633  int nstarted, /**< number of jobs that start before the curtime or at curtime */
634  int nfinished, /**< number of jobs that finished before curtime or at curtime */
635  SCIP_Bool lower, /**< shall cuts be created due to lower or upper bounds? */
636  int* lhs /**< lhs for the new row sum of lbs + minoffset */
637  )
638 {
639  SCIP_VAR* var;
640  int startindex;
641  int endtime;
642  int duration;
643  int starttime;
644 
645  int varidx;
646  int sumofstarts;
647  int mindelta;
648  int counter;
649 
650  assert(curtime >= consdata->hmin);
651  assert(curtime < consdata->hmax);
652 
653  counter = 0;
654  sumofstarts = 0;
655 
656  mindelta = INT_MAX;
657 
658  startindex = nstarted - 1;
659 
660  /* search for the (nstarted - nfinished) jobs which are active at curtime */
661  while( nstarted - nfinished > counter )
662  {
663  assert(startindex >= 0);
664 
665  /* collect job information */
666  varidx = startindices[startindex];
667  assert(varidx >= 0 && varidx < consdata->nvars);
668 
669  var = consdata->vars[varidx];
670  duration = consdata->durations[varidx];
671  assert(duration > 0);
672  assert(var != NULL);
673 
674  if( lower )
675  starttime = convertBoundToInt(scip, SCIPvarGetLbLocal(var));
676  else
677  starttime = convertBoundToInt(scip, SCIPvarGetUbLocal(var));
678 
679  endtime = MIN(starttime + duration, consdata->hmax);
680 
681  /* check the end time of this job is larger than the curtime; in this case the job is still running */
682  if( endtime > curtime )
683  {
684  (*activevars)[counter] = var;
685  sumofstarts += starttime;
686  mindelta = MIN(mindelta, endtime - curtime); /* this amount of schifting holds for lb and ub */
687  counter++;
688  }
689 
690  startindex--;
691  }
692 
693  assert(mindelta > 0);
694  *lhs = lower ? sumofstarts + mindelta : sumofstarts - mindelta;
695 
696  return SCIP_OKAY;
697 }
698 
699 /** initialize the sorted event point arrays */
700 static
702  SCIP* scip, /**< SCIP data structure */
703  int nvars, /**< number of start time variables (activities) */
704  SCIP_VAR** vars, /**< array of start time variables */
705  int* durations, /**< array of durations per start time variable */
706  int* starttimes, /**< array to store sorted start events */
707  int* endtimes, /**< array to store sorted end events */
708  int* startindices, /**< permutation with rspect to the start times */
709  int* endindices, /**< permutation with rspect to the end times */
710  SCIP_Bool local /**< shall local bounds be used */
711  )
712 {
713  SCIP_VAR* var;
714  int j;
715 
716  assert(vars != NULL || nvars == 0);
717 
718  /* assign variables, start and endpoints to arrays */
719  for ( j = 0; j < nvars; ++j )
720  {
721  assert(vars != NULL);
722 
723  var = vars[j];
724  assert(var != NULL);
725 
726  if( local )
727  starttimes[j] = convertBoundToInt(scip, SCIPvarGetLbLocal(var));
728  else
729  starttimes[j] = convertBoundToInt(scip, SCIPvarGetLbGlobal(var));
730 
731  startindices[j] = j;
732 
733  if( local )
734  endtimes[j] = convertBoundToInt(scip, SCIPvarGetUbLocal(var)) + durations[j];
735  else
736  endtimes[j] = convertBoundToInt(scip, SCIPvarGetUbGlobal(var)) + durations[j];
737 
738  endindices[j] = j;
739 
740  }
741 
742  /* sort the arrays not-decreasing according to startsolvalues and endsolvalues (and sort the indices in the same way) */
743  SCIPsortIntInt(starttimes, startindices, j);
744  SCIPsortIntInt(endtimes, endindices, j);
745 }
746 
747 /** initialize the sorted event point arrays w.r.t. the given primal solutions */
748 static
750  SCIP* scip, /**< SCIP data structure */
751  SCIP_SOL* sol, /**< solution */
752  int nvars, /**< number of start time variables (activities) */
753  SCIP_VAR** vars, /**< array of start time variables */
754  int* durations, /**< array of durations per start time variable */
755  int* starttimes, /**< array to store sorted start events */
756  int* endtimes, /**< array to store sorted end events */
757  int* startindices, /**< permutation with rspect to the start times */
758  int* endindices /**< permutation with rspect to the end times */
759  )
760 {
761  SCIP_VAR* var;
762  int j;
763 
764  assert(vars != NULL || nvars == 0);
765 
766  /* assign variables, start and endpoints to arrays */
767  for ( j = 0; j < nvars; ++j )
768  {
769  assert(vars != NULL);
770 
771  var = vars[j];
772  assert(var != NULL);
773 
774  starttimes[j] = convertBoundToInt(scip, SCIPgetSolVal(scip, sol, var));
775  startindices[j] = j;
776 
777  endtimes[j] = convertBoundToInt(scip, SCIPgetSolVal(scip, sol, var)) + durations[j];
778  endindices[j] = j;
779 
780  }
781 
782  /* sort the arrays not-decreasing according to startsolvalues and endsolvalues (and sort the indices in the same way) */
783  SCIPsortIntInt(starttimes, startindices, j);
784  SCIPsortIntInt(endtimes, endindices, j);
785 }
786 
787 /** initialize the sorted event point arrays
788  *
789  * @todo Check the separation process!
790  */
791 static
793  SCIP* scip, /**< SCIP data structure */
794  SCIP_CONSDATA* consdata, /**< constraint data */
795  SCIP_SOL* sol, /**< primal CIP solution, NULL for current LP solution */
796  int* starttimes, /**< array to store sorted start events */
797  int* endtimes, /**< array to store sorted end events */
798  int* startindices, /**< permutation with rspect to the start times */
799  int* endindices, /**< permutation with rspect to the end times */
800  int* nvars, /**< number of variables that are integral */
801  SCIP_Bool lower /**< shall the constraints be derived for lower or upper bounds? */
802  )
803 {
804  SCIP_VAR* var;
805  int tmpnvars;
806  int j;
807 
808  tmpnvars = consdata->nvars;
809  *nvars = 0;
810 
811  /* assign variables, start and endpoints to arrays */
812  for ( j = 0; j < tmpnvars; ++j )
813  {
814  var = consdata->vars[j];
815  assert(var != NULL);
816  assert(consdata->durations[j] > 0);
817  assert(consdata->demands[j] > 0);
818 
819  if( lower )
820  {
821  /* only consider jobs that are at their lower or upper bound */
822  if( !SCIPisFeasIntegral(scip, SCIPgetSolVal(scip, sol, var))
823  || !SCIPisFeasEQ(scip, SCIPgetSolVal(scip, sol, var), SCIPvarGetLbLocal(var)) )
824  continue;
825 
826 
827  starttimes[*nvars] = convertBoundToInt(scip, SCIPgetSolVal(scip, sol, var));
828  startindices[*nvars] = j;
829 
830  endtimes[*nvars] = starttimes[*nvars] + consdata->durations[j];
831  endindices[*nvars] = j;
832 
833  SCIPdebugMessage("%d: variable <%s>[%g,%g] (sol %g, duration %d) starttime %d, endtime = %d, demand = %d\n",
834  *nvars, SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var), SCIPgetSolVal(scip, sol, var),
835  consdata->durations[j],
836  starttimes[*nvars], starttimes[*nvars] + consdata->durations[startindices[*nvars]],
837  consdata->demands[startindices[*nvars]]);
838 
839  (*nvars)++;
840  }
841  else
842  {
843  if( !SCIPisFeasIntegral(scip, SCIPgetSolVal(scip, sol, var))
844  || !SCIPisFeasEQ(scip, SCIPgetSolVal(scip, sol, var), SCIPvarGetUbLocal(var)) )
845  continue;
846 
847  starttimes[*nvars] = convertBoundToInt(scip, SCIPgetSolVal(scip, sol, var));
848  startindices[*nvars] = j;
849 
850  endtimes[*nvars] = starttimes[*nvars] + consdata->durations[j];
851  endindices[*nvars] = j;
852 
853  SCIPdebugMessage("%d: variable <%s>[%g,%g] (sol %g, duration %d) starttime %d, endtime = %d, demand = %d\n",
854  *nvars, SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var), SCIPgetSolVal(scip, sol, var),
855  consdata->durations[j],
856  starttimes[*nvars], starttimes[*nvars] + consdata->durations[startindices[*nvars]],
857  consdata->demands[startindices[*nvars]]);
858 
859  (*nvars)++;
860  }
861  }
862 
863  /* sort the arrays not-decreasing according to startsolvalues and endsolvalues (and sort the indices in the same way) */
864  SCIPsortIntInt(starttimes, startindices, *nvars);
865  SCIPsortIntInt(endtimes, endindices, *nvars);
866 
867 #ifdef SCIP_DEBUG
868  SCIPdebugMessage("sorted output %d\n", *nvars);
869 
870  for ( j = 0; j < *nvars; ++j )
871  {
872  SCIPdebugMessage("%d: job[%d] starttime %d, endtime = %d, demand = %d\n", j,
873  startindices[j], starttimes[j], starttimes[j] + consdata->durations[startindices[j]],
874  consdata->demands[startindices[j]]);
875  }
876 
877  for ( j = 0; j < *nvars; ++j )
878  {
879  SCIPdebugMessage("%d: job[%d] endtime %d, demand = %d\n", j, endindices[j], endtimes[j],
880  consdata->demands[endindices[j]]);
881  }
882 #endif
883 }
884 
885 #ifdef SCIP_STATISTIC
886 /** this method checks for relevant intervals for energetic reasoning */
887 static
888 SCIP_RETCODE computeRelevantEnergyIntervals(
889  SCIP* scip, /**< SCIP data structure */
890  int nvars, /**< number of start time variables (activities) */
891  SCIP_VAR** vars, /**< array of start time variables */
892  int* durations, /**< array of durations */
893  int* demands, /**< array of demands */
894  int capacity, /**< cumulative capacity */
895  int hmin, /**< left bound of time axis to be considered (including hmin) */
896  int hmax, /**< right bound of time axis to be considered (not including hmax) */
897  int** timepoints, /**< array to store relevant points in time */
898  SCIP_Real** cumulativedemands, /**< array to store the estimated cumulative demand for each point in time */
899  int* ntimepoints, /**< pointer to store the number of timepoints */
900  int* maxdemand, /**< pointer to store maximum over all demands */
901  SCIP_Real* minfreecapacity /**< pointer to store the minimum free capacity */
902  )
903 {
904  int* starttimes; /* stores when each job is starting */
905  int* endtimes; /* stores when each job ends */
906  int* startindices; /* we will sort the startsolvalues, thus we need to know wich index of a job it corresponds to */
907  int* endindices; /* we will sort the endsolvalues, thus we need to know wich index of a job it corresponds to */
908 
909  SCIP_Real totaldemand;
910  int curtime; /* point in time which we are just checking */
911  int endindex; /* index of endsolvalues with: endsolvalues[endindex] > curtime */
912 
913  int j;
914 
915  assert( scip != NULL );
916  assert(durations != NULL);
917  assert(demands != NULL);
918  assert(capacity >= 0);
919 
920  /* if no activities are associated with this cumulative then this constraint is redundant */
921  if( nvars == 0 )
922  return SCIP_OKAY;
923 
924  assert(vars != NULL);
925 
926  SCIP_CALL( SCIPallocBufferArray(scip, &starttimes, nvars) );
927  SCIP_CALL( SCIPallocBufferArray(scip, &endtimes, nvars) );
928  SCIP_CALL( SCIPallocBufferArray(scip, &startindices, nvars) );
929  SCIP_CALL( SCIPallocBufferArray(scip, &endindices, nvars) );
930 
931  /* create event point arrays */
932  createSortedEventpoints(scip, nvars, vars, durations, starttimes, endtimes, startindices, endindices, TRUE);
933 
934  endindex = 0;
935  totaldemand = 0.0;
936 
937  *ntimepoints = 0;
938  (*timepoints)[0] = starttimes[0];
939  (*cumulativedemands)[0] = 0;
940  *maxdemand = 0;
941 
942  /* check each startpoint of a job whether the capacity is kept or not */
943  for( j = 0; j < nvars; ++j )
944  {
945  int lct;
946  int idx;
947 
948  curtime = starttimes[j];
949 
950  if( curtime >= hmax )
951  break;
952 
953  /* free all capacity usages of jobs the are no longer running */
954  while( endindex < nvars && endtimes[endindex] <= curtime )
955  {
956  int est;
957 
958  if( (*timepoints)[*ntimepoints] < endtimes[endindex] )
959  {
960  (*ntimepoints)++;
961  (*timepoints)[*ntimepoints] = endtimes[endindex];
962  (*cumulativedemands)[*ntimepoints] = 0;
963  }
964 
965  idx = endindices[endindex];
966  est = convertBoundToInt(scip, SCIPvarGetLbLocal(vars[idx]));
967  totaldemand -= (SCIP_Real) demands[idx] * durations[idx] / (endtimes[endindex] - est);
968  endindex++;
969 
970  (*cumulativedemands)[*ntimepoints] = totaldemand;
971  }
972 
973  idx = startindices[j];
974  lct = convertBoundToInt(scip, SCIPvarGetUbLocal(vars[idx]) + durations[idx]);
975  totaldemand += (SCIP_Real) demands[idx] * durations[idx] / (lct - starttimes[j]);
976 
977  if( (*timepoints)[*ntimepoints] < curtime )
978  {
979  (*ntimepoints)++;
980  (*timepoints)[*ntimepoints] = curtime;
981  (*cumulativedemands)[*ntimepoints] = 0;
982  }
983 
984  (*cumulativedemands)[*ntimepoints] = totaldemand;
985 
986  /* add the relative capacity requirements for all job which start at the curtime */
987  while( j+1 < nvars && starttimes[j+1] == curtime )
988  {
989  ++j;
990  idx = startindices[j];
991  lct = convertBoundToInt(scip, SCIPvarGetUbLocal(vars[idx]) + durations[idx]);
992  totaldemand += (SCIP_Real) demands[idx] * durations[idx] / (lct - starttimes[j]);
993 
994  (*cumulativedemands)[*ntimepoints] = totaldemand;
995  }
996  } /*lint --e{850}*/
997 
998  /* free all capacity usages of jobs that are no longer running */
999  while( endindex < nvars/* && endtimes[endindex] < hmax*/)
1000  {
1001  int est;
1002  int idx;
1003 
1004  if( (*timepoints)[*ntimepoints] < endtimes[endindex] )
1005  {
1006  (*ntimepoints)++;
1007  (*timepoints)[*ntimepoints] = endtimes[endindex];
1008  (*cumulativedemands)[*ntimepoints] = 0;
1009  }
1010 
1011  idx = endindices[endindex];
1012  est = convertBoundToInt(scip, SCIPvarGetLbLocal(vars[idx]));
1013  totaldemand -= (SCIP_Real) demands[idx] * durations[idx] / (endtimes[endindex] - est);
1014  (*cumulativedemands)[*ntimepoints] = totaldemand;
1015 
1016  ++endindex;
1017  }
1018 
1019  (*ntimepoints)++;
1020  /* compute minimum free capacity */
1021  (*minfreecapacity) = INT_MAX;
1022  for( j = 0; j < *ntimepoints; ++j )
1023  {
1024  if( (*timepoints)[j] >= hmin && (*timepoints)[j] < hmax )
1025  *minfreecapacity = MIN( *minfreecapacity, (SCIP_Real)capacity - (*cumulativedemands)[j] );
1026  }
1027 
1028  /* free buffer arrays */
1029  SCIPfreeBufferArray(scip, &endindices);
1030  SCIPfreeBufferArray(scip, &startindices);
1031  SCIPfreeBufferArray(scip, &endtimes);
1032  SCIPfreeBufferArray(scip, &starttimes);
1033 
1034  return SCIP_OKAY;
1035 }
1036 
1037 /** evaluates the cumulativeness and disjointness factor of a cumulative constraint */
1038 static
1039 SCIP_RETCODE evaluateCumulativeness(
1040  SCIP* scip, /**< pointer to scip */
1041  SCIP_CONS* cons /**< cumulative constraint */
1042  )
1043 {
1044  SCIP_CONSDATA* consdata;
1045  int nvars;
1046  int v;
1047  int capacity;
1048 
1049  /* output values: */
1050  SCIP_Real disjfactor2; /* (peak-capacity)/capacity * (large demands/nvars_t) */
1051  SCIP_Real cumfactor1;
1052  SCIP_Real resstrength1; /* overall strength */
1053  SCIP_Real resstrength2; /* timepoint wise maximum */
1054 
1055  /* helpful variables: */
1056  SCIP_Real globalpeak;
1057  SCIP_Real globalmaxdemand;
1058 
1059  /* get constraint data structure */
1060  consdata = SCIPconsGetData(cons);
1061  assert(consdata != NULL);
1062 
1063  nvars = consdata->nvars;
1064  capacity = consdata->capacity;
1065  globalpeak = 0.0;
1066  globalmaxdemand = 0.0;
1067 
1068  disjfactor2 = 0.0;
1069  cumfactor1 = 0.0;
1070  resstrength2 = 0.0;
1071 
1072  /* check each starting time (==each job, but inefficient) */
1073  for( v = 0; v < nvars; ++v )
1074  {
1075  SCIP_Real peak;
1076  SCIP_Real maxdemand;
1077  SCIP_Real deltademand;
1078  int ndemands;
1079  int nlarge;
1080 
1081  int timepoint;
1082  int j;
1083  timepoint = convertBoundToInt(scip, SCIPvarGetLbLocal(consdata->vars[v]));
1084  peak = consdata->demands[v];
1085  ndemands = 1;
1086  maxdemand = 0;
1087  nlarge = 0;
1088 
1089  if( consdata->demands[v] > capacity / 3 )
1090  nlarge++;
1091 
1092  for( j = 0; j < nvars; ++j )
1093  {
1094  int lb;
1095 
1096  if( j == v )
1097  continue;
1098 
1099  maxdemand = 0.0;
1100  lb = convertBoundToInt(scip, SCIPvarGetLbLocal(consdata->vars[j]));
1101 
1102  if( lb <= timepoint && lb + consdata->durations[j] > timepoint )
1103  {
1104  peak += consdata->demands[j];
1105  ndemands++;
1106 
1107  if( consdata->demands[j] > consdata->capacity / 3 )
1108  nlarge++;
1109  }
1110  }
1111 
1112  deltademand = (SCIP_Real)peak / (SCIP_Real)ndemands;
1113  globalpeak = MAX(globalpeak, peak);
1114  globalmaxdemand = MAX(globalmaxdemand, maxdemand);
1115 
1116  if( peak > capacity )
1117  {
1118  disjfactor2 = MAX( disjfactor2, (peak-(SCIP_Real)capacity)/peak * (nlarge/(SCIP_Real)ndemands) );
1119  cumfactor1 = MAX( cumfactor1, (peak-capacity)/peak * (capacity-deltademand)/(SCIP_Real)capacity );
1120  resstrength2 = MAX(resstrength2, (capacity-maxdemand)/(peak-maxdemand) );
1121  }
1122  }
1123 
1124  resstrength1 = (capacity-globalmaxdemand) / (globalpeak-globalmaxdemand);
1125 
1126  consdata->maxpeak = convertBoundToInt(scip, globalpeak);
1127  consdata->disjfactor2 = disjfactor2;
1128  consdata->cumfactor1 = cumfactor1;
1129  consdata->resstrength2 = resstrength2;
1130  consdata->resstrength1 = resstrength1;
1131 
1132  /* get estimated res strength */
1133  {
1134  int* timepoints;
1135  SCIP_Real* estimateddemands;
1136  int ntimepoints;
1137  int maxdemand;
1138  SCIP_Real minfreecapacity;
1139 
1140  SCIP_CALL( SCIPallocBufferArray(scip, &timepoints, 2*nvars) );
1141  SCIP_CALL( SCIPallocBufferArray(scip, &estimateddemands, 2*nvars) );
1142 
1143  ntimepoints = 0;
1144  minfreecapacity = INT_MAX;
1145 
1146 
1147  SCIP_CALL( computeRelevantEnergyIntervals(scip, nvars, consdata->vars,
1148  consdata->durations, consdata->demands,
1149  capacity, consdata->hmin, consdata->hmax, &timepoints, &estimateddemands,
1150  &ntimepoints, &maxdemand, &minfreecapacity) );
1151 
1152  /* free buffer arrays */
1153  SCIPfreeBufferArray(scip, &estimateddemands);
1154  SCIPfreeBufferArray(scip, &timepoints);
1155 
1156  consdata->estimatedstrength = (SCIP_Real)(capacity - minfreecapacity) / (SCIP_Real) capacity;
1157  }
1158 
1159  SCIPstatisticPrintf("cumulative constraint<%s>: DISJ1=%g, DISJ2=%g, CUM=%g, RS1 = %g, RS2 = %g, EST = %g\n",
1160  SCIPconsGetName(cons), consdata->disjfactor1, disjfactor2, cumfactor1, resstrength1, resstrength2,
1161  consdata->estimatedstrength);
1162 
1163  return SCIP_OKAY;
1164 }
1165 #endif
1166 
1167 /** gets the active variables together with the constant */
1168 static
1170  SCIP* scip, /**< SCIP data structure */
1171  SCIP_VAR** var, /**< pointer to store the active variable */
1172  int* scalar, /**< pointer to store the scalar */
1173  int* constant /**< pointer to store the constant */
1174  )
1175 {
1176  if( !SCIPvarIsActive(*var) )
1177  {
1178  SCIP_Real realscalar;
1179  SCIP_Real realconstant;
1180 
1181  realscalar = 1.0;
1182  realconstant = 0.0;
1183 
1185 
1186  /* transform variable to active variable */
1187  SCIP_CALL( SCIPgetProbvarSum(scip, var, &realscalar, &realconstant) );
1188  assert(!SCIPisZero(scip, realscalar));
1189  assert(SCIPvarIsActive(*var));
1190 
1191  if( realconstant < 0.0 )
1192  (*constant) = -convertBoundToInt(scip, -realconstant);
1193  else
1194  (*constant) = convertBoundToInt(scip, realconstant);
1195 
1196  if( realscalar < 0.0 )
1197  (*scalar) = -convertBoundToInt(scip, -realscalar);
1198  else
1199  (*scalar) = convertBoundToInt(scip, realscalar);
1200  }
1201  else
1202  {
1203  (*scalar) = 1;
1204  (*constant) = 0;
1205  }
1206 
1207  assert(*scalar != 0);
1208 
1209  return SCIP_OKAY;
1210 }
1211 
1212 /** computes the total energy of all jobs */
1213 static
1215  int* durations, /**< array of job durations */
1216  int* demands, /**< array of job demands */
1217  int njobs /**< number of jobs */
1218  )
1219 {
1220  int energy;
1221  int j;
1222 
1223  energy = 0;
1224 
1225  for( j = 0; j < njobs; ++j )
1226  energy += durations[j] * demands[j];
1227 
1228  return energy;
1229 }
1230 
1231 /**@} */
1232 
1233 /**@name Default method to solve a cumulative condition
1234  *
1235  * @{
1236  */
1237 
1238 /** solve single cumulative condition using SCIP and a single cumulative constraint */
1239 static
1240 SCIP_DECL_SOLVECUMULATIVE(solveCumulativeViaScipCp)
1241 {
1242  SCIP* subscip;
1243  SCIP_VAR** subvars;
1244  SCIP_CONS* cons;
1245  SCIP_RETCODE retcode;
1246  char name[SCIP_MAXSTRLEN];
1247  int v;
1248 
1249  assert(njobs > 0);
1250 
1251  (*solved) = FALSE;
1252  (*infeasible) = FALSE;
1253  (*unbounded) = FALSE;
1254  (*error) = FALSE;
1255 
1256  SCIPdebugMessage("solve independent cumulative condition with %d variables\n", njobs);
1257 
1258  /* initialize the sub-problem */
1259  SCIP_CALL( SCIPcreate(&subscip) );
1260 
1261  /* copy all plugins */
1263 
1264  /* create the subproblem */
1265  SCIP_CALL( SCIPcreateProbBasic(subscip, "cumulative") );
1266 
1267  SCIP_CALL( SCIPallocMemoryArray(subscip, &subvars, njobs) );
1268 
1269  /* create for each job a start time variable */
1270  for( v = 0; v < njobs; ++v )
1271  {
1272  SCIP_Real objval;
1273 
1274  /* construct varibale name */
1275  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "job%d", v);
1276 
1277  if( objvals == NULL )
1278  objval = 0.0;
1279  else
1280  objval = objvals[v];
1281 
1282  SCIP_CALL( SCIPcreateVarBasic(subscip, &subvars[v], name, ests[v], lsts[v], objval, SCIP_VARTYPE_INTEGER) );
1283  SCIP_CALL( SCIPaddVar(subscip, subvars[v]) );
1284  }
1285 
1286  /* create cumulative constraint */
1287  SCIP_CALL( SCIPcreateConsBasicCumulative(subscip, &cons, "cumulative",
1288  njobs, subvars, durations, demands, capacity) );
1289 
1290  /* set effective horizon */
1291  SCIP_CALL( SCIPsetHminCumulative(subscip, cons, hmin) );
1292  SCIP_CALL( SCIPsetHmaxCumulative(subscip, cons, hmax) );
1293 
1294  /* add cumulative constraint */
1295  SCIP_CALL( SCIPaddCons(subscip, cons) );
1296  SCIP_CALL( SCIPreleaseCons(subscip, &cons) );
1297 
1298  /* set CP solver settings
1299  *
1300  * @note This "meta" setting has to be set first since this call overwrite all parameters including for example the
1301  * time limit.
1302  */
1304 
1305  /* do not abort subproblem on CTRL-C */
1306  SCIP_CALL( SCIPsetBoolParam(subscip, "misc/catchctrlc", FALSE) );
1307 
1308  /* disable output to console */
1309  SCIP_CALL( SCIPsetIntParam(subscip, "display/verblevel", 0) );
1310 
1311  /* set limits for the subproblem */
1312  SCIP_CALL( SCIPsetLongintParam(subscip, "limits/nodes", maxnodes) );
1313  SCIP_CALL( SCIPsetRealParam(subscip, "limits/time", timelimit) );
1314  SCIP_CALL( SCIPsetRealParam(subscip, "limits/memory", memorylimit) );
1315 
1316  /* solve single cumulative constraint by branch and bound */
1317  retcode = SCIPsolve(subscip);
1318 
1319  if( retcode != SCIP_OKAY )
1320  (*error) = TRUE;
1321  else
1322  {
1323  SCIPdebugMessage("solved single cumulative condition with status %d\n", SCIPgetStatus(subscip));
1324 
1325  /* evaluated solution status */
1326  switch( SCIPgetStatus(subscip) )
1327  {
1328  case SCIP_STATUS_INFORUNBD:
1330  (*infeasible) = TRUE;
1331  (*solved) = TRUE;
1332  break;
1333  case SCIP_STATUS_UNBOUNDED:
1334  (*unbounded) = TRUE;
1335  (*solved) = TRUE;
1336  break;
1337  case SCIP_STATUS_OPTIMAL:
1338  {
1339  SCIP_SOL* sol;
1340  SCIP_Real solval;
1341 
1342  sol = SCIPgetBestSol(subscip);
1343  assert(sol != NULL);
1344 
1345  for( v = 0; v < njobs; ++v )
1346  {
1347  solval = SCIPgetSolVal(subscip, sol, subvars[v]);
1348 
1349  ests[v] = solval;
1350  lsts[v] = solval;
1351  }
1352  (*solved) = TRUE;
1353  break;
1354  }
1355  case SCIP_STATUS_NODELIMIT:
1357  case SCIP_STATUS_TIMELIMIT:
1358  case SCIP_STATUS_MEMLIMIT:
1360  /* transfer the global bound changes */
1361  for( v = 0; v < njobs; ++v )
1362  {
1363  ests[v] = SCIPvarGetLbGlobal(subvars[v]);
1364  lsts[v] = SCIPvarGetUbGlobal(subvars[v]);
1365  }
1366  (*solved) = FALSE;
1367  break;
1368 
1369  case SCIP_STATUS_UNKNOWN:
1371  case SCIP_STATUS_GAPLIMIT:
1372  case SCIP_STATUS_SOLLIMIT:
1374  SCIPerrorMessage("invalid status code <%d>\n", SCIPgetStatus(subscip));
1375  return SCIP_INVALIDDATA;
1376  }
1377  }
1378 
1379  /* release all variables */
1380  for( v = 0; v < njobs; ++v )
1381  {
1382  SCIP_CALL( SCIPreleaseVar(subscip, &subvars[v]) );
1383  }
1384 
1385  SCIPfreeMemoryArray(subscip, &subvars);
1386 
1387  SCIP_CALL( SCIPfree(&subscip) );
1388 
1389  return SCIP_OKAY;
1390 }
1391 
1392 #if 0
1393 /** solve single cumulative condition using SCIP and the time indexed formulation */
1394 static
1395 SCIP_DECL_SOLVECUMULATIVE(solveCumulativeViaScipMip)
1396 {
1397  SCIP* subscip;
1398  SCIP_VAR*** binvars;
1399  SCIP_RETCODE retcode;
1400  char name[SCIP_MAXSTRLEN];
1401  int minest;
1402  int maxlct;
1403  int t;
1404  int v;
1405 
1406  assert(njobs > 0);
1407 
1408  (*solved) = FALSE;
1409  (*infeasible) = FALSE;
1410  (*unbounded) = FALSE;
1411  (*error) = FALSE;
1412 
1413  SCIPdebugMessage("solve independent cumulative condition with %d variables\n", njobs);
1414 
1415  /* initialize the sub-problem */
1416  SCIP_CALL( SCIPcreate(&subscip) );
1417 
1418  /* copy all plugins */
1420 
1421  /* create the subproblem */
1422  SCIP_CALL( SCIPcreateProbBasic(subscip, "cumulative") );
1423 
1424  SCIP_CALL( SCIPallocMemoryArray(subscip, &binvars, njobs) );
1425 
1426  minest = INT_MAX;
1427  maxlct = INT_MIN;
1428 
1429  /* create for each job and time step a binary variable which is one if this jobs starts at this time point and a set
1430  * partitioning constrain which forces that job starts
1431  */
1432  for( v = 0; v < njobs; ++v )
1433  {
1434  SCIP_CONS* cons;
1435  SCIP_Real objval;
1436  int timeinterval;
1437  int est;
1438  int lst;
1439 
1440  if( objvals == NULL )
1441  objval = 0.0;
1442  else
1443  objval = objvals[v];
1444 
1445  est = ests[v];
1446  lst = lsts[v];
1447 
1448  /* compute number of possible start points */
1449  timeinterval = lst - est + 1;
1450  assert(timeinterval > 0);
1451 
1452  /* compute the smallest earliest start time and largest latest completion time */
1453  minest = MIN(minest, est);
1454  maxlct = MAX(maxlct, lst + durations[v]);
1455 
1456  /* construct constraint name */
1457  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "job_%d", v);
1458 
1459  SCIP_CALL( SCIPcreateConsBasicSetpart(subscip, &cons, name, 0, NULL) );
1460 
1461  SCIP_CALL( SCIPallocMemoryArray(subscip, &binvars[v], timeinterval) );
1462 
1463  for( t = 0; t < timeinterval; ++t )
1464  {
1465  SCIP_VAR* binvar;
1466 
1467  /* construct varibale name */
1468  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "job_%d_time_%d", v, t + est);
1469 
1470  SCIP_CALL( SCIPcreateVarBasic(subscip, &binvar, name, 0.0, 1.0, objval, SCIP_VARTYPE_BINARY) );
1471  SCIP_CALL( SCIPaddVar(subscip, binvar) );
1472 
1473  /* add binary varibale to the set partitioning constraint which ensures that the job is started */
1474  SCIP_CALL( SCIPaddCoefSetppc(subscip, cons, binvar) );
1475 
1476  binvars[v][t] = binvar;
1477  }
1478 
1479  /* add and release the set partitioning constraint */
1480  SCIP_CALL( SCIPaddCons(subscip, cons) );
1481  SCIP_CALL( SCIPreleaseCons(subscip, &cons) );
1482  }
1483 
1484  /* adjusted the smallest earliest start time and the largest latest completion time with the effective horizon */
1485  hmin = MAX(hmin, minest);
1486  hmax = MIN(hmax, maxlct);
1487  assert(hmin > INT_MIN);
1488  assert(hmax < INT_MAX);
1489  assert(hmin < hmax);
1490 
1491  /* create for each time a knapsack constraint which ensures that the resource capacity is not exceeded */
1492  for( t = hmin; t < hmax; ++t )
1493  {
1494  SCIP_CONS* cons;
1495 
1496  /* construct constraint name */
1497  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "time_%d", t);
1498 
1499  /* create an empty knapsack constraint */
1500  SCIP_CALL( SCIPcreateConsBasicKnapsack(subscip, &cons, name, 0, NULL, NULL, (SCIP_Longint)capacity) );
1501 
1502  /* add all jobs which potentially can be processed at that time point */
1503  for( v = 0; v < njobs; ++v )
1504  {
1505  int duration;
1506  int demand;
1507  int start;
1508  int end;
1509  int est;
1510  int lst;
1511  int k;
1512 
1513  est = ests[v];
1514  lst = lsts[v] ;
1515 
1516  duration = durations[v];
1517  assert(duration > 0);
1518 
1519  /* check if the varibale is processed potentially at time point t */
1520  if( t < est || t >= lst + duration )
1521  continue;
1522 
1523  demand = demands[v];
1524  assert(demand >= 0);
1525 
1526  start = MAX(t - duration + 1, est);
1527  end = MIN(t, lst);
1528 
1529  assert(start <= end);
1530 
1531  for( k = start; k <= end; ++k )
1532  {
1533  assert(binvars[v][k] != NULL);
1534  SCIP_CALL( SCIPaddCoefKnapsack(subscip, cons, binvars[v][k], (SCIP_Longint) demand) );
1535  }
1536  }
1537 
1538  /* add and release the knapsack constraint */
1539  SCIP_CALL( SCIPaddCons(subscip, cons) );
1540  SCIP_CALL( SCIPreleaseCons(subscip, &cons) );
1541  }
1542 
1543  /* do not abort subproblem on CTRL-C */
1544  SCIP_CALL( SCIPsetBoolParam(subscip, "misc/catchctrlc", FALSE) );
1545 
1546  /* disable output to console */
1547  SCIP_CALL( SCIPsetIntParam(subscip, "display/verblevel", 0) );
1548 
1549  /* set limits for the subproblem */
1550  SCIP_CALL( SCIPsetLongintParam(subscip, "limits/nodes", maxnodes) );
1551  SCIP_CALL( SCIPsetRealParam(subscip, "limits/time", timelimit) );
1552  SCIP_CALL( SCIPsetRealParam(subscip, "limits/memory", memorylimit) );
1553 
1554  /* solve single cumulative constraint by branch and bound */
1555  retcode = SCIPsolve(subscip);
1556 
1557  if( retcode != SCIP_OKAY )
1558  (*error) = TRUE;
1559  else
1560  {
1561  SCIPdebugMessage("solved single cumulative condition with status %d\n", SCIPgetStatus(subscip));
1562 
1563  /* evaluated solution status */
1564  switch( SCIPgetStatus(subscip) )
1565  {
1566  case SCIP_STATUS_INFORUNBD:
1568  (*infeasible) = TRUE;
1569  (*solved) = TRUE;
1570  break;
1571  case SCIP_STATUS_UNBOUNDED:
1572  (*unbounded) = TRUE;
1573  (*solved) = TRUE;
1574  break;
1575  case SCIP_STATUS_OPTIMAL:
1576  {
1577  SCIP_SOL* sol;
1578 
1579  sol = SCIPgetBestSol(subscip);
1580  assert(sol != NULL);
1581 
1582  for( v = 0; v < njobs; ++v )
1583  {
1584  int timeinterval;
1585  int est;
1586  int lst;
1587 
1588  est = ests[v];
1589  lst = lsts[v];
1590 
1591  /* compute number of possible start points */
1592  timeinterval = lst - est + 1;
1593 
1594  /* check which binary varibale is set to one */
1595  for( t = 0; t < timeinterval; ++t )
1596  {
1597  if( SCIPgetSolVal(subscip, sol, binvars[v][t]) > 0.5 )
1598  {
1599  ests[v] = est + t;
1600  lsts[v] = est + t;
1601  break;
1602  }
1603  }
1604  }
1605 
1606  (*solved) = TRUE;
1607  break;
1608  }
1609  case SCIP_STATUS_NODELIMIT:
1611  case SCIP_STATUS_TIMELIMIT:
1612  case SCIP_STATUS_MEMLIMIT:
1614  /* transfer the global bound changes */
1615  for( v = 0; v < njobs; ++v )
1616  {
1617  int timeinterval;
1618  int est;
1619  int lst;
1620 
1621  est = ests[v];
1622  lst = lsts[v];
1623 
1624  /* compute number of possible start points */
1625  timeinterval = lst - est + 1;
1626 
1627  /* check which binary varibale is the first binary varibale which is not globally fixed to zero */
1628  for( t = 0; t < timeinterval; ++t )
1629  {
1630  if( SCIPvarGetUbGlobal(binvars[v][t]) > 0.5 )
1631  {
1632  ests[v] = est + t;
1633  break;
1634  }
1635  }
1636 
1637  /* check which binary varibale is the last binary varibale which is not globally fixed to zero */
1638  for( t = timeinterval - 1; t >= 0; --t )
1639  {
1640  if( SCIPvarGetUbGlobal(binvars[v][t]) > 0.5 )
1641  {
1642  lsts[v] = est + t;
1643  break;
1644  }
1645  }
1646  }
1647  (*solved) = FALSE;
1648  break;
1649 
1650  case SCIP_STATUS_UNKNOWN:
1652  case SCIP_STATUS_GAPLIMIT:
1653  case SCIP_STATUS_SOLLIMIT:
1655  SCIPerrorMessage("invalid status code <%d>\n", SCIPgetStatus(subscip));
1656  return SCIP_INVALIDDATA;
1657  }
1658  }
1659 
1660  /* release all variables */
1661  for( v = 0; v < njobs; ++v )
1662  {
1663  int timeinterval;
1664  int est;
1665  int lst;
1666 
1667  est = ests[v];
1668  lst = lsts[v];
1669 
1670  /* compute number of possible start points */
1671  timeinterval = lst - est + 1;
1672 
1673  for( t = 0; t < timeinterval; ++t )
1674  {
1675  SCIP_CALL( SCIPreleaseVar(subscip, &binvars[v][t]) );
1676  }
1677  SCIPfreeMemoryArray(subscip, &binvars[v]);
1678  }
1679 
1680  SCIPfreeMemoryArray(subscip, &binvars);
1681 
1682  SCIP_CALL( SCIPfree(&subscip) );
1683 
1684  return SCIP_OKAY;
1685 }
1686 #endif
1687 
1688 /**@} */
1689 
1690 /**@name Constraint handler data
1691  *
1692  * Method used to create and free the constraint handler data when including and removing the cumulative constraint
1693  * handler.
1694  *
1695  * @{
1696  */
1697 
1698 /** creates constaint handler data for cumulative constraint handler */
1699 static
1701  SCIP* scip, /**< SCIP data structure */
1702  SCIP_CONSHDLRDATA** conshdlrdata, /**< pointer to store the constraint handler data */
1703  SCIP_EVENTHDLR* eventhdlr /**< event handler */
1704  )
1705 {
1706  /* create precedence constraint handler data */
1707  assert(scip != NULL);
1708  assert(conshdlrdata != NULL);
1709  assert(eventhdlr != NULL);
1710 
1711  SCIP_CALL( SCIPallocMemory(scip, conshdlrdata) );
1712 
1713  /* set event handler for checking if bounds of start time variables are tighten */
1714  (*conshdlrdata)->eventhdlr = eventhdlr;
1715 
1716  /* set default methed for solving single cumulative conditions using SCIP and a CP model */
1717  (*conshdlrdata)->solveCumulative = solveCumulativeViaScipCp;
1718 
1719 #ifdef SCIP_STATISTIC
1720  (*conshdlrdata)->nlbtimetable = 0;
1721  (*conshdlrdata)->nubtimetable = 0;
1722  (*conshdlrdata)->ncutofftimetable = 0;
1723  (*conshdlrdata)->nlbedgefinder = 0;
1724  (*conshdlrdata)->nubedgefinder = 0;
1725  (*conshdlrdata)->ncutoffedgefinder = 0;
1726  (*conshdlrdata)->ncutoffoverload = 0;
1727  (*conshdlrdata)->ncutoffoverloadTTEF = 0;
1728 
1729  (*conshdlrdata)->nirrelevantjobs = 0;
1730  (*conshdlrdata)->nalwaysruns = 0;
1731  (*conshdlrdata)->nremovedlocks = 0;
1732  (*conshdlrdata)->ndualfixs = 0;
1733  (*conshdlrdata)->ndecomps = 0;
1734  (*conshdlrdata)->ndualbranchs = 0;
1735  (*conshdlrdata)->nallconsdualfixs = 0;
1736  (*conshdlrdata)->naddedvarbounds = 0;
1737  (*conshdlrdata)->naddeddisjunctives = 0;
1738 #endif
1739 
1740  return SCIP_OKAY;
1741 }
1742 
1743 /** frees constraint handler data for logic or constraint handler */
1744 static
1746  SCIP* scip, /**< SCIP data structure */
1747  SCIP_CONSHDLRDATA** conshdlrdata /**< pointer to the constraint handler data */
1748  )
1749 {
1750  assert(conshdlrdata != NULL);
1751  assert(*conshdlrdata != NULL);
1752 
1753  SCIPfreeMemory(scip, conshdlrdata);
1754 }
1755 
1756 /**@} */
1757 
1758 
1759 /**@name Constraint data methods
1760  *
1761  * @{
1762  */
1763 
1764 /** catches bound change events for all variables in transformed cumulative constraint */
1765 static
1767  SCIP* scip, /**< SCIP data structure */
1768  SCIP_CONSDATA* consdata, /**< cumulative constraint data */
1769  SCIP_EVENTHDLR* eventhdlr /**< event handler to call for the event processing */
1770  )
1771 {
1772  int v;
1773 
1774  assert(scip != NULL);
1775  assert(consdata != NULL);
1776  assert(eventhdlr != NULL);
1777 
1778  /* catch event for every single variable */
1779  for( v = 0; v < consdata->nvars; ++v )
1780  {
1781  SCIP_CALL( SCIPcatchVarEvent(scip, consdata->vars[v],
1782  SCIP_EVENTTYPE_BOUNDTIGHTENED, eventhdlr, (SCIP_EVENTDATA*)consdata, NULL) );
1783  }
1784 
1785  return SCIP_OKAY;
1786 }
1787 
1788 /** drops events for variable at given position */
1789 static
1791  SCIP* scip, /**< SCIP data structure */
1792  SCIP_CONSDATA* consdata, /**< cumulative constraint data */
1793  SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */
1794  int pos /**< array position of variable to catch bound change events for */
1795  )
1796 {
1797  assert(scip != NULL);
1798  assert(consdata != NULL);
1799  assert(eventhdlr != NULL);
1800  assert(0 <= pos && pos < consdata->nvars);
1801  assert(consdata->vars[pos] != NULL);
1802 
1803  SCIP_CALL( SCIPdropVarEvent(scip, consdata->vars[pos],
1804  SCIP_EVENTTYPE_BOUNDTIGHTENED, eventhdlr, (SCIP_EVENTDATA*)consdata, -1) );
1805 
1806  return SCIP_OKAY;
1807 }
1808 
1809 /** drops bound change events for all variables in transformed linear constraint */
1810 static
1812  SCIP* scip, /**< SCIP data structure */
1813  SCIP_CONSDATA* consdata, /**< linear constraint data */
1814  SCIP_EVENTHDLR* eventhdlr /**< event handler to call for the event processing */
1815  )
1816 {
1817  int v;
1818 
1819  assert(scip != NULL);
1820  assert(consdata != NULL);
1821 
1822  /* drop event of every single variable */
1823  for( v = 0; v < consdata->nvars; ++v )
1824  {
1825  SCIP_CALL( consdataDropEvents(scip, consdata, eventhdlr, v) );
1826  }
1827 
1828  return SCIP_OKAY;
1829 }
1830 
1831 /** initialize variable lock data structure */
1832 static
1834  SCIP_CONSDATA* consdata, /**< constraint data */
1835  SCIP_Bool locked /**< should the variable be locked? */
1836  )
1837 {
1838  int nvars;
1839  int v;
1840 
1841  nvars = consdata->nvars;
1842 
1843  /* initialize locking arrays */
1844  for( v = 0; v < nvars; ++v )
1845  {
1846  consdata->downlocks[v] = locked;
1847  consdata->uplocks[v] = locked;
1848  }
1849 }
1850 
1851 /** creates constraint data of cumulative constraint */
1852 static
1854  SCIP* scip, /**< SCIP data structure */
1855  SCIP_CONSDATA** consdata, /**< pointer to consdata */
1856  SCIP_VAR** vars, /**< array of integer variables */
1857  SCIP_CONS** linkingconss, /**< array of linking constraints for the integer variables, or NULL */
1858  int* durations, /**< array containing corresponding durations */
1859  int* demands, /**< array containing corresponding demands */
1860  int nvars, /**< number of variables */
1861  int capacity, /**< available cumulative capacity */
1862  int hmin, /**< left bound of time axis to be considered (including hmin) */
1863  int hmax, /**< right bound of time axis to be considered (not including hmax) */
1864  SCIP_Bool check /**< is the corresponding constraint a check constraint */
1865  )
1866 {
1867  int v;
1868 
1869  assert(scip != NULL);
1870  assert(consdata != NULL);
1871  assert(vars != NULL || nvars > 0);
1872  assert(demands != NULL);
1873  assert(durations != NULL);
1874  assert(capacity >= 0);
1875  assert(hmin >= 0);
1876  assert(hmin < hmax);
1877 
1878  /* create constraint data */
1879  SCIP_CALL( SCIPallocBlockMemory(scip, consdata) );
1880 
1881  (*consdata)->hmin = hmin;
1882  (*consdata)->hmax = hmax;
1883 
1884  (*consdata)->capacity = capacity;
1885  (*consdata)->demandrows = NULL;
1886  (*consdata)->demandrowssize = 0;
1887  (*consdata)->ndemandrows = 0;
1888  (*consdata)->scoverrows = NULL;
1889  (*consdata)->nscoverrows = 0;
1890  (*consdata)->scoverrowssize = 0;
1891  (*consdata)->bcoverrows = NULL;
1892  (*consdata)->nbcoverrows = 0;
1893  (*consdata)->bcoverrowssize = 0;
1894  (*consdata)->nvars = nvars;
1895  (*consdata)->varssize = nvars;
1896  (*consdata)->signature = 0;
1897  (*consdata)->validsignature = FALSE;
1898  (*consdata)->normalized = FALSE;
1899  (*consdata)->covercuts = FALSE;
1900  (*consdata)->propagated = FALSE;
1901  (*consdata)->varbounds = FALSE;
1902  (*consdata)->triedsolving = FALSE;
1903 
1904  if( nvars > 0 )
1905  {
1906  assert(vars != NULL); /* for flexelint */
1907 
1908  SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*consdata)->vars, vars, nvars) );
1909  SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*consdata)->demands, demands, nvars) );
1910  SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*consdata)->durations, durations, nvars) );
1911  (*consdata)->linkingconss = NULL;
1912 
1913  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(*consdata)->downlocks, nvars) );
1914  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(*consdata)->uplocks, nvars) );
1915 
1916  /* initialize variable lock data structure; the locks are only used if the contraint is a check constraint */
1917  initializeLocks(*consdata, check);
1918 
1919  if( linkingconss != NULL )
1920  {
1921  SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*consdata)->linkingconss, linkingconss, nvars) );
1922  }
1923 
1924  /* transform variables, if they are not yet transformed */
1925  if( SCIPisTransformed(scip) )
1926  {
1927  SCIPdebugMessage("get tranformed variables and constraints\n");
1928 
1929  /* get transformed variables and do NOT captures these */
1930  SCIP_CALL( SCIPgetTransformedVars(scip, (*consdata)->nvars, (*consdata)->vars, (*consdata)->vars) );
1931 
1932  /* multi-aggregated variables cannot be replaced by active variable; therefore we mark all variables for not
1933  * been multi-aggregated
1934  */
1935  for( v = 0; v < nvars; ++v )
1936  {
1937  SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, (*consdata)->vars[v]) );
1938  }
1939 
1940  if( linkingconss != NULL )
1941  {
1942  /* get transformed constraints and captures these */
1943  SCIP_CALL( SCIPtransformConss(scip, (*consdata)->nvars, (*consdata)->linkingconss, (*consdata)->linkingconss) );
1944 
1945  for( v = 0; v < nvars; ++v )
1946  assert(SCIPgetConsLinking(scip, (*consdata)->vars[v]) == (*consdata)->linkingconss[v]);
1947  }
1948  }
1949  }
1950  else
1951  {
1952  (*consdata)->vars = NULL;
1953  (*consdata)->downlocks = NULL;
1954  (*consdata)->uplocks = NULL;
1955  (*consdata)->demands = NULL;
1956  (*consdata)->durations = NULL;
1957  (*consdata)->linkingconss = NULL;
1958  }
1959 
1960  /* initialize values for running propagation algorithms efficiently */
1961  (*consdata)->resstrength1 = -1.0;
1962  (*consdata)->resstrength2 = -1.0;
1963  (*consdata)->cumfactor1 = -1.0;
1964  (*consdata)->disjfactor1 = -1.0;
1965  (*consdata)->disjfactor2 = -1.0;
1966  (*consdata)->estimatedstrength = -1.0;
1967 
1968  SCIPstatistic( (*consdata)->maxpeak = -1 );
1969 
1970  return SCIP_OKAY;
1971 }
1972 
1973 /** releases LP rows of constraint data and frees rows array */
1974 static
1976  SCIP* scip, /**< SCIP data structure */
1977  SCIP_CONSDATA** consdata /**< constraint data */
1978  )
1979 {
1980  int r;
1981 
1982  assert(consdata != NULL);
1983  assert(*consdata != NULL);
1984 
1985  for( r = 0; r < (*consdata)->ndemandrows; ++r )
1986  {
1987  assert((*consdata)->demandrows[r] != NULL);
1988  SCIP_CALL( SCIPreleaseRow(scip, &(*consdata)->demandrows[r]) );
1989  }
1990 
1991  SCIPfreeBlockMemoryArrayNull(scip, &(*consdata)->demandrows, (*consdata)->demandrowssize);
1992 
1993  (*consdata)->ndemandrows = 0;
1994  (*consdata)->demandrowssize = 0;
1995 
1996  /* free rows of cover cuts */
1997  for( r = 0; r < (*consdata)->nscoverrows; ++r )
1998  {
1999  assert((*consdata)->scoverrows[r] != NULL);
2000  SCIP_CALL( SCIPreleaseRow(scip, &(*consdata)->scoverrows[r]) );
2001  }
2002 
2003  SCIPfreeBlockMemoryArrayNull(scip, &(*consdata)->scoverrows, (*consdata)->scoverrowssize);
2004 
2005  (*consdata)->nscoverrows = 0;
2006  (*consdata)->scoverrowssize = 0;
2007 
2008  for( r = 0; r < (*consdata)->nbcoverrows; ++r )
2009  {
2010  assert((*consdata)->bcoverrows[r] != NULL);
2011  SCIP_CALL( SCIPreleaseRow(scip, &(*consdata)->bcoverrows[r]) );
2012  }
2013 
2014  SCIPfreeBlockMemoryArrayNull(scip, &(*consdata)->bcoverrows, (*consdata)->bcoverrowssize);
2015 
2016  (*consdata)->nbcoverrows = 0;
2017  (*consdata)->bcoverrowssize = 0;
2018 
2019  (*consdata)->covercuts = FALSE;
2020 
2021  return SCIP_OKAY;
2022 }
2023 
2024 /** frees a cumulative constraint data */
2025 static
2027  SCIP* scip, /**< SCIP data structure */
2028  SCIP_CONSDATA** consdata /**< pointer to linear constraint data */
2029  )
2030 {
2031  int varssize;
2032  int nvars;
2033 
2034  assert(consdata != NULL);
2035  assert(*consdata != NULL);
2036 
2037  nvars = (*consdata)->nvars;
2038  varssize = (*consdata)->varssize;
2039 
2040  if( varssize > 0 )
2041  {
2042  int v;
2043 
2044  /* release and free the rows */
2045  SCIP_CALL( consdataFreeRows(scip, consdata) );
2046 
2047  /* release the linking constraints if they were generated */
2048  if( (*consdata)->linkingconss != NULL )
2049  {
2050  for( v = nvars-1; v >= 0; --v )
2051  {
2052  assert((*consdata)->linkingconss[v] != NULL );
2053  SCIP_CALL( SCIPreleaseCons(scip, &(*consdata)->linkingconss[v]) );
2054  }
2055 
2056  SCIPfreeBlockMemoryArray(scip, &(*consdata)->linkingconss, varssize);
2057  }
2058 
2059  /* free arrays */
2060  SCIPfreeBlockMemoryArray(scip, &(*consdata)->downlocks, varssize);
2061  SCIPfreeBlockMemoryArray(scip, &(*consdata)->uplocks, varssize);
2062  SCIPfreeBlockMemoryArray(scip, &(*consdata)->durations, varssize);
2063  SCIPfreeBlockMemoryArray(scip, &(*consdata)->demands, varssize);
2064  SCIPfreeBlockMemoryArray(scip, &(*consdata)->vars, varssize);
2065  }
2066 
2067  /* free memory */
2068  SCIPfreeBlockMemory(scip, consdata);
2069 
2070  return SCIP_OKAY;
2071 }
2072 
2073 /** prints cumulative constraint to file stream */
2074 static
2076  SCIP* scip, /**< SCIP data structure */
2077  SCIP_CONSDATA* consdata, /**< cumulative constraint data */
2078  FILE* file /**< output file (or NULL for standard output) */
2079  )
2080 {
2081  int v;
2082 
2083  assert(consdata != NULL);
2084 
2085  /* print coefficients */
2086  SCIPinfoMessage( scip, file, "cumulative(");
2087 
2088  for( v = 0; v < consdata->nvars; ++v )
2089  {
2090  assert(consdata->vars[v] != NULL);
2091  if( v > 0 )
2092  SCIPinfoMessage(scip, file, ", ");
2093  SCIPinfoMessage(scip, file, "<%s>[%g,%g](%d)[%d]", SCIPvarGetName(consdata->vars[v]),
2094  SCIPvarGetLbGlobal(consdata->vars[v]), SCIPvarGetUbGlobal(consdata->vars[v]),
2095  consdata->durations[v], consdata->demands[v]);
2096  }
2097  SCIPinfoMessage(scip, file, ")[%d,%d) <= %d", consdata->hmin, consdata->hmax, consdata->capacity);
2098 }
2099 
2100 /** deletes coefficient at given position from constraint data */
2101 static
2103  SCIP* scip, /**< SCIP data structure */
2104  SCIP_CONSDATA* consdata, /**< cumulative constraint data */
2105  SCIP_CONS* cons, /**< knapsack constraint */
2106  int pos /**< position of coefficient to delete */
2107  )
2108 {
2109  SCIP_CONSHDLR* conshdlr;
2110  SCIP_CONSHDLRDATA* conshdlrdata;
2111 
2112  assert(scip != NULL);
2113  assert(consdata != NULL);
2114  assert(cons != NULL);
2115  assert(SCIPconsIsTransformed(cons));
2116  assert(!SCIPinProbing(scip));
2117 
2118  SCIPdebugMessage("cumulative constraint <%s>: remove variable <%s>\n",
2119  SCIPconsGetName(cons), SCIPvarGetName(consdata->vars[pos]));
2120 
2121  /* remove the rounding locks for the deleted variable */
2122  SCIP_CALL( SCIPunlockVarCons(scip, consdata->vars[pos], cons, consdata->downlocks[pos], consdata->uplocks[pos]) );
2123 
2124  consdata->downlocks[pos] = FALSE;
2125  consdata->uplocks[pos] = FALSE;
2126 
2127  if( consdata->linkingconss != NULL )
2128  {
2129  SCIP_CALL( SCIPreleaseCons(scip, &consdata->linkingconss[pos]) );
2130  }
2131 
2132  /* get event handler */
2133  conshdlr = SCIPconsGetHdlr(cons);
2134  assert(conshdlr != NULL);
2135  conshdlrdata = SCIPconshdlrGetData(conshdlr);
2136  assert(conshdlrdata != NULL);
2137  assert(conshdlrdata->eventhdlr != NULL);
2138 
2139  /* drop events */
2140  SCIP_CALL( consdataDropEvents(scip, consdata, conshdlrdata->eventhdlr, pos) );
2141 
2142  SCIPdebugMessage("remove variable <%s>[%g,%g] from cumulative constraint <%s>\n",
2143  SCIPvarGetName(consdata->vars[pos]), SCIPvarGetLbGlobal(consdata->vars[pos]), SCIPvarGetUbGlobal(consdata->vars[pos]), SCIPconsGetName(cons));
2144 
2145 
2146  /* in case the we did not remove the variable in the last slot of the arrays we move the current last to this
2147  * position
2148  */
2149  if( pos != consdata->nvars - 1 )
2150  {
2151  consdata->vars[pos] = consdata->vars[consdata->nvars-1];
2152  consdata->downlocks[pos] = consdata->downlocks[consdata->nvars-1];
2153  consdata->uplocks[pos] = consdata->uplocks[consdata->nvars-1];
2154  consdata->demands[pos] = consdata->demands[consdata->nvars-1];
2155  consdata->durations[pos] = consdata->durations[consdata->nvars-1];
2156 
2157  if( consdata->linkingconss != NULL )
2158  {
2159  consdata->linkingconss[pos]= consdata->linkingconss[consdata->nvars-1];
2160  }
2161  }
2162 
2163  consdata->nvars--;
2164  consdata->validsignature = FALSE;
2165  consdata->normalized = FALSE;
2166 
2167  return SCIP_OKAY;
2168 }
2169 
2170 /** collect linking constraints for each integer variable */
2171 static
2173  SCIP* scip, /**< SCIP data structure */
2174  SCIP_CONSDATA* consdata /**< pointer to consdata */
2175  )
2176 {
2177  int nvars;
2178  int v;
2179 
2180  assert(scip != NULL);
2181  assert(consdata != NULL);
2182 
2183  nvars = consdata->nvars;
2184  assert(nvars > 0);
2185  assert(consdata->linkingconss == NULL);
2186 
2187  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->linkingconss, consdata->varssize) );
2188 
2189  for( v = 0; v < nvars; ++v )
2190  {
2191  SCIP_CONS* cons;
2192  SCIP_VAR* var;
2193 
2194  var = consdata->vars[v];
2195  assert(var != NULL);
2196 
2197  SCIPdebugMessage("linking constraint (%d of %d) for variable <%s>\n", v+1, nvars, SCIPvarGetName(var));
2198 
2199  /* create linking constraint if it does not exist yet */
2200  if( !SCIPexistsConsLinking(scip, var) )
2201  {
2202  char name[SCIP_MAXSTRLEN];
2203 
2204  (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "link(%s)", SCIPvarGetName(var));
2205 
2206  /* creates and captures an linking constraint */
2207  SCIP_CALL( SCIPcreateConsLinking(scip, &cons, name, var, NULL, 0, 0,
2208  TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE /*TRUE*/, FALSE) );
2209  SCIP_CALL( SCIPaddCons(scip, cons) );
2210  consdata->linkingconss[v] = cons;
2211  }
2212  else
2213  {
2214  consdata->linkingconss[v] = SCIPgetConsLinking(scip, var);
2215  SCIP_CALL( SCIPcaptureCons(scip, consdata->linkingconss[v]) );
2216  }
2217 
2218  assert(SCIPexistsConsLinking(scip, var));
2219  assert(consdata->linkingconss[v] != NULL);
2220  assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(consdata->linkingconss[v])), "linking") == 0 );
2221  assert(SCIPgetConsLinking(scip, var) == consdata->linkingconss[v]);
2222  }
2223 
2224  return SCIP_OKAY;
2225 }
2226 
2227 /**@} */
2228 
2229 
2230 /**@name Check methods
2231  *
2232  * @{
2233  */
2234 
2235 /** check for the given starting time variables with their demands and durations if the cumulative conditions for the
2236  * given solution is satisfied
2237  */
2238 static
2240  SCIP* scip, /**< SCIP data structure */
2241  SCIP_SOL* sol, /**< primal solution, or NULL for current LP/pseudo solution */
2242  int nvars, /**< number of variables (jobs) */
2243  SCIP_VAR** vars, /**< array of integer variable which corresponds to starting times for a job */
2244  int* durations, /**< array containing corresponding durations */
2245  int* demands, /**< array containing corresponding demands */
2246  int capacity, /**< available cumulative capacity */
2247  int hmin, /**< left bound of time axis to be considered (including hmin) */
2248  int hmax, /**< right bound of time axis to be considered (not including hmax) */
2249  SCIP_Bool* violated, /**< pointer to store if the cumulative condition is violated */
2250  SCIP_CONS* cons, /**< constraint which is checked */
2251  SCIP_Bool printreason /**< should the reason for the violation be printed? */
2252  )
2253 {
2254  int* startsolvalues; /* stores when each job is starting */
2255  int* endsolvalues; /* stores when each job ends */
2256  int* startindices; /* we will sort the startsolvalues, thus we need to know which index of a job it corresponds to */
2257  int* endindices; /* we will sort the endsolvalues, thus we need to know which index of a job it corresponds to */
2258 
2259  int freecapacity;
2260  int curtime; /* point in time which we are just checking */
2261  int endindex; /* index of endsolvalues with: endsolvalues[endindex] > curtime */
2262  int j;
2263 
2264  assert(scip != NULL);
2265  assert(violated != NULL);
2266 
2267  (*violated) = FALSE;
2268 
2269  if( nvars == 0 )
2270  return SCIP_OKAY;
2271 
2272  assert(vars != NULL);
2273  assert(demands != NULL);
2274  assert(durations != NULL);
2275 
2276  /* compute time points where we have to check whether capacity constraint is infeasible or not */
2277  SCIP_CALL( SCIPallocBufferArray(scip, &startsolvalues, nvars) );
2278  SCIP_CALL( SCIPallocBufferArray(scip, &endsolvalues, nvars) );
2279  SCIP_CALL( SCIPallocBufferArray(scip, &startindices, nvars) );
2280  SCIP_CALL( SCIPallocBufferArray(scip, &endindices, nvars) );
2281 
2282  /* assign variables, start and endpoints to arrays */
2283  for ( j = 0; j < nvars; ++j )
2284  {
2285  int solvalue;
2286 
2287  /* the constraint of the cumulative constraint handler should be called after the integrality check */
2288  assert(SCIPisFeasIntegral(scip, SCIPgetSolVal(scip, sol, vars[j])));
2289 
2290  solvalue = convertBoundToInt(scip, SCIPgetSolVal(scip, sol, vars[j]));
2291 
2292  /* we need to ensure that we check at least one time point during the effective horizon; therefore we project all
2293  * jobs which start before hmin to hmin
2294  */
2295  startsolvalues[j] = MAX(solvalue, hmin);
2296  startindices[j] = j;
2297 
2298  endsolvalues[j] = MAX(solvalue + durations[j], hmin);
2299  endindices[j] = j;
2300  }
2301 
2302  /* sort the arrays not-decreasing according to start solution values and end solution values (and sort the
2303  * corresponding indices in the same way)
2304  */
2305  SCIPsortIntInt(startsolvalues, startindices, nvars);
2306  SCIPsortIntInt(endsolvalues, endindices, nvars);
2307 
2308  endindex = 0;
2309  freecapacity = capacity;
2310 
2311  /* check each start point of a job whether the capacity is kept or not */
2312  for( j = 0; j < nvars; ++j )
2313  {
2314  /* only check intervals [hmin,hmax) */
2315  curtime = startsolvalues[j];
2316 
2317  if( curtime >= hmax )
2318  break;
2319 
2320  /* subtract all capacity needed up to this point */
2321  freecapacity -= demands[startindices[j]];
2322  while( j+1 < nvars && startsolvalues[j+1] == curtime )
2323  {
2324  j++;
2325  freecapacity -= demands[startindices[j]];
2326  }
2327 
2328  /* free all capacity usages of jobs that are no longer running */
2329  while( endindex < nvars && curtime >= endsolvalues[endindex] )
2330  {
2331  freecapacity += demands[endindices[endindex]];
2332  ++endindex;
2333  }
2334  assert(freecapacity <= capacity);
2335 
2336  /* check freecapacity to be smaller than zero */
2337  if( freecapacity < 0 && curtime >= hmin )
2338  {
2339  SCIPdebugMessage("freecapacity = %3d\n", freecapacity);
2340  (*violated) = TRUE;
2341 
2342  if( printreason )
2343  {
2344  int i;
2345 
2346  /* first state the violated constraints */
2347  SCIP_CALL( SCIPprintCons(scip, cons, NULL) );
2348 
2349  /* second state the reason */
2350  SCIPinfoMessage(scip, NULL,
2351  ";\nviolation: at time point %d available capacity = %d, needed capacity = %d\n",
2352  curtime, capacity, capacity - freecapacity);
2353 
2354  for( i = 0; i <= j; ++i )
2355  {
2356  if( startsolvalues[i] + durations[startindices[i]] > curtime )
2357  {
2358  SCIPinfoMessage(scip, NULL, "activity %s, start = %i, duration = %d, demand = %d \n",
2359  SCIPvarGetName(vars[startindices[i]]), startsolvalues[i], durations[startindices[i]],
2360  demands[startindices[i]]);
2361  }
2362  }
2363  }
2364  break;
2365  }
2366  } /*lint --e{850}*/
2367 
2368  /* free all buffer arrays */
2369  SCIPfreeBufferArray(scip, &endindices);
2370  SCIPfreeBufferArray(scip, &startindices);
2371  SCIPfreeBufferArray(scip, &endsolvalues);
2372  SCIPfreeBufferArray(scip, &startsolvalues);
2373 
2374  return SCIP_OKAY;
2375 }
2376 
2377 /** check if the given constrait is valid; checks each starting point of a job whether the remaining capacity is at
2378  * least zero or not. If not (*violated) is set to TRUE
2379  */
2380 static
2382  SCIP* scip, /**< SCIP data structure */
2383  SCIP_CONS* cons, /**< constraint to be checked */
2384  SCIP_SOL* sol, /**< primal solution, or NULL for current LP/pseudo solution */
2385  SCIP_Bool* violated, /**< pointer to store if the constraint is violated */
2386  SCIP_Bool printreason /**< should the reason for the violation be printed? */
2387  )
2388 {
2389  SCIP_CONSDATA* consdata;
2390 
2391  assert(scip != NULL);
2392  assert(cons != NULL);
2393  assert(violated != NULL);
2394 
2395  SCIPdebugMessage("check cumulative constraints <%s>\n", SCIPconsGetName(cons));
2396 
2397  consdata = SCIPconsGetData(cons);
2398  assert(consdata != NULL);
2399 
2400  /* check the cumulative condition */
2401  SCIP_CALL( checkCumulativeCondition(scip, sol, consdata->nvars, consdata->vars,
2402  consdata->durations, consdata->demands, consdata->capacity, consdata->hmin, consdata->hmax,
2403  violated, cons, printreason) );
2404 
2405  return SCIP_OKAY;
2406 }
2407 
2408 /**@} */
2409 
2410 /**@name Conflict analysis
2411  *
2412  * @{
2413  */
2414 
2415 /** resolves the propagation of the core time algorithm */
2416 static
2418  SCIP* scip, /**< SCIP data structure */
2419  int nvars, /**< number of start time variables (activities) */
2420  SCIP_VAR** vars, /**< array of start time variables */
2421  int* durations, /**< array of durations */
2422  int* demands, /**< array of demands */
2423  int capacity, /**< cumulative capacity */
2424  int hmin, /**< left bound of time axis to be considered (including hmin) */
2425  int hmax, /**< right bound of time axis to be considered (not including hmax) */
2426  SCIP_VAR* infervar, /**< inference variable */
2427  int inferdemand, /**< demand of the inference variable */
2428  int inferpeak, /**< time point which causes the propagation */
2429  int relaxedpeak, /**< relaxed time point which would be sufficient to be proved */
2430  SCIP_BDCHGIDX* bdchgidx, /**< the index of the bound change, representing the point of time where the change took place */
2431  SCIP_Bool usebdwidening, /**< should bound widening be used during conflict analysis? */
2432  int* provedpeak, /**< pointer to store the actually proved peak, or NULL */
2433  SCIP_Bool* explanation /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */
2434  )
2435 {
2436  SCIP_VAR* var;
2437  SCIP_Bool* reported;
2438  int duration;
2439  int maxlst;
2440  int minect;
2441  int ect;
2442  int lst;
2443  int j;
2444 
2445  assert(SCIPgetStage(scip) == SCIP_STAGE_SOLVING || SCIPinProbing(scip));
2446 
2447  SCIPdebugMessage("variable <%s>: (demand %d) resolve propagation of core time algorithm (peak %d)\n",
2448  SCIPvarGetName(infervar), inferdemand, inferpeak);
2449  assert(nvars > 0);
2450 
2451  /* adjusted capacity */
2452  capacity -= inferdemand;
2453  maxlst = INT_MIN;
2454  minect = INT_MAX;
2455 
2456  SCIP_CALL( SCIPallocBufferArray(scip, &reported, nvars) );
2457  BMSclearMemoryArray(reported, nvars);
2458 
2459  /* first we loop over all variables and adjust the capacity with those jobs which provide a global core at the
2460  * inference peak and those where the current conflict bounds provide a core at the inference peak
2461  */
2462  for( j = 0; j < nvars && capacity >= 0; ++j )
2463  {
2464  var = vars[j];
2465  assert(var != NULL);
2466 
2467  /* skip inference variable */
2468  if( var == infervar )
2469  continue;
2470 
2471  duration = durations[j];
2472  assert(duration > 0);
2473 
2474  /* compute cores of jobs; if core overlaps interval of inference variable add this job to the array */
2475  assert(SCIPisFeasEQ(scip, SCIPvarGetUbAtIndex(var, bdchgidx, TRUE), SCIPvarGetUbAtIndex(var, bdchgidx, FALSE)));
2476  assert(SCIPisFeasIntegral(scip, SCIPvarGetUbAtIndex(var, bdchgidx, TRUE)));
2477  assert(SCIPisFeasEQ(scip, SCIPvarGetLbAtIndex(var, bdchgidx, TRUE), SCIPvarGetLbAtIndex(var, bdchgidx, FALSE)));
2478  assert(SCIPisFeasIntegral(scip, SCIPvarGetLbAtIndex(var, bdchgidx, TRUE)));
2479 
2480  SCIPdebugMessage("variable <%s>: glb=[%g,%g] conflict=[%g,%g] (duration %d, demand %d)\n",
2482  SCIPgetConflictVarLb(scip, var), SCIPgetConflictVarUb(scip, var), duration, demands[j]);
2483 
2484  ect = convertBoundToInt(scip, SCIPvarGetLbGlobal(var)) + duration;
2485  lst = convertBoundToInt(scip, SCIPvarGetUbGlobal(var));
2486 
2487  /* check if the inference peak is part of the global bound core; if so we decreasing the capacity by the demand of
2488  * that job without adding it the explanation
2489  */
2490  if( inferpeak < ect && lst <= inferpeak )
2491  {
2492  capacity -= demands[j];
2493  reported[j] = TRUE;
2494 
2495  maxlst = MAX(maxlst, lst);
2496  minect = MIN(minect, ect);
2497  assert(maxlst < minect);
2498 
2499  if( explanation != NULL )
2500  explanation[j] = TRUE;
2501 
2502  continue;
2503  }
2504 
2505  /* collect the conflict bound core (the conflict bounds are those bounds which are already part of the conflict)
2506  * hence these bound are already reported by other resolve propation steps. In case a bound (lower or upper) is
2507  * not part of the conflict yet we get the global bounds back.
2508  */
2509  ect = convertBoundToInt(scip, SCIPgetConflictVarLb(scip, var)) + duration;
2510  lst = convertBoundToInt(scip, SCIPgetConflictVarUb(scip, var));
2511 
2512  /* check if the inference peak is part of the conflict bound core; if so we decreasing the capacity by the demand
2513  * of that job without and collect the job as part of the explanation
2514  *
2515  * @note we do not need to reported that job to SCIP since the required bounds are already reported
2516  */
2517  if( inferpeak < ect && lst <= inferpeak )
2518  {
2519  capacity -= demands[j];
2520  reported[j] = TRUE;
2521 
2522  maxlst = MAX(maxlst, lst);
2523  minect = MIN(minect, ect);
2524  assert(maxlst < minect);
2525 
2526  if( explanation != NULL )
2527  explanation[j] = TRUE;
2528  }
2529  }
2530 
2531  if( capacity >= 0 )
2532  {
2533  int* cands;
2534  int* canddemands;
2535  int ncands;
2536  int c;
2537 
2538  SCIP_CALL( SCIPallocBufferArray(scip, &cands, nvars) );
2539  SCIP_CALL( SCIPallocBufferArray(scip, &canddemands, nvars) );
2540  ncands = 0;
2541 
2542  /* collect all cores of the variables which lay in the considered time window except the inference variable */
2543  for( j = 0; j < nvars; ++j )
2544  {
2545  var = vars[j];
2546  assert(var != NULL);
2547 
2548  /* skip inference variable */
2549  if( var == infervar || reported[j] )
2550  continue;
2551 
2552  duration = durations[j];
2553  assert(duration > 0);
2554 
2555  /* compute cores of jobs; if core overlaps interval of inference variable add this job to the array */
2556  assert(SCIPisFeasEQ(scip, SCIPvarGetUbAtIndex(var, bdchgidx, TRUE), SCIPvarGetUbAtIndex(var, bdchgidx, FALSE)));
2557  assert(SCIPisFeasIntegral(scip, SCIPvarGetUbAtIndex(var, bdchgidx, TRUE)));
2558  assert(SCIPisFeasEQ(scip, SCIPvarGetLbAtIndex(var, bdchgidx, TRUE), SCIPvarGetLbAtIndex(var, bdchgidx, FALSE)));
2559  assert(SCIPisFeasIntegral(scip, SCIPvarGetLbAtIndex(var, bdchgidx, TRUE)));
2560 
2561  /* collect local core information */
2562  ect = convertBoundToInt(scip, SCIPvarGetLbAtIndex(var, bdchgidx, FALSE)) + duration;
2563  lst = convertBoundToInt(scip, SCIPvarGetUbAtIndex(var, bdchgidx, FALSE));
2564 
2565  SCIPdebugMessage("variable <%s>: loc=[%g,%g] glb=[%g,%g] (duration %d, demand %d)\n",
2566  SCIPvarGetName(var), SCIPvarGetLbAtIndex(var, bdchgidx, FALSE), SCIPvarGetUbAtIndex(var, bdchgidx, FALSE),
2567  SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var), duration, demands[j]);
2568 
2569  /* check if the inference peak is part of the core */
2570  if( inferpeak < ect && lst <= inferpeak )
2571  {
2572  cands[ncands] = j;
2573  canddemands[ncands] = demands[j];
2574  ncands++;
2575 
2576  capacity -= demands[j];
2577  }
2578  }
2579 
2580  /* sort candidates indices w.r.t. their demands */
2581  SCIPsortDownIntInt(canddemands, cands, ncands);
2582 
2583  assert(capacity < 0);
2584  assert(ncands > 0);
2585 
2586  /* greedily remove candidates form the list such that the needed capacity is still exceeded */
2587  while( capacity + canddemands[ncands-1] < 0 )
2588  {
2589  ncands--;
2590  capacity += canddemands[ncands];
2591  assert(ncands > 0);
2592  }
2593 
2594  /* compute the size (number of time steps) of the job cores */
2595  for( c = 0; c < ncands; ++c )
2596  {
2597  var = vars[cands[c]];
2598  assert(var != NULL);
2599 
2600  duration = durations[cands[c]];
2601 
2602  ect = convertBoundToInt(scip, SCIPvarGetLbAtIndex(var, bdchgidx, FALSE)) + duration;
2603  lst = convertBoundToInt(scip, SCIPvarGetUbAtIndex(var, bdchgidx, FALSE));
2604 
2605  maxlst = MAX(maxlst, lst);
2606  minect = MIN(minect, ect);
2607  assert(maxlst < minect);
2608  }
2609 
2610  SCIPdebugMessage("infer peak %d, relaxed peak %d, lst %d, ect %d\n", inferpeak, relaxedpeak, maxlst, minect);
2611  assert(inferpeak >= maxlst);
2612  assert(inferpeak < minect);
2613 
2614  /* check if the collect variable are sufficient to prove the relaxed bound (relaxedpeak) */
2615  if( relaxedpeak < inferpeak )
2616  {
2617  inferpeak = MAX(maxlst, relaxedpeak);
2618  }
2619  else if( relaxedpeak > inferpeak )
2620  {
2621  inferpeak = MIN(minect-1, relaxedpeak);
2622  }
2623  assert(inferpeak >= hmin);
2624  assert(inferpeak < hmax);
2625  assert(inferpeak >= maxlst);
2626  assert(inferpeak < minect);
2627 
2628  /* post all necessary bound changes */
2629  for( c = 0; c < ncands; ++c )
2630  {
2631  var = vars[cands[c]];
2632  assert(var != NULL);
2633 
2634  if( usebdwidening )
2635  {
2636  duration = durations[cands[c]];
2637 
2638  SCIP_CALL( SCIPaddConflictRelaxedLb(scip, var, bdchgidx, (SCIP_Real)(inferpeak - duration + 1)) );
2639  SCIP_CALL( SCIPaddConflictRelaxedUb(scip, var, bdchgidx, (SCIP_Real)inferpeak) );
2640  }
2641  else
2642  {
2643  SCIP_CALL( SCIPaddConflictLb(scip, var, bdchgidx) );
2644  SCIP_CALL( SCIPaddConflictUb(scip, var, bdchgidx) );
2645  }
2646 
2647  if( explanation != NULL )
2648  explanation[cands[c]] = TRUE;
2649  }
2650 
2651  SCIPfreeBufferArray(scip, &canddemands);
2652  SCIPfreeBufferArray(scip, &cands);
2653  }
2654 
2655  SCIPfreeBufferArray(scip, &reported);
2656 
2657  if( provedpeak != NULL )
2658  *provedpeak = inferpeak;
2659 
2660  return SCIP_OKAY;
2661 }
2662 
2663 #if 0
2664 /** repropagation of edge finding algorithm simplified version from Petr Vilim only a small subset is reported such that
2665  * energy in total and for bound change is enough
2666  */
2667 static
2668 SCIP_RETCODE resolvePropagationEdgeFinding(
2669  SCIP* scip, /**< SCIP data structure */
2670  int nvars, /**< number of start time variables (activities) */
2671  SCIP_VAR** vars, /**< array of start time variables */
2672  int* durations, /**< array of durations */
2673  int hmin, /**< left bound of time axis to be considered (including hmin) */
2674  int hmax, /**< right bound of time axis to be considered (not including hmax) */
2675  SCIP_VAR* infervar, /**< variable whose bound change is to be explained */
2676  INFERINFO inferinfo, /**< inference info containing position of correct bdchgids */
2677  SCIP_BOUNDTYPE boundtype, /**< the type of the changed bound (lower or upper bound) */
2678  SCIP_BDCHGIDX* bdchgidx, /**< the index of the bound change, representing the point of time where the change took place */
2679  SCIP_Bool usebdwidening, /**< should bound widening be used during conflict analysis? */
2680  SCIP_Bool* explanation /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */
2681  )
2682 {
2683  int est;
2684  int lct;
2685  int j;
2686 
2687  /* ???????????????????? do bound widening */
2688 
2689  assert(scip != NULL);
2690  assert(nvars > 0);
2691  assert(inferInfoGetProprule(inferinfo) == PROPRULE_2_EDGEFINDING);
2692 
2693  SCIPdebugMessage("repropagate edge-finding with short reasons for variable <%s>\n", SCIPvarGetName(infervar));
2694 
2695  if( boundtype == SCIP_BOUNDTYPE_LOWER )
2696  {
2697  SCIP_CALL( SCIPaddConflictLb(scip, infervar, bdchgidx) );
2698  }
2699  else
2700  {
2701  SCIP_CALL( SCIPaddConflictUb(scip, infervar, bdchgidx) );
2702  }
2703 
2704  est = inferInfoGetData1(inferinfo);
2705  lct = inferInfoGetData2(inferinfo);
2706  assert(est < lct);
2707 
2708  /* collect the energies of all variables in [est_omega, lct_omega] */
2709  for( j = 0; j < nvars; ++j )
2710  {
2711  SCIP_VAR* var;
2712  SCIP_Bool left;
2713  SCIP_Bool right;
2714  int duration;
2715  int lb;
2716  int ub;
2717 
2718  var = vars[j];
2719  assert(var != NULL);
2720 
2721  if( var == infervar )
2722  {
2723  if( explanation != NULL )
2724  explanation[j] = TRUE;
2725 
2726  continue;
2727  }
2728 
2729  lb = convertBoundToInt(scip, SCIPvarGetLbAtIndex(var, bdchgidx, FALSE));
2730  ub = convertBoundToInt(scip, SCIPvarGetUbAtIndex(var, bdchgidx, FALSE));
2731 
2732  duration = durations[j];
2733  assert(duration > 0);
2734 
2735  /* in case the earliest start time is equal to hmin we have to also consider the jobs which run in that region
2736  * since we use adjusted jobs during the propagation
2737  */
2738  left = (est == hmin && lb + duration > hmin) || lb >= est;
2739 
2740  /* in case the latest completion time is equal to hmax we have to also consider the jobs which run in that region
2741  * since we use adjusted jobs during the propagation
2742  */
2743  right = (lct == hmax && ub < hmax) || ub + duration <= lct;
2744 
2745  /* store all jobs running in [est_omega; lct_omega] */
2746  if( left && right )
2747  {
2748  /* check if bound widening should be used */
2749  if( usebdwidening )
2750  {
2751  SCIP_CALL( SCIPaddConflictRelaxedLb(scip, var, bdchgidx, (SCIP_Real)(lct - duration)) );
2752  SCIP_CALL( SCIPaddConflictRelaxedUb(scip, var, bdchgidx, (SCIP_Real)(est)) );
2753  }
2754  else
2755  {
2756  SCIP_CALL( SCIPaddConflictLb(scip, var, bdchgidx) );
2757  SCIP_CALL( SCIPaddConflictUb(scip, var, bdchgidx) );
2758  }
2759 
2760  if( explanation != NULL )
2761  explanation[j] = TRUE;
2762  }
2763  }
2764 
2765  return SCIP_OKAY;
2766 }
2767 #endif
2768 
2769 /** compute the minimum overlaps w.r.t. the duration of the job and the time window [begin,end) */
2770 static
2772  int begin, /**< begin of the times interval */
2773  int end, /**< end of time interval */
2774  int est, /**< earliest start time */
2775  int lst, /**< latest start time */
2776  int duration /**< duration of the job */
2777  )
2778 {
2779  int left;
2780  int right;
2781  int ect;
2782  int lct;
2783 
2784  ect = est + duration;
2785  lct = lst + duration;
2786 
2787  /* check if job runs completely within [begin,end) */
2788  if( lct <= end && est >= begin )
2789  return duration;
2790 
2791  assert(lst <= end && ect >= begin);
2792 
2793  left = ect - begin;
2794  assert(left > 0);
2795 
2796  right = end - lst;
2797  assert(right > 0);
2798 
2799  return MIN3(left, right, end - begin);
2800 }
2801 
2802 /** an overload was detected due to the time-time edge-finding propagate; initialized conflict analysis, add an initial
2803  * reason
2804  *
2805  * @note the conflict analysis is not performend, only the initialized SCIP_Bool pointer is set to TRUE
2806  */
2807 static
2809  SCIP* scip, /**< SCIP data structure */
2810  int nvars, /**< number of start time variables (activities) */
2811  SCIP_VAR** vars, /**< array of start time variables */
2812  int* durations, /**< array of durations */
2813  int* demands, /**< array of demands */
2814  int capacity, /**< capacity of the cumulative condition */
2815  int begin, /**< begin of the time window */
2816  int end, /**< end of the time window */
2817  SCIP_VAR* infervar, /**< variable which was propagate, or NULL */
2818  SCIP_BOUNDTYPE boundtype, /**< the type of the changed bound (lower or upper bound) */
2819  SCIP_BDCHGIDX* bdchgidx, /**< the index of the bound change, representing the point of time where the change took place */
2820  SCIP_Real relaxedbd, /**< the relaxed bound which is sufficient to be explained */
2821  SCIP_Bool usebdwidening, /**< should bound widening be used during conflict analysis? */
2822  SCIP_Bool* explanation /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */
2823  )
2824 {
2825  int* locenergies;
2826  int* overlaps;
2827  int* idxs;
2828 
2829  int requiredenergy;
2830  int v;
2831 
2832  SCIP_CALL( SCIPallocBufferArray(scip, &locenergies, nvars) );
2833  SCIP_CALL( SCIPallocBufferArray(scip, &overlaps, nvars) );
2834  SCIP_CALL( SCIPallocBufferArray(scip, &idxs, nvars) );
2835 
2836  /* energy which needs be explained */
2837  requiredenergy = (end - begin) * capacity;
2838 
2839  SCIPdebugMessage("analysis energy load in [%d,%d) (capacity %d, energy %d)\n", begin, end, capacity, requiredenergy);
2840 
2841  /* collect global contribution and adjusted the required energy by the amount of energy the inference variable
2842  * takes
2843  */
2844  for( v = 0; v < nvars; ++v )
2845  {
2846  SCIP_VAR* var;
2847  int glbenergy;
2848  int duration;
2849  int demand;
2850  int est;
2851  int lst;
2852 
2853  var = vars[v];
2854  assert(var != NULL);
2855 
2856  locenergies[v] = 0;
2857  overlaps[v] = 0;
2858  idxs[v] = v;
2859 
2860  demand = demands[v];
2861  assert(demand > 0);
2862 
2863  duration = durations[v];
2864  assert(duration > 0);
2865 
2866  /* check if the variable equals the inference variable (the one which was propagated) */
2867  if( infervar == var )
2868  {
2869  int overlap;
2870  int right;
2871  int left;
2872 
2873  assert(relaxedbd != SCIP_UNKNOWN); /*lint !e777*/
2874 
2875  SCIPdebugMessage("inference variable <%s>[%g,%g] %s %g (duration %d, demand %d)\n",
2876  SCIPvarGetName(var), SCIPvarGetLbAtIndex(var, bdchgidx, FALSE), SCIPvarGetUbAtIndex(var, bdchgidx, FALSE),
2877  boundtype == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=", relaxedbd, duration, demand);
2878 
2879  /* compute the amount of energy which needs to be available for enforcing the propagation and report the bound
2880  * which is necessary from the inference variable
2881  */
2882  if( boundtype == SCIP_BOUNDTYPE_UPPER )
2883  {
2884  int lct;
2885 
2886  /* get the latest start time of the infer start time variable before the propagation took place */
2887  lst = convertBoundToInt(scip, SCIPvarGetUbAtIndex(var, bdchgidx, FALSE));
2888 
2889  /* the latest start time of the inference start time variable before the propagation needs to be smaller as
2890  * the end of the time interval; meaning the job needs be overlap with the time interval in case the job is
2891  * scheduled w.r.t. its latest start time
2892  */
2893  assert(lst < end);
2894 
2895  /* compute the overlap of the job in case it would be scheduled w.r.t. its latest start time and the time
2896  * interval (before the propagation)
2897  */
2898  right = MIN3(end - lst, end - begin, duration);
2899 
2900  /* the job needs to overlap with the interval; otherwise the propagation w.r.t. this time window is not valid */
2901  assert(right > 0);
2902 
2903  lct = convertBoundToInt(scip, relaxedbd) + duration;
2904  assert(begin <= lct);
2905  assert(bdchgidx == NULL || convertBoundToInt(scip, SCIPvarGetUbAtIndex(var, bdchgidx, TRUE)) < begin);
2906 
2907  /* compute the overlap of the job after the propagation but considering the relaxed bound */
2908  left = MIN(lct - begin + 1, end - begin);
2909  assert(left > 0);
2910 
2911  /* compute the minimum overlap; */
2912  overlap = MIN(left, right);
2913  assert(overlap > 0);
2914  assert(overlap <= end - begin);
2915  assert(overlap <= duration);
2916 
2917  if( usebdwidening )
2918  {
2919  assert(convertBoundToInt(scip, SCIPvarGetUbAtIndex(var, bdchgidx, FALSE)) <= (end - overlap));
2920  SCIP_CALL( SCIPaddConflictRelaxedUb(scip, var, bdchgidx, (SCIP_Real)(end - overlap)) );
2921  }
2922  else
2923  {
2924  SCIP_CALL( SCIPaddConflictUb(scip, var, bdchgidx) );
2925  }
2926  }
2927  else
2928  {
2929  int ect;
2930 
2931  assert(boundtype == SCIP_BOUNDTYPE_LOWER);
2932 
2933  /* get the earliest completion time of the infer start time variable before the propagation took place */
2934  ect = convertBoundToInt(scip, SCIPvarGetLbAtIndex(var, bdchgidx, FALSE)) + duration;
2935 
2936  /* the earliest start time of the inference start time variable before the propagation needs to be larger as
2937  * than the beginning of the time interval; meaning the job needs be overlap with the time interval in case
2938  * the job is scheduled w.r.t. its earliest start time
2939  */
2940  assert(ect > begin);
2941 
2942  /* compute the overlap of the job in case it would be scheduled w.r.t. its earliest start time and the time
2943  * interval (before the propagation)
2944  */
2945  left = MIN3(ect - begin, end - begin, duration);
2946 
2947  /* the job needs to overlap with the interval; otherwise the propagation w.r.t. this time window is not valid */
2948  assert(left > 0);
2949 
2950  est = convertBoundToInt(scip, relaxedbd);
2951  assert(end >= est);
2952  assert(bdchgidx == NULL || end - SCIPvarGetLbAtIndex(var, bdchgidx, TRUE) < duration);
2953 
2954  /* compute the overlap of the job after the propagation but considering the relaxed bound */
2955  right = MIN(end - est + 1, end - begin);
2956  assert(right > 0);
2957 
2958  /* compute the minimum overlap */
2959  overlap = MIN(left, right);
2960  assert(overlap > 0);
2961  assert(overlap <= end - begin);
2962  assert(overlap <= duration);
2963 
2964  if( usebdwidening )
2965  {
2966  assert(convertBoundToInt(scip, SCIPvarGetLbAtIndex(var, bdchgidx, FALSE)) >= (begin + overlap - duration));
2967  SCIP_CALL( SCIPaddConflictRelaxedLb(scip, var, bdchgidx, (SCIP_Real)(begin + overlap - duration)) );
2968  }
2969  else
2970  {
2971  SCIP_CALL( SCIPaddConflictLb(scip, var, bdchgidx) );
2972  }
2973  }
2974 
2975  /* subtract the amount of energy which is available due to the overlap of the inference start time */
2976  requiredenergy -= overlap * demand;
2977 
2978  if( explanation != NULL )
2979  explanation[v] = TRUE;
2980 
2981  continue;
2982  }
2983 
2984  /* global time points */
2985  est = convertBoundToInt(scip, SCIPvarGetLbGlobal(var));
2986  lst = convertBoundToInt(scip, SCIPvarGetUbGlobal(var));
2987 
2988  glbenergy = 0;
2989 
2990  /* check if the has any overlap w.r.t. global bound; meaning some parts of the job will run for sure within the
2991  * time window
2992  */
2993  if( est + duration > begin && lst < end )
2994  {
2995  /* evaluated global contribution */
2996  glbenergy = computeOverlap(begin, end, est, lst, duration) * demand;
2997 
2998  /* remove the globally available energy form the required energy */
2999  requiredenergy -= glbenergy;
3000 
3001  if( explanation != NULL )
3002  explanation[v] = TRUE;
3003  }
3004 
3005  /* local time points */
3006  est = convertBoundToInt(scip, SCIPvarGetLbAtIndex(var, bdchgidx, FALSE));
3007  lst = convertBoundToInt(scip, SCIPvarGetUbAtIndex(var, bdchgidx, FALSE));
3008 
3009  /* check if the job has any overlap w.r.t. local bound; meaning some parts of the job will run for sure within the
3010  * time window
3011  */
3012  if( est + duration > begin && lst < end )
3013  {
3014  overlaps[v] = computeOverlap(begin, end, est, lst, duration);
3015 
3016  /* evaluated additionally local energy contribution */
3017  locenergies[v] = overlaps[v] * demand - glbenergy;
3018  assert(locenergies[v] >= 0);
3019  }
3020  }
3021 
3022  /* sort the variable contributions w.r.t. additional local energy contributions */
3023  SCIPsortDownIntIntInt(locenergies, overlaps, idxs, nvars);
3024 
3025  /* add local energy contributions until an overload is implied */
3026  for( v = 0; v < nvars && requiredenergy >= 0; ++v )
3027  {
3028  SCIP_VAR* var;
3029  int duration;
3030  int overlap;
3031  int relaxlb;
3032  int relaxub;
3033  int idx;
3034 
3035  idx = idxs[v];
3036  assert(idx >= 0 && idx < nvars);
3037 
3038  var = vars[idx];
3039  assert(var != NULL);
3040  assert(var != infervar);
3041 
3042  duration = durations[idx];
3043  assert(duration > 0);
3044 
3045  overlap = overlaps[v];
3046  assert(overlap > 0);
3047 
3048  requiredenergy -= locenergies[v];
3049 
3050  if( requiredenergy < -1 )
3051  {
3052  int demand;
3053 
3054  demand = demands[idx];
3055  assert(demand > 0);
3056 
3057  overlap += (int)((requiredenergy + 1) / demand);
3058 
3059 #ifndef NDEBUG
3060  requiredenergy += locenergies[v];
3061  requiredenergy -= overlap * demand;
3062  assert(requiredenergy < 0);
3063 #endif
3064  }
3065  assert(overlap > 0);
3066 
3067  relaxlb = begin - duration + overlap;
3068  relaxub = end - overlap;
3069 
3070  SCIPdebugMessage("variable <%s> glb=[%g,%g] loc=[%g,%g], conf=[%g,%g], added=[%d,%d] (demand %d, duration %d)\n",
3071  SCIPvarGetName(var),
3074  SCIPgetConflictVarLb(scip, var), SCIPgetConflictVarUb(scip, var),
3075  relaxlb, relaxub, demands[idx], duration);
3076 
3077  SCIP_CALL( SCIPaddConflictRelaxedLb(scip, var, bdchgidx, (SCIP_Real)relaxlb) );
3078  SCIP_CALL( SCIPaddConflictRelaxedUb(scip, var, bdchgidx, (SCIP_Real)relaxub) );
3079 
3080  if( explanation != NULL )
3081  explanation[idx] = TRUE;
3082  }
3083 
3084  assert(requiredenergy < 0);
3085 
3086  SCIPfreeBufferArray(scip, &idxs);
3087  SCIPfreeBufferArray(scip, &overlaps);
3088  SCIPfreeBufferArray(scip, &locenergies);
3089 
3090  return SCIP_OKAY;
3091 }
3092 
3093 /** resolve propagation w.r.t. the cumulative condition */
3094 static
3096  SCIP* scip, /**< SCIP data structure */
3097  int nvars, /**< number of start time variables (activities) */
3098  SCIP_VAR** vars, /**< array of start time variables */
3099  int* durations, /**< array of durations */
3100  int* demands, /**< array of demands */
3101  int capacity, /**< cumulative capacity */
3102  int hmin, /**< left bound of time axis to be considered (including hmin) */
3103  int hmax, /**< right bound of time axis to be considered (not including hmax) */
3104  SCIP_VAR* infervar, /**< the conflict variable whose bound change has to be resolved */
3105  INFERINFO inferinfo, /**< the user information */
3106  SCIP_BOUNDTYPE boundtype, /**< the type of the changed bound (lower or upper bound) */
3107  SCIP_BDCHGIDX* bdchgidx, /**< the index of the bound change, representing the point of time where the change took place */
3108  SCIP_Real relaxedbd, /**< the relaxed bound which is sufficient to be explained */
3109  SCIP_Bool usebdwidening, /**< should bound widening be used during conflict analysis? */
3110  SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */
3111  SCIP_RESULT* result /**< pointer to store the result of the propagation conflict resolving call */
3112  )
3113 {
3114  switch( inferInfoGetProprule(inferinfo) )
3115  {
3116  case PROPRULE_1_CORETIMES:
3117  {
3118  int inferdemand;
3119  int inferduration;
3120  int inferpos;
3121  int inferpeak;
3122  int relaxedpeak;
3123  int provedpeak;
3124 
3125  /* get the position of the inferred variable in the vars array */
3126  inferpos = inferInfoGetData1(inferinfo);
3127  if( inferpos >= nvars || vars[inferpos] != infervar )
3128  {
3129  /* find inference variable in constraint */
3130  for( inferpos = 0; inferpos < nvars && vars[inferpos] != infervar; ++inferpos )
3131  {}
3132  }
3133  assert(inferpos < nvars);
3134  assert(vars[inferpos] == infervar);
3135 
3136  inferdemand = demands[inferpos];
3137  inferduration = durations[inferpos];
3138 
3139  if( boundtype == SCIP_BOUNDTYPE_UPPER )
3140  {
3141  /* we propagated the latest start time (upper bound) step wise with a step length of at most the duration of
3142  * the inference variable
3143  */
3144  assert(SCIPvarGetUbAtIndex(infervar, bdchgidx, FALSE) - SCIPvarGetUbAtIndex(infervar, bdchgidx, TRUE) < inferduration + 0.5);
3145 
3146  SCIPdebugMessage("variable <%s>: upper bound changed from %g to %g (relaxed %g)\n",
3147  SCIPvarGetName(infervar), SCIPvarGetUbAtIndex(infervar, bdchgidx, FALSE),
3148  SCIPvarGetUbAtIndex(infervar, bdchgidx, TRUE), relaxedbd);
3149 
3150  /* get the inference peak that the time point which lead to the that propagtion */
3151  inferpeak = convertBoundToInt(scip, SCIPvarGetUbAtIndex(infervar, bdchgidx, TRUE)) + inferduration;
3152  relaxedpeak = convertBoundToInt(scip, relaxedbd) + inferduration;
3153 
3154  /* make sure that the relaxed peak is part of the effective horizon */
3155  relaxedpeak = MIN(relaxedpeak, hmax-1);
3156  assert(relaxedpeak >= hmin);
3157 
3158  assert(relaxedpeak >= inferpeak);
3159  }
3160  else
3161  {
3162  assert(boundtype == SCIP_BOUNDTYPE_LOWER);
3163 
3164  SCIPdebugMessage("variable <%s>: lower bound changed from %g to %g (relaxed %g)\n",
3165  SCIPvarGetName(infervar), SCIPvarGetLbAtIndex(infervar, bdchgidx, FALSE),
3166  SCIPvarGetLbAtIndex(infervar, bdchgidx, TRUE), relaxedbd);
3167 
3168  /* get the time interval where the job could not be scheduled */
3169  inferpeak = convertBoundToInt(scip, SCIPvarGetLbAtIndex(infervar, bdchgidx, TRUE)) - 1;
3170  relaxedpeak = convertBoundToInt(scip, relaxedbd) - 1;
3171 
3172  /* make sure that the relaxed peak is part of the effective horizon */
3173  relaxedpeak = MAX(relaxedpeak, hmin);
3174  assert(relaxedpeak < hmax);
3175 
3176  assert(relaxedpeak <= inferpeak);
3177  }
3178 
3179  /* resolves the propagation of the core time algorithm */
3180  SCIP_CALL( resolvePropagationCoretimes(scip, nvars, vars, durations, demands, capacity, hmin, hmax,
3181  infervar, inferdemand, inferpeak, relaxedpeak, bdchgidx, usebdwidening, &provedpeak, explanation) );
3182 
3183  if( boundtype == SCIP_BOUNDTYPE_UPPER )
3184  {
3185  if( usebdwidening )
3186  {
3187  SCIP_CALL( SCIPaddConflictRelaxedUb(scip, infervar, NULL, (SCIP_Real)provedpeak) );
3188  }
3189  else
3190  {
3191  /* old upper bound of variable itself is part of the explanation */
3192  SCIP_CALL( SCIPaddConflictUb(scip, infervar, bdchgidx) );
3193  }
3194  }
3195  else
3196  {
3197  assert(boundtype == SCIP_BOUNDTYPE_LOWER);
3198 
3199  if( usebdwidening )
3200  {
3201  SCIP_CALL( SCIPaddConflictRelaxedLb(scip, infervar, bdchgidx, (SCIP_Real)(provedpeak - inferduration + 1)) );
3202  }
3203  else
3204  {
3205  /* old lower bound of variable itself is part of the explanation */
3206  SCIP_CALL( SCIPaddConflictLb(scip, infervar, bdchgidx) );
3207  }
3208  }
3209 
3210  if( explanation != NULL )
3211  explanation[inferpos] = TRUE;
3212 
3213  break;
3214  }
3216  case PROPRULE_3_TTEF:
3217  {
3218  int begin;
3219  int end;
3220 
3221  begin = inferInfoGetData1(inferinfo);
3222  end = inferInfoGetData2(inferinfo);
3223  assert(begin < end);
3224 
3225  begin = MAX(begin, hmin);
3226  end = MIN(end, hmax);
3227 
3228  SCIP_CALL( analyzeEnergyRequirement(scip, nvars, vars, durations, demands, capacity,
3229  begin, end, infervar, boundtype, bdchgidx, relaxedbd, usebdwidening, explanation) );
3230 
3231  break;
3232  }
3233 
3234  default:
3235  SCIPerrorMessage("invalid inference information %d\n", inferInfoGetProprule(inferinfo));
3236  SCIPABORT();
3237  return SCIP_INVALIDDATA; /*lint !e527*/
3238  }
3239 
3240  (*result) = SCIP_SUCCESS;
3241 
3242  return SCIP_OKAY;
3243 }
3244 
3245 /**@} */
3246 
3247 
3248 /**@name Enforcement methods
3249  *
3250  * @{
3251  */
3252 
3253 /** apply all fixings which are given by the alternative bounds */
3254 static
3256  SCIP* scip, /**< SCIP data structure */
3257  SCIP_VAR** vars, /**< array of active variables */
3258  int nvars, /**< number of active variables */
3259  int* alternativelbs, /**< alternative lower bounds */
3260  int* alternativeubs, /**< alternative lower bounds */
3261  int* downlocks, /**< number of constraints with down lock participating by the computation */
3262  int* uplocks, /**< number of constraints with up lock participating by the computation */
3263  SCIP_Bool* branched /**< pointer to store if a branching was applied */
3264  )
3265 {
3266  int v;
3267 
3268  for( v = 0; v < nvars; ++v )
3269  {
3270  SCIP_VAR* var;
3271  SCIP_Real objval;
3272 
3273  var = vars[v];
3274  assert(var != NULL);
3275 
3276  objval = SCIPvarGetObj(var);
3277 
3278  if( SCIPvarGetNLocksDown(var) == downlocks[v] && !SCIPisNegative(scip, objval) )
3279  {
3280  int ub;
3281 
3282  ub = convertBoundToInt(scip, SCIPvarGetUbLocal(var));
3283 
3284  if( alternativelbs[v] <= ub )
3285  {
3286  SCIP_CALL( SCIPbranchVarHole(scip, var, SCIPvarGetLbLocal(var), (SCIP_Real)alternativelbs[v], NULL, NULL) );
3287  (*branched) = TRUE;
3288 
3289  SCIPdebugMessage("variable <%s> branched domain hole (%g,%d)\n", SCIPvarGetName(var),
3290  SCIPvarGetLbLocal(var), alternativelbs[v]);
3291 
3292  return SCIP_OKAY;
3293  }
3294  }
3295 
3296  if( SCIPvarGetNLocksUp(var) == uplocks[v] && !SCIPisPositive(scip, objval) )
3297  {
3298  int lb;
3299 
3300  lb = convertBoundToInt(scip, SCIPvarGetLbLocal(var));
3301 
3302  if( alternativeubs[v] >= lb )
3303  {
3304  SCIP_CALL( SCIPbranchVarHole(scip, var, (SCIP_Real)alternativeubs[v], SCIPvarGetUbLocal(var), NULL, NULL) );
3305  (*branched) = TRUE;
3306 
3307  SCIPdebugMessage("variable <%s> branched domain hole (%d,%g)\n", SCIPvarGetName(var),
3308  alternativeubs[v], SCIPvarGetUbLocal(var));
3309 
3310  return SCIP_OKAY;
3311  }
3312  }
3313  }
3314 
3315  return SCIP_OKAY;
3316 }
3317 
3318 /** remove the capacity requirments for all job which start at the curtime */
3319 static
3321  SCIP_CONSDATA* consdata, /**< constraint data */
3322  int curtime, /**< current point in time */
3323  int* starttimes, /**< array of start times */
3324  int* startindices, /**< permutation with respect to the start times */
3325  int* freecapacity, /**< pointer to store the resulting free capacity */
3326  int* idx, /**< pointer to index in start time array */
3327  int nvars /**< number of vars in array of starttimes and startindices */
3328  )
3329 {
3330 
3331 #if defined SCIP_DEBUG && !defined NDEBUG
3332  int oldidx;
3333  oldidx = *idx;
3334 #endif
3335 
3336  assert(idx != NULL);
3337  assert(starttimes != NULL);
3338  assert(starttimes != NULL);
3339  assert(freecapacity != NULL);
3340  assert(starttimes[*idx] == curtime);
3341  assert(consdata->demands != NULL);
3342  assert(freecapacity != idx);
3343 
3344  /* subtract all capacity needed up to this point */
3345  (*freecapacity) -= consdata->demands[startindices[*idx]];
3346 
3347  while( (*idx)+1 < nvars && starttimes[(*idx)+1] == curtime )
3348  {
3349  ++(*idx);
3350  (*freecapacity) -= consdata->demands[startindices[(*idx)]];
3351  assert(freecapacity != idx);
3352  }
3353 #ifdef SCIP_DEBUG
3354  assert(oldidx <= *idx);
3355 #endif
3356 }
3357 
3358 /** add the capacity requirments for all job which end at the curtime */
3359 static
3361  SCIP_CONSDATA* consdata, /**< constraint data */
3362  int curtime, /**< current point in time */
3363  int* endtimes, /**< array of end times */
3364  int* endindices, /**< permutation with rspect to the end times */
3365  int* freecapacity, /**< pointer to store the resulting free capacity */
3366  int* idx, /**< pointer to index in end time array */
3367  int nvars /**< number of vars in array of starttimes and startindices */
3368  )
3369 {
3370 #if defined SCIP_DEBUG && !defined NDEBUG
3371  int oldidx;
3372  oldidx = *idx;
3373 #endif
3374 
3375  /* free all capacity usages of jobs the are no longer running */
3376  while( endtimes[*idx] <= curtime && *idx < nvars)
3377  {
3378  (*freecapacity) += consdata->demands[endindices[*idx]];
3379  ++(*idx);
3380  }
3381 
3382 #ifdef SCIP_DEBUG
3383  assert(oldidx <= *idx);
3384 #endif
3385 }
3386 
3387 /** computes a point in time when the capacity is exceeded returns hmax if this does not happen */
3388 static
3390  SCIP* scip, /**< SCIP data structure */
3391  SCIP_CONSDATA* consdata, /**< constraint handler data */
3392  SCIP_SOL* sol, /**< primal solution, or NULL for current LP/pseudo solution */
3393  int* timepoint /**< pointer to store the time point of the peak */
3394  )
3395 {
3396  int* starttimes; /* stores when each job is starting */
3397  int* endtimes; /* stores when each job ends */
3398  int* startindices; /* we will sort the startsolvalues, thus we need to know wich index of a job it corresponds to */
3399  int* endindices; /* we will sort the endsolvalues, thus we need to know wich index of a job it corresponds to */
3400 
3401  int nvars; /* number of activities for this constraint */
3402  int freecapacity; /* remaining capacity */
3403  int curtime; /* point in time which we are just checking */
3404  int endindex; /* index of endsolvalues with: endsolvalues[endindex] > curtime */
3405 
3406  int hmin;
3407  int hmax;
3408 
3409  int j;
3410 
3411  assert(consdata != NULL);
3412 
3413  nvars = consdata->nvars;
3414  assert(nvars > 0);
3415 
3416  *timepoint = consdata->hmax;
3417 
3418  assert(consdata->vars != NULL);
3419 
3420  SCIP_CALL( SCIPallocBufferArray(scip, &starttimes, nvars) );
3421  SCIP_CALL( SCIPallocBufferArray(scip, &endtimes, nvars) );
3422  SCIP_CALL( SCIPallocBufferArray(scip, &startindices, nvars) );
3423  SCIP_CALL( SCIPallocBufferArray(scip, &endindices, nvars) );
3424 
3425  /* create event point arrays */
3426  createSortedEventpointsSol(scip, sol, consdata->nvars, consdata->vars, consdata->durations,
3427  starttimes, endtimes, startindices, endindices);
3428 
3429  endindex = 0;
3430  freecapacity = consdata->capacity;
3431  hmin = consdata->hmin;
3432  hmax = consdata->hmax;
3433 
3434  /* check each startpoint of a job whether the capacity is kept or not */
3435  for( j = 0; j < nvars; ++j )
3436  {
3437  curtime = starttimes[j];
3438  SCIPdebugMessage("look at %d-th job with start %d\n", j, curtime);
3439 
3440  if( curtime >= hmax )
3441  break;
3442 
3443  /* remove the capacity requirments for all job which start at the curtime */
3444  subtractStartingJobDemands(consdata, curtime, starttimes, startindices, &freecapacity, &j, nvars);
3445 
3446  /* add the capacity requirments for all job which end at the curtime */
3447  addEndingJobDemands(consdata, curtime, endtimes, endindices, &freecapacity, &endindex, nvars);
3448 
3449  assert(freecapacity <= consdata->capacity);
3450  assert(endindex <= nvars);
3451 
3452  /* endindex - points to the next job which will finish */
3453  /* j - points to the last job that has been released */
3454 
3455  /* if free capacity is smaller than zero, then add branching candidates */
3456  if( freecapacity < 0 && curtime >= hmin )
3457  {
3458  *timepoint = curtime;
3459  break;
3460  }
3461  } /*lint --e{850}*/
3462 
3463  /* free all buffer arrays */
3464  SCIPfreeBufferArray(scip, &endindices);
3465  SCIPfreeBufferArray(scip, &startindices);
3466  SCIPfreeBufferArray(scip, &endtimes);
3467  SCIPfreeBufferArray(scip, &starttimes);
3468 
3469  return SCIP_OKAY;
3470 }
3471 
3472 /** checks all cumulative constraints for infeasibility and add branching candidates to storage */
3473 static
3475  SCIP* scip, /**< SCIP data structure */
3476  SCIP_CONS** conss, /**< constraints to be processed */
3477  int nconss, /**< number of constraints */
3478  SCIP_SOL* sol, /**< primal solution, or NULL for current LP/pseudo solution */
3479  int* nbranchcands /**< pointer to store the number of branching variables */
3480  )
3481 {
3482  SCIP_HASHTABLE* collectedvars;
3483  int c;
3484 
3485  assert(scip != NULL);
3486  assert(conss != NULL);
3487 
3488  /* create a hash table */
3490  SCIPvarGetHashkey, SCIPvarIsHashkeyEq, SCIPvarGetHashkeyVal, NULL) );
3491 
3492  assert(scip != NULL);
3493  assert(conss != NULL);
3494 
3495  for( c = 0; c < nconss; ++c )
3496  {
3497  SCIP_CONS* cons;
3498  SCIP_CONSDATA* consdata;
3499 
3500  int curtime;
3501  int j;
3502 
3503  cons = conss[c];
3504  assert(cons != NULL);
3505 
3506  if( !SCIPconsIsActive(cons) )
3507  continue;
3508 
3509  consdata = SCIPconsGetData(cons);
3510  assert(consdata != NULL);
3511 
3512  /* get point in time when capacity is exceeded */
3513  SCIP_CALL( computePeak(scip, consdata, sol, &curtime) );
3514 
3515  if( curtime < consdata->hmin || curtime >= consdata->hmax )
3516  continue;
3517 
3518  /* report all variables that are running at that point in time */
3519  for( j = 0; j < consdata->nvars; ++j )
3520  {
3521  SCIP_VAR* var;
3522  int lb;
3523  int ub;
3524 
3525  var = consdata->vars[j];
3526  assert(var != NULL);
3527 
3528  /* check if the variable was already added */
3529  if( SCIPhashtableExists(collectedvars, (void*)var) )
3530  continue;
3531 
3532  lb = convertBoundToInt(scip, SCIPvarGetLbLocal(var));
3533  ub = convertBoundToInt(scip, SCIPvarGetUbLocal(var));
3534 
3535  if( lb <= curtime && ub + consdata->durations[j] > curtime && lb < ub )
3536  {
3537  SCIP_Real solval;
3538  SCIP_Real score;
3539 
3540  solval = SCIPgetSolVal(scip, sol, var);
3541  score = MIN(solval - lb, ub - solval) / ((SCIP_Real)ub-lb);
3542 
3543  SCIPdebugMessage("add var <%s> to branch cand storage\n", SCIPvarGetName(var));
3544  SCIP_CALL( SCIPaddExternBranchCand(scip, var, score, lb + (ub - lb) / 2.0 + 0.2) );
3545  (*nbranchcands)++;
3546 
3547  SCIP_CALL( SCIPhashtableInsert(collectedvars, var) );
3548  }
3549  }
3550  }
3551 
3552  SCIPhashtableFree(&collectedvars);
3553 
3554  SCIPdebugMessage("found %d branching candidates\n", *nbranchcands);
3555 
3556  return SCIP_OKAY;
3557 }
3558 
3559 /** enforcement pseudo or LP solution */
3560 static
3562  SCIP* scip, /**< SCIP data structure */
3563  SCIP_CONS** conss, /**< constraints to be processed */
3564  int nconss, /**< number of constraints */
3565  SCIP_Bool branch, /**< should branching candidates be collected */
3566  SCIP_RESULT* result /**< pointer to store the result */
3567  )
3568 {
3569  if( branch )
3570  {
3571  int nbranchcands;
3572 
3573  nbranchcands = 0;
3574  SCIP_CALL( collectBranchingCands(scip, conss, nconss, NULL, &nbranchcands) );
3575 
3576  if( nbranchcands > 0 )
3577  (*result) = SCIP_INFEASIBLE;
3578  }
3579  else
3580  {
3581  SCIP_Bool violated;
3582  int c;
3583 
3584  violated = FALSE;
3585 
3586  /* first check if a constraints is violated */
3587  for( c = 0; c < nconss && !violated; ++c )
3588  {
3589  SCIP_CONS* cons;
3590 
3591  cons = conss[c];
3592  assert(cons != NULL);
3593 
3594  SCIP_CALL( checkCons(scip, cons, NULL, &violated, FALSE) );
3595  }
3596 
3597  if( violated )
3598  (*result) = SCIP_INFEASIBLE;
3599  }
3600 
3601  return SCIP_OKAY;
3602 }
3603 
3604 /**@} */
3605 
3606 /**@name Propagation
3607  *
3608  * @{
3609  */
3610 
3611 /** check if cumulative constraint is independently of all other constraints */
3612 static
3614  SCIP* scip, /**< SCIP data structure */
3615  SCIP_CONS* cons /**< cumulative constraint */
3616  )
3617 {
3618  SCIP_CONSDATA* consdata;
3619  SCIP_VAR** vars;
3620  SCIP_Bool* downlocks;
3621  SCIP_Bool* uplocks;
3622  int nvars;
3623  int v;
3624 
3625  consdata = SCIPconsGetData(cons);
3626  assert(consdata != NULL);
3627 
3628  nvars = consdata->nvars;
3629  vars = consdata->vars;
3630  downlocks = consdata->downlocks;
3631  uplocks = consdata->uplocks;
3632 
3633  /* check if the cumulative constraint has the only locks on the involved variables */
3634  for( v = 0; v < nvars; ++v )
3635  {
3636  SCIP_VAR* var;
3637 
3638  var = vars[v];
3639  assert(var != NULL);
3640 
3641  if( SCIPvarGetNLocksDown(var) > (int)downlocks[v] || SCIPvarGetNLocksUp(var) > (int)uplocks[v] )
3642  return FALSE;
3643  }
3644 
3645  return TRUE;
3646 }
3647 
3648 /** in case the cumulative constraint is independent of every else, solve the cumulative problem and apply the fixings
3649  * (dual reductions)
3650  */
3651 static
3653  SCIP* scip, /**< SCIP data structure */
3654  SCIP_CONS* cons, /**< cumulative constraint */
3655  SCIP_Longint maxnodes, /**< number of branch-and-bound nodes to solve an independent cumulative constraint (-1: no limit) */
3656  int* nchgbds, /**< pointer to store the number changed variable bounds */
3657  int* nfixedvars, /**< pointer to count number of fixings */
3658  int* ndelconss, /**< pointer to count number of deleted constraints */
3659  SCIP_Bool* cutoff, /**< pointer to store if the constraint is infeasible */
3660  SCIP_Bool* unbounded /**< pointer to store if the constraint is unbounded */
3661  )
3662 {
3663  SCIP_CONSDATA* consdata;
3664  SCIP_VAR** vars;
3665  SCIP_Real* objvals;
3666  SCIP_Real* lbs;
3667  SCIP_Real* ubs;
3668  SCIP_Real timelimit;
3669  SCIP_Real memorylimit;
3670  SCIP_Bool solved;
3671  SCIP_Bool error;
3672 
3673  int ncheckconss;
3674  int nvars;
3675  int v;
3676 
3677  assert(scip != NULL);
3678  assert(!SCIPconsIsModifiable(cons));
3679  assert(SCIPgetNConss(scip) > 0);
3680 
3681  /* if SCIP is in probing mode or repropagation we cannot perform this dual reductions since this dual reduction
3682  * would/could end in an implication which can lead to cutoff of the/all optimal solution
3683  */
3684  if( SCIPinProbing(scip) || SCIPinRepropagation(scip) )
3685  return SCIP_OKAY;
3686 
3687  /* constraints for which the check flag is set to FALSE, did not contribute to the lock numbers; therefore, we cannot
3688  * use the locks to decide for a dual reduction using this constraint;
3689  */
3690  if( !SCIPconsIsChecked(cons) )
3691  return SCIP_OKAY;
3692 
3693  ncheckconss = SCIPgetNCheckConss(scip);
3694 
3695  /* if the cumulative constraint is the only constraint of the original problem or the only check constraint in the
3696  * presolved problem do nothing execpt to change the parameter settings
3697  */
3698  if( ncheckconss == 1 )
3699  {
3700  /* shrink the minimal maximum value for the conflict length */
3701  SCIP_CALL( SCIPsetIntParam(scip, "conflict/minmaxvars", 10) );
3702 
3703  /* use only first unique implication point */
3704  SCIP_CALL( SCIPsetIntParam(scip, "conflict/fuiplevels", 1) );
3705 
3706  /* do not use reconversion conflicts */
3707  SCIP_CALL( SCIPsetIntParam(scip, "conflict/reconvlevels", 0) );
3708 
3709  /* after 250 conflict we force a restart since then the variable statistics are reasonable initialized */
3710  SCIP_CALL( SCIPsetIntParam(scip, "conflict/restartnum", 250) );
3711 
3712  /* increase the number of conflicts which induce a restart */
3713  SCIP_CALL( SCIPsetRealParam(scip, "conflict/restartfac", 2.0) );
3714 
3715  /* weight the variable which made into a conflict */
3716  SCIP_CALL( SCIPsetRealParam(scip, "conflict/conflictweight", 1.0) );
3717 
3718  /* do not check pseudo solution (for performance reasons) */
3719  SCIP_CALL( SCIPsetBoolParam(scip, "constraints/disableenfops", TRUE) );
3720 
3721  /* use value based history to detect a reasonable branching point */
3722  SCIP_CALL( SCIPsetBoolParam(scip, "history/valuebased", TRUE) );
3723 
3724  /* turn of LP relaxation */
3725  SCIP_CALL( SCIPsetIntParam(scip, "lp/solvefreq", -1) );
3726 
3727  /* prefer the down branch in case the value based history does not suggest something */
3728  SCIP_CALL( SCIPsetCharParam(scip, "nodeselection/childsel", 'd') );
3729 
3730  /* accept any bound change */
3731  SCIP_CALL( SCIPsetRealParam(scip, "numerics/boundstreps", 1e-6) );
3732 
3733  /* allow for at most 10 restart, after that the value based history should be reliable */
3734  SCIP_CALL( SCIPsetIntParam(scip, "presolving/maxrestarts", 10) );
3735 
3736  /* set priority for depth first search to highest possible value */
3737  SCIP_CALL( SCIPsetIntParam(scip, "nodeselection/dfs/stdpriority", INT_MAX/4) );
3738 
3739  return SCIP_OKAY;
3740  }
3741 
3742  consdata = SCIPconsGetData(cons);
3743  assert(consdata != NULL);
3744 
3745  /* check if already tried to solve that constraint as independent sub problem; we do not want to try it again if we
3746  * fail on the first place
3747  */
3748  if( consdata->triedsolving )
3749  return SCIP_OKAY;
3750 
3751  /* check if constraint is independently */
3752  if( !isConsIndependently(scip, cons) )
3753  return SCIP_OKAY;
3754 
3755  /* mark the constraint to be tried of solving it as independent sub problem; in case that is successful the
3756  * constraint is deleted; otherwise, we want to ensure that we do not try that again
3757  */
3758  consdata->triedsolving = TRUE;
3759 
3760  SCIPdebugMessage("the cumulative constraint <%s> is independent from rest of the problem (%d variables, %d constraints)\n",
3761  SCIPconsGetName(cons), SCIPgetNVars(scip), SCIPgetNConss(scip));
3762  SCIPdebugPrintCons(scip, cons, NULL);
3763 
3764  nvars = consdata->nvars;
3765  vars = consdata->vars;
3766 
3767  SCIP_CALL( SCIPallocBufferArray(scip, &lbs, nvars) );
3768  SCIP_CALL( SCIPallocBufferArray(scip, &ubs, nvars) );
3769  SCIP_CALL( SCIPallocBufferArray(scip, &objvals, nvars) );
3770 
3771  for( v = 0; v < nvars; ++v )
3772  {
3773  SCIP_VAR* var;
3774 
3775  /* if a variables array is given, use the variable bounds otherwise the default values stored in the ests and lsts
3776  * array
3777  */
3778  var = vars[v];
3779  assert(var != NULL);
3780 
3781  lbs[v] = SCIPvarGetLbLocal(var);
3782  ubs[v] = SCIPvarGetUbLocal(var);
3783 
3784  objvals[v] = SCIPvarGetObj(var);
3785  }
3786 
3787  /* check whether there is enough time and memory left */
3788  SCIP_CALL( SCIPgetRealParam(scip, "limits/time", &timelimit) );
3789  if( !SCIPisInfinity(scip, timelimit) )
3790  timelimit -= SCIPgetSolvingTime(scip);
3791  SCIP_CALL( SCIPgetRealParam(scip, "limits/memory", &memorylimit) );
3792 
3793  /* substract the memory already used by the main SCIP and the estimated memory usage of external software */
3794  if( !SCIPisInfinity(scip, memorylimit) )
3795  {
3796  memorylimit -= SCIPgetMemUsed(scip)/1048576.0;
3797  memorylimit -= SCIPgetMemExternEstim(scip)/1048576.0;
3798  }
3799 
3800  /* solve the cumulative condition separately */
3801  SCIP_CALL( SCIPsolveCumulative(scip, nvars, lbs, ubs, objvals, consdata->durations, consdata->demands, consdata->capacity,
3802  consdata->hmin, consdata->hmax, timelimit, memorylimit, maxnodes, &solved, cutoff, unbounded, &error) );
3803 
3804  if( !(*cutoff) && !(*unbounded) && !error )
3805  {
3806  SCIP_Bool infeasible;
3807  SCIP_Bool tightened;
3808  SCIP_Bool allfixed;
3809 
3810  allfixed = TRUE;
3811 
3812  for( v = 0; v < nvars; ++v )
3813  {
3814  /* check if variable is fixed */
3815  if( lbs[v] + 0.5 > ubs[v] )
3816  {
3817  SCIP_CALL( SCIPfixVar(scip, vars[v], lbs[v], &infeasible, &tightened) );
3818  assert(!infeasible);
3819 
3820  if( tightened )
3821  {
3822  (*nfixedvars)++;
3823  consdata->triedsolving = FALSE;
3824  }
3825  }
3826  else
3827  {
3828  SCIP_CALL( SCIPtightenVarLb(scip, vars[v], lbs[v], TRUE, &infeasible, &tightened) );
3829  assert(!infeasible);
3830 
3831  if( tightened )
3832  {
3833  (*nchgbds)++;
3834  consdata->triedsolving = FALSE;
3835  }
3836 
3837  SCIP_CALL( SCIPtightenVarUb(scip, vars[v], ubs[v], TRUE, &infeasible, &tightened) );
3838  assert(!infeasible);
3839 
3840  if( tightened )
3841  {
3842  (*nchgbds)++;
3843  consdata->triedsolving = FALSE;
3844  }
3845 
3846  allfixed = FALSE;
3847  }
3848  }
3849 
3850  /* if all variables are fixed, remove the cumulative constraint since it is redundant */
3851  if( allfixed )
3852  {
3853  SCIP_CALL( SCIPdelConsLocal(scip, cons) );
3854  (*ndelconss)++;
3855  }
3856  }
3857 
3858  SCIPfreeBufferArray(scip, &objvals);
3859  SCIPfreeBufferArray(scip, &ubs);
3860  SCIPfreeBufferArray(scip, &lbs);
3861 
3862  return SCIP_OKAY;
3863 }
3864 
3865 /** start conflict analysis to analysis the core insertion which is infeasible */
3866 static
3868  SCIP* scip, /**< SCIP data structure */
3869  int nvars, /**< number of start time variables (activities) */
3870  SCIP_VAR** vars, /**< array of start time variables */
3871  int* durations, /**< array of durations */
3872  int* demands, /**< array of demands */
3873  int capacity, /**< cumulative capacity */
3874  int hmin, /**< left bound of time axis to be considered (including hmin) */
3875  int hmax, /**< right bound of time axis to be considered (not including hmax) */
3876  SCIP_VAR* infervar, /**< start time variable which lead to the infeasibilty */
3877  int inferduration, /**< duration of the start time variable */
3878  int inferdemand, /**< demand of the start time variable */
3879  int inferpeak, /**< profile preak which causes the infeasibilty */
3880  SCIP_Bool usebdwidening, /**< should bound widening be used during conflict analysis? */
3881  SCIP_Bool* initialized, /**< pointer to store if the conflict analysis was initialized */
3882  SCIP_Bool* explanation /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */
3883  )
3884 {
3885  SCIPdebugMessage("detected infeasibility due to adding a core to the core resource profile\n");
3886  SCIPdebugMessage("variable <%s>[%g,%g] (demand %d, duration %d)\n", SCIPvarGetName(infervar),
3887  SCIPvarGetLbLocal(infervar), SCIPvarGetUbLocal(infervar), inferdemand, inferduration);
3888 
3889  /* initialize conflict analysis if conflict analysis is applicable */
3891  {
3893 
3894  SCIP_CALL( resolvePropagationCoretimes(scip, nvars, vars, durations, demands, capacity, hmin, hmax,
3895  infervar, inferdemand, inferpeak, inferpeak, NULL, usebdwidening, NULL, explanation) );
3896 
3897  SCIPdebugMessage("add lower and upper bounds of variable <%s>\n", SCIPvarGetName(infervar));
3898 
3899  /* add both bound of the inference variable since these biuld the core which we could not inserted */
3900  if( usebdwidening )
3901  {
3902  SCIP_CALL( SCIPaddConflictRelaxedLb(scip, infervar, NULL, (SCIP_Real)(inferpeak - inferduration + 1)) );
3903  SCIP_CALL( SCIPaddConflictRelaxedUb(scip, infervar, NULL, (SCIP_Real)inferpeak) );
3904  }
3905  else
3906  {
3907  SCIP_CALL( SCIPaddConflictLb(scip, infervar, NULL) );
3908  SCIP_CALL( SCIPaddConflictUb(scip, infervar, NULL) );
3909  }
3910 
3911  *initialized = TRUE;
3912  }
3913 
3914  return SCIP_OKAY;
3915 }
3916 
3917 /** We are using the core resource profile which contains all core except the one of the start time variable which we
3918  * want to propagate, to incease the earliest start time. This we are doing in steps of length at most the duration of
3919  * the job. The reason for that is, that this makes it later easier to resolve this propagation during the conflict
3920  * analysis
3921  */
3922 static
3924  SCIP* scip, /**< SCIP data structure */
3925  int nvars, /**< number of start time variables (activities) */
3926  SCIP_VAR** vars, /**< array of start time variables */
3927  int* durations, /**< array of durations */
3928  int* demands, /**< array of demands */
3929  int capacity, /**< cumulative capacity */
3930  int hmin, /**< left bound of time axis to be considered (including hmin) */
3931  int hmax, /**< right bound of time axis to be considered (not including hmax) */
3932  SCIP_CONS* cons, /**< constraint which is propagated */
3933  SCIP_PROFILE* profile, /**< resource profile */
3934  int idx, /**< position of the variable to propagate */
3935  int* nchgbds, /**< pointer to store the number of bound changes */
3936  SCIP_Bool usebdwidening, /**< should bound widening be used during conflict analysis? */
3937  SCIP_Bool* initialized, /**< was conflict analysis initialized */
3938  SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */
3939  SCIP_Bool* infeasible /**< pointer to store if the constraint is infeasible */
3940  )
3941 {
3942  SCIP_VAR* var;
3943  int ntimepoints;
3944  int duration;
3945  int demand;
3946  int peak;
3947  int newlb;
3948  int est;
3949  int lst;
3950  int pos;
3951 
3952  var = vars[idx];
3953  assert(var != NULL);
3954 
3955  duration = durations[idx];
3956  assert(duration > 0);
3957 
3958  demand = demands[idx];
3959  assert(demand > 0);
3960 
3961  est = convertBoundToInt(scip, SCIPvarGetLbLocal(var));
3962  lst = convertBoundToInt(scip, SCIPvarGetUbLocal(var));
3963  ntimepoints = SCIPprofileGetNTimepoints(profile);
3964 
3965  /* first we find left position of earliest start time (lower bound) in resource profile; this position gives us the
3966  * load which we have at the earliest start time (lower bound)
3967  */
3968  (void) SCIPprofileFindLeft(profile, est, &pos);
3969 
3970  SCIPdebugMessage("propagate earliest start time (lower bound) (pos %d)\n", pos);
3971 
3972  /* we now trying to move the earliest start time in steps of at most "duration" length */
3973  do
3974  {
3975  INFERINFO inferinfo;
3976  SCIP_Bool tightened;
3977  int ect;
3978 
3979 #ifndef NDEBUG
3980  {
3981  /* in debug mode we check that we adjust the search position correctly */
3982  int tmppos;
3983 
3984  (void)SCIPprofileFindLeft(profile, est, &tmppos);
3985  assert(pos == tmppos);
3986  }
3987 #endif
3988  ect = est + duration;
3989  peak = -1;
3990 
3991  /* we search for a peak within the core profile which conflicts with the demand of the start time variable; we
3992  * want a peak which is closest to the earliest completion time
3993  */
3994  do
3995  {
3996  /* check if the profile load conflicts with the demand of the start time variable */
3997  if( SCIPprofileGetLoad(profile, pos) + demand > capacity )
3998  peak = pos;
3999 
4000  pos++;
4001  }
4002  while( pos < ntimepoints && SCIPprofileGetTime(profile, pos) < ect );
4003 
4004  /* if we found no peak that means current the job could be scheduled at its earliest start time without
4005  * conflicting to the core resource profile
4006  */
4007  if( peak == -1 )
4008  break;
4009 
4010  /* the peak position gives us a time point where the start time variable is in conflict with the resource
4011  * profile. That means we have to move it to the next time point in the resource profile but at most to the
4012  * earliest completion time (the remaining move will done in the next loop)
4013  */
4014  newlb = SCIPprofileGetTime(profile, peak+1);
4015  newlb = MIN(newlb, ect);
4016 
4017  /* if the earliest start time is greater than the lst we detected an infeasibilty */
4018  if( newlb > lst )
4019  {
4020  SCIPdebugMessage("variable <%s>: cannot be scheduled\n", SCIPvarGetName(var));
4021 
4022  /* use conflict analysis to analysis the core insertion which was infeasible */
4023  SCIP_CALL( analyseInfeasibelCoreInsertion(scip, nvars, vars, durations, demands, capacity, hmin, hmax,
4024  var, duration, demand, newlb-1, usebdwidening, initialized, explanation) );
4025 
4026  if( explanation != NULL )
4027  explanation[idx] = TRUE;
4028 
4029  *infeasible = TRUE;
4030 
4031  break;
4032  }
4033 
4034  /* construct the inference information which we are using with the conflict analysis to resolve that particular
4035  * bound change
4036  */
4037  inferinfo = getInferInfo(PROPRULE_1_CORETIMES, idx, 0);
4038 
4039  /* perform the bound lower bound change */
4040  SCIP_CALL( SCIPinferVarLbCons(scip, var, (SCIP_Real)newlb, cons, inferInfoToInt(inferinfo), TRUE, infeasible, &tightened) );
4041  assert(tightened);
4042  assert(!(*infeasible));
4043 
4044  SCIPdebugMessage("variable <%s> new lower bound <%d> -> <%d>\n", SCIPvarGetName(var), est, newlb);
4045  (*nchgbds)++;
4046 
4047  /* for the statistic we count the number of times a lower bound was tightened due the the time-table algorithm */
4049 
4050  /* adjust the earliest start time
4051  *
4052  * @note We are taking the lower of the start time variable on purpose instead of newlb. This is due the fact that
4053  * the proposed lower bound might be even strength by be the core which can be the case if aggregations are
4054  * involved.
4055  */
4056  est = convertBoundToInt(scip, SCIPvarGetLbLocal(var));
4057  assert(est >= newlb);
4058 
4059  /* adjust the search position for the resource profile for the next step */
4060  if( est == SCIPprofileGetTime(profile, peak+1) )
4061  pos = peak + 1;
4062  else
4063  pos = peak;
4064  }
4065  while( est < lst );
4066 
4067  return SCIP_OKAY;
4068 }
4069 
4070 /** We are using the core resource profile which contains all core except the one of the start time variable which we
4071  * want to propagate, to decrease the latest start time. This we are doing in steps of length at most the duration of
4072  * the job. The reason for that is, that this makes it later easier to resolve this propagation during the conflict
4073  * analysis
4074  */
4075 static
4077  SCIP* scip, /**< SCIP data structure */
4078  SCIP_VAR* var, /**< start time variable to propagate */
4079  int duration, /**< duration of the job */
4080  int demand, /**< demand of the job */
4081  int capacity, /**< cumulative capacity */
4082  SCIP_CONS* cons, /**< constraint which is propagated */
4083  SCIP_PROFILE* profile, /**< resource profile */
4084  int idx, /**< position of the variable to propagate */
4085  int* nchgbds /**< pointer to store the number of bound changes */
4086  )
4087 {
4088  int ntimepoints;
4089  int newub;
4090  int peak;
4091  int pos;
4092  int est;
4093  int lst;
4094  int lct;
4095 
4096  assert(var != NULL);
4097  assert(duration > 0);
4098  assert(demand > 0);
4099 
4100  est = convertBoundToInt(scip, SCIPvarGetLbLocal(var));
4101  lst = convertBoundToInt(scip, SCIPvarGetUbLocal(var));
4102 
4103  /* in case the start time variable is fixed do nothing */
4104  if( est == lst )
4105  return SCIP_OKAY;
4106 
4107  ntimepoints = SCIPprofileGetNTimepoints(profile);
4108 
4109  lct = lst + duration;
4110 
4111  /* first we find left position of latest completion time minus 1 (upper bound + duration) in resource profile; That
4112  * is the last time point where the job would run if schedule it at its latest start time (upper bound). This
4113  * position gives us the load which we have at the latest completion time minus one
4114  */
4115  (void) SCIPprofileFindLeft(profile, lct - 1, &pos);
4116 
4117  SCIPdebugMessage("propagate upper bound (pos %d)\n", pos);
4118  SCIPdebug( SCIPprofilePrint(profile, SCIPgetMessagehdlr(scip), NULL) );
4119 
4120  if( pos == ntimepoints-1 && SCIPprofileGetTime(profile, pos) == lst )
4121  return SCIP_OKAY;
4122 
4123  /* we now trying to move the latest start time in steps of at most "duration" length */
4124  do
4125  {
4126  INFERINFO inferinfo;
4127  SCIP_Bool tightened;
4128  SCIP_Bool infeasible;
4129 
4130  peak = -1;
4131 
4132 #ifndef NDEBUG
4133  {
4134  /* in debug mode we check that we adjust the search position correctly */
4135  int tmppos;
4136 
4137  (void)SCIPprofileFindLeft(profile, lct - 1, &tmppos);
4138  assert(pos == tmppos);
4139  }
4140 #endif
4141 
4142  /* we search for a peak within the core profile which conflicts with the demand of the start time variable; we
4143  * want a peak which is closest to the latest start time
4144  */
4145  do
4146  {
4147  if( SCIPprofileGetLoad(profile, pos) + demand > capacity )
4148  peak = pos;
4149 
4150  pos--;
4151  }
4152  while( pos >= 0 && SCIPprofileGetTime(profile, pos+1) > lst);
4153 
4154  /* if we found no peak that means the current job could be scheduled at its latest start time without conflicting
4155  * to the core resource profile
4156  */
4157  if( peak == -1 )
4158  break;
4159 
4160  /* the peak position gives us a time point where the start time variable is in conflict with the resource
4161  * profile. That means the job has be done until that point. Hence that gives us the latest completion
4162  * time. Note that that we want to move the bound by at most the duration length (the remaining move we are
4163  * doing in the next loop)
4164  */
4165  newub = SCIPprofileGetTime(profile, peak);
4166  newub = MAX(newub, lst) - duration;
4167  assert(newub >= est);
4168 
4169  /* construct the inference information which we are using with the conflict analysis to resolve that particular
4170  * bound change
4171  */
4172  inferinfo = getInferInfo(PROPRULE_1_CORETIMES, idx, 0);
4173 
4174  /* perform the bound upper bound change */
4175  SCIP_CALL( SCIPinferVarUbCons(scip, var, (SCIP_Real)newub, cons, inferInfoToInt(inferinfo), TRUE, &infeasible, &tightened) );
4176  assert(tightened);
4177  assert(!infeasible);
4178 
4179  SCIPdebugMessage("variable <%s>: new upper bound <%d> -> <%d>\n", SCIPvarGetName(var), lst, newub);
4180  (*nchgbds)++;
4181 
4182  /* for the statistic we count the number of times a upper bound was tightened due the the time-table algorithm */
4184 
4185  /* adjust the latest start and completion time
4186  *
4187  * @note We are taking the upper of the start time variable on purpose instead of newub. This is due the fact that
4188  * the proposed upper bound might be even strength by be the core which can be the case if aggregations are
4189  * involved.
4190  */
4191  lst = convertBoundToInt(scip, SCIPvarGetUbLocal(var));
4192  assert(lst <= newub);
4193  lct = lst + duration;
4194 
4195  /* adjust the search position for the resource profile for the next step */
4196  if( SCIPprofileGetTime(profile, peak) == lct )
4197  pos = peak - 1;
4198  else
4199  pos = peak;
4200  }
4201  while( est < lst );
4202 
4203  return SCIP_OKAY;
4204 }
4205 
4206 /** compute for the different earliest start and latest completion time the core energy of the corresponding time
4207  * points
4208  */
4209 static
4211  SCIP* scip, /**< SCIP data structure */
4212  SCIP_PROFILE* profile, /**< core profile */
4213  int nvars, /**< number of start time variables (activities) */
4214  int* ests, /**< array of sorted earliest start times */
4215  int* lcts, /**< array of sorted latest completion times */
4216  int* coreEnergyAfterEst, /**< array to store the core energy after the earliest start time of each job */
4217  int* coreEnergyAfterLct /**< array to store the core energy after the latest completion time of each job */
4218  )
4219 {
4220  int ntimepoints;
4221  int energy;
4222  int t;
4223  int v;
4224 
4225  ntimepoints = SCIPprofileGetNTimepoints(profile);
4226  t = ntimepoints - 1;
4227  energy = 0;
4228 
4229  /* compute core energy after the earliest start time of each job */
4230  for( v = nvars-1; v >= 0; --v )
4231  {
4232  while( t > 0 && SCIPprofileGetTime(profile, t-1) >= ests[v] )
4233  {
4234  assert(SCIPprofileGetLoad(profile, t-1) >= 0);
4235  assert(SCIPprofileGetTime(profile, t) - SCIPprofileGetTime(profile, t-1)>= 0);
4236  energy += SCIPprofileGetLoad(profile, t-1) * (SCIPprofileGetTime(profile, t) - SCIPprofileGetTime(profile, t-1));
4237  t--;
4238  }
4239  assert(SCIPprofileGetTime(profile, t) >= ests[v] || t == ntimepoints-1);
4240 
4241  /* maybe ests[j] is in-between two timepoints */
4242  if( SCIPprofileGetTime(profile, t) - ests[v] > 0 )
4243  {
4244  assert(t > 0);
4245  coreEnergyAfterEst[v] = energy + SCIPprofileGetLoad(profile, t-1) * (SCIPprofileGetTime(profile, t) - ests[v]);
4246  }
4247  else
4248  coreEnergyAfterEst[v] = energy;
4249  }
4250 
4251  t = ntimepoints - 1;
4252  energy = 0;
4253 
4254  /* compute core energy after the latest completion time of each job */
4255  for( v = nvars-1; v >= 0; --v )
4256  {
4257  while( t > 0 && SCIPprofileGetTime(profile, t-1) >= lcts[v] )
4258  {
4259  assert(SCIPprofileGetLoad(profile, t-1) >= 0);
4260  assert(SCIPprofileGetTime(profile, t) - SCIPprofileGetTime(profile, t-1)>= 0);
4261  energy += SCIPprofileGetLoad(profile, t-1) * (SCIPprofileGetTime(profile, t) - SCIPprofileGetTime(profile, t-1));
4262  t--;
4263  }
4264  assert(SCIPprofileGetTime(profile, t) >= lcts[v] || t == ntimepoints-1);
4265 
4266  /* maybe lcts[j] is in-between two timepoints */
4267  if( SCIPprofileGetTime(profile, t) - lcts[v] > 0 )
4268  {
4269  assert(t > 0);
4270  coreEnergyAfterLct[v] = energy + SCIPprofileGetLoad(profile, t-1) * (SCIPprofileGetTime(profile, t) - lcts[v]);
4271  }
4272  else
4273  coreEnergyAfterLct[v] = energy;
4274  }
4275 
4276  return SCIP_OKAY;
4277 }
4278 
4279 /** collect earliest start times, latest completion time, and free energy contributions */
4280 static
4282  SCIP* scip, /**< SCIP data structure */
4283  int nvars, /**< number of start time variables (activities) */
4284  SCIP_VAR** vars, /**< array of start time variables */
4285  int* durations, /**< array of durations */
4286  int* demands, /**< array of demands */
4287  int hmin, /**< left bound of time axis to be considered (including hmin) */
4288  int hmax, /**< right bound of time axis to be considered (not including hmax) */
4289  int* permests, /**< array to store the variable positions */
4290  int* ests, /**< array to store earliest start times */
4291  int* permlcts, /**< array to store the variable positions */
4292  int* lcts, /**< array to store latest completion times */
4293  int* ects, /**< array to store earliest completion times of the flexible part of the job */
4294  int* lsts, /**< array to store latest start times of the flexible part of the job */
4295  int* flexenergies /**< array to store the flexible energies of each job */
4296  )
4297 {
4298  int v;
4299 
4300  for( v = 0; v < nvars; ++ v)
4301  {
4302  int duration;
4303  int leftadjust;
4304  int rightadjust;
4305  int core;
4306  int est;
4307  int lct;
4308  int ect;
4309  int lst;
4310 
4311  duration = durations[v];
4312  assert(duration > 0);
4313 
4314  est = convertBoundToInt(scip, SCIPvarGetLbLocal(vars[v]));
4315  lst = convertBoundToInt(scip, SCIPvarGetUbLocal(vars[v]));
4316  ect = est + duration;
4317  lct = lst + duration;
4318 
4319  ests[v] = est;
4320  lcts[v] = lct;
4321  permests[v] = v;
4322  permlcts[v] = v;
4323 
4324  /* compute core time window which lies within the effective horizon */
4325  core = computeCoreWithInterval(hmin, hmax, ect, lst);
4326 
4327  /* compute the number of time steps the job could run before the effective horizon */
4328  leftadjust = MAX(0, hmin - est);
4329 
4330  /* compute the number of time steps the job could run after the effective horizon */
4331  rightadjust = MAX(0, lct - hmax);
4332 
4333  /* compute for each job the energy which is flexible; meaning not part of the core */
4334  flexenergies[v] = duration - leftadjust - rightadjust - core;
4335  flexenergies[v] = MAX(0, flexenergies[v]);
4336  flexenergies[v] *= demands[v];
4337  assert(flexenergies[v] >= 0);
4338 
4339  /* the earliest completion time of the flexible energy */
4340  ects[v] = MIN(ect, lst);
4341 
4342  /* the latest start time of the flexible energy */
4343  lsts[v] = MAX(ect, lst);
4344  }
4345 }
4346 
4347 /** try to tighten the lower bound of the given variable */
4348 static
4350  SCIP* scip, /**< SCIP data structure */
4351  SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
4352  int nvars, /**< number of start time variables (activities) */
4353  SCIP_VAR** vars, /**< array of start time variables */
4354  int* durations, /**< array of durations */
4355  int* demands, /**< array of demands */
4356  int capacity, /**< cumulative capacity */
4357  int hmin, /**< left bound of time axis to be considered (including hmin) */
4358  int hmax, /**< right bound of time axis to be considered (not including hmax) */
4359  SCIP_VAR* var, /**< variable to be considered for upper bound tightening */
4360  int duration, /**< duration of the job */
4361  int demand, /**< demand of the job */
4362  int est, /**< earliest start time of the job */
4363  int ect, /**< earliest completion time of the flexible part of the job */
4364  int lct, /**< latest completion time of the job */
4365  int begin, /**< begin of the time window under investigation */
4366  int end, /**< end of the time window under investigation */
4367  int energy, /**< available energy for the flexible part of the hob within the time window */
4368  int* bestlb, /**< pointer to strope the best lower bound change */
4369  int* inferinfos, /**< pointer to store the inference information which is need for the (best) lower bound change */
4370  SCIP_Bool* initialized, /**< was conflict analysis initialized */
4371  SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */
4372  SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */
4373  )
4374 {
4375  int newlb;
4376 
4377  assert(begin >= hmin);
4378  assert(end <= hmax);
4379 
4380  /* check if the time-table edge-finding should infer bounds */
4381  if( !conshdlrdata->ttefinfer )
4382  return SCIP_OKAY;
4383 
4384  /* if the job can be processed completely before or after the time window, nothing can be tightened */
4385  if( est >= end || ect <= begin )
4386  return SCIP_OKAY;
4387 
4388  /* if flexible part runs completely within the time window (assuming it is scheduled on its earliest start time), we
4389  * skip since the overload check will do the job
4390  */
4391  if( est >= begin && ect <= end )
4392  return SCIP_OKAY;
4393 
4394  /* check if the available energy in the time window is to small to handle the flexible part if it is schedule on its
4395  * earliest start time
4396  */
4397  if( energy >= demand * (MAX(begin, est) - MIN(end, ect)) )
4398  return SCIP_OKAY;
4399 
4400  /* adjust the available energy for the job; the given available energy assumes that the core of the considered job is
4401  * present; therefore, we need to add the core;
4402  *
4403  * @note the variable ect define the earliest completion time of the flexible part of the job; hence we need to
4404  * compute the earliest completion time of the (whole) job
4405  */
4406  energy += computeCoreWithInterval(begin, end, est + duration, lct - duration) * demand;
4407 
4408  /* compute a latest start time (upper bound) such that the job consums at most the available energy
4409  *
4410  * @note we can round down the compute duration w.r.t. the available energy
4411  */
4412  newlb = end - energy / demand;
4413 
4414  /* check if we detected an infeasibility which is the case if the new lower bound is larger than the current upper
4415  * bound (latest start time); meaning it is not possible to schedule the job
4416  */
4417  if( newlb > lct - duration )
4418  {
4419  /* initialize conflict analysis if conflict analysis is applicable */
4421  {
4422  SCIP_Real relaxedbd;
4423 
4424  assert(convertBoundToInt(scip, SCIPvarGetUbLocal(var)) < newlb);
4425 
4426  /* it is enough to overshoot the upper bound of the variable by one */
4427  relaxedbd = SCIPvarGetUbLocal(var) + 1.0;
4428 
4429  /* initialize conflict analysis */
4431 
4432  /* added to upper bound (which was overcut be new lower bound) of the variable */
4433  SCIP_CALL( SCIPaddConflictUb(scip, var, NULL) );
4434 
4435  /* analyze the infeasible */
4436  SCIP_CALL( analyzeEnergyRequirement(scip, nvars, vars, durations, demands, capacity,
4437  begin, end, var, SCIP_BOUNDTYPE_LOWER, NULL, relaxedbd, conshdlrdata->usebdwidening, explanation) );
4438 
4439  (*initialized) = TRUE;
4440  }
4441 
4442  (*cutoff) = TRUE;
4443  }
4444  else if( newlb > (*bestlb) )
4445  {
4446  INFERINFO inferinfo;
4447 
4448  assert(newlb > begin);
4449 
4450  inferinfo = getInferInfo(PROPRULE_3_TTEF, begin, end);
4451 
4452  /* construct inference information */
4453  (*inferinfos) = inferInfoToInt(inferinfo);
4454  (*bestlb) = newlb;
4455  }
4456 
4457  return SCIP_OKAY;
4458 }
4459 
4460 /** try to tighten the upper bound of the given variable */
4461 static
4463  SCIP* scip, /**< SCIP data structure */
4464  SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
4465  int nvars, /**< number of start time variables (activities) */
4466  SCIP_VAR** vars, /**< array of start time variables */
4467  int* durations, /**< array of durations */
4468  int* demands, /**< array of demands */
4469  int capacity, /**< cumulative capacity */
4470  int hmin, /**< left bound of time axis to be considered (including hmin) */
4471  int hmax, /**< right bound of time axis to be considered (not including hmax) */
4472  SCIP_VAR* var, /**< variable to be considered for upper bound tightening */
4473  int duration, /**< duration of the job */
4474  int demand, /**< demand of the job */
4475  int est, /**< earliest start time of the job */
4476  int lst, /**< latest start time of the flexible part of the job */
4477  int lct, /**< latest completion time of the job */
4478  int begin, /**< begin of the time window under investigation */
4479  int end, /**< end of the time window under investigation */
4480  int energy, /**< available energy for the flexible part of the hob within the time window */
4481  int* bestub, /**< pointer to strope the best upper bound change */
4482  int* inferinfos, /**< pointer to store the inference information which is need for the (best) upper bound change */
4483  SCIP_Bool* initialized, /**< was conflict analysis initialized */
4484  SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */
4485  SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */
4486  )
4487 {
4488  int newub;
4489 
4490  assert(begin >= hmin);
4491  assert(end <= hmax);
4492  assert(est < begin);
4493 
4494  /* check if the time-table edge-finding should infer bounds */
4495  if( !conshdlrdata->ttefinfer )
4496  return SCIP_OKAY;
4497 
4498  /* if flexible part of the job can be processed completely before or after the time window, nothing can be tightened */
4499  if( lst >= end || lct <= begin )
4500  return SCIP_OKAY;
4501 
4502  /* if flexible part runs completely within the time window (assuming it is scheduled on its latest start time), we
4503  * skip since the overload check will do the job
4504  */
4505  if( lst >= begin && lct <= end )
4506  return SCIP_OKAY;
4507 
4508  /* check if the available energy in the time window is to small to handle the flexible part of the job */
4509  if( energy >= demand * (MIN(end, lct) - MAX(begin, lst)) )
4510  return SCIP_OKAY;
4511 
4512  /* adjust the available energy for the job; the given available energy assumes that the core of the considered job is
4513  * present; therefore, we need to add the core;
4514  *
4515  * @note the variable lst define the latest start time of the flexible part of the job; hence we need to compute the
4516  * latest start of the (whole) job
4517  */
4518  energy += computeCoreWithInterval(begin, end, est + duration, lct - duration) * demand;
4519  assert(energy >= 0);
4520 
4521  /* compute a latest start time (upper bound) such that the job consums at most the available energy
4522  *
4523  * @note we can round down the compute duration w.r.t. the available energy
4524  */
4525  assert(demand > 0);
4526  newub = begin - duration + energy / demand;
4527 
4528  /* check if we detected an infeasibility which is the case if the new upper bound is smaller than the current lower
4529  * bound (earliest start time); meaning it is not possible to schedule the job
4530  */
4531  if( newub < est )
4532  {
4533  /* initialize conflict analysis if conflict analysis is applicable */
4535  {
4536  SCIP_Real relaxedbd;
4537 
4538  assert(convertBoundToInt(scip, SCIPvarGetLbLocal(var)) > newub);
4539 
4540  /* it is enough to undershoot the lower bound of the variable by one */
4541  relaxedbd = SCIPvarGetLbLocal(var) - 1.0;
4542 
4543  /* initialize conflict analysis */
4545 
4546  /* added to lower bound (which was undercut be new upper bound) of the variable */
4547  SCIP_CALL( SCIPaddConflictLb(scip, var, NULL) );
4548 
4549  /* analyze the infeasible */
4550  SCIP_CALL( analyzeEnergyRequirement(scip, nvars, vars, durations, demands, capacity,
4551  begin, end, var, SCIP_BOUNDTYPE_UPPER, NULL, relaxedbd, conshdlrdata->usebdwidening, explanation) );
4552 
4553  (*initialized) = TRUE;
4554  }
4555 
4556  (*cutoff) = TRUE;
4557  }
4558  else if( newub < (*bestub) )
4559  {
4560  INFERINFO inferinfo;
4561 
4562  assert(newub < begin);
4563 
4564  inferinfo = getInferInfo(PROPRULE_3_TTEF, begin, end);
4565 
4566  /* construct inference information */
4567  (*inferinfos) = inferInfoToInt(inferinfo);
4568  (*bestub) = newub;
4569  }
4570 
4571  return SCIP_OKAY;
4572 }
4573 
4574 /** propagate the upper bounds and "opportunistically" the lower bounds using the time-table edge-finding algorithm */
4575 static
4577  SCIP* scip, /**< SCIP data structure */
4578  SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
4579  int nvars, /**< number of start time variables (activities) */
4580  SCIP_VAR** vars, /**< array of start time variables */
4581  int* durations, /**< array of durations */
4582  int* demands, /**< array of demands */
4583  int capacity, /**< cumulative capacity */
4584  int hmin, /**< left bound of time axis to be considered (including hmin) */
4585  int hmax, /**< right bound of time axis to be considered (not including hmax) */
4586  int* newlbs, /**< array to buffer new lower bounds */
4587  int* newubs, /**< array to buffer new upper bounds */
4588  int* lbinferinfos, /**< array to store the inference information for the lower bound changes */
4589  int* ubinferinfos, /**< array to store the inference information for the upper bound changes */
4590  int* lsts, /**< array of latest start time of the flexible part in the same order as the variables */
4591  int* flexenergies, /**< array of flexible energies in the same order as the variables */
4592  int* perm, /**< permutation of the variables w.r.t. the non-decreasing order of the earliest start times */
4593  int* ests, /**< array with earliest strart times sorted in non-decreasing order */
4594  int* lcts, /**< array with latest completion times sorted in non-decreasing order */
4595  int* coreEnergyAfterEst, /**< core energy after the earliest start times */
4596  int* coreEnergyAfterLct, /**< core energy after the latest completion times */
4597  SCIP_Bool* initialized, /**< was conflict analysis initialized */
4598  SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */
4599  SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */
4600  )
4601 {
4602  int coreEnergyAfterEnd;
4603  int maxavailable;
4604  int minavailable;
4605  int totalenergy;
4606  int nests;
4607  int est;
4608  int lct;
4609  int start;
4610  int end;
4611  int v;
4612 
4613  est = INT_MAX;
4614  lct = INT_MIN;
4615 
4616  /* compute earliest start and latest completion time of all jobs */
4617  for( v = 0; v < nvars; ++v )
4618  {
4619  start = convertBoundToInt(scip, SCIPvarGetLbLocal(vars[v]));
4620  end = convertBoundToInt(scip, SCIPvarGetUbLocal(vars[v])) + durations[v];
4621 
4622  est = MIN(est, start);
4623  lct = MAX(lct, end);
4624  }
4625 
4626  /* adjust the effective time horizon */
4627  hmin = MAX(hmin, est);
4628  hmax = MIN(hmax, lct);
4629 
4630  end = hmax + 1;
4631  coreEnergyAfterEnd = -1;
4632 
4633  maxavailable = (hmax - hmin) * capacity;
4634  minavailable = maxavailable;
4635  totalenergy = computeTotalEnergy(durations, demands, nvars);
4636 
4637  /* check if the smallest interval has a size such that the total energy fits, if so we can skip the propagator */
4638  if( (lcts[0] - ests[nvars-1]) * capacity >= totalenergy )
4639  return SCIP_OKAY;
4640 
4641  nests = nvars;
4642 
4643  /* loop over all variable in non-increasing order w.r.t. the latest completion time; thereby, the latest completion
4644  * times define the end of the time interval under investigation
4645  */
4646  for( v = nvars-1; v >= 0 && !(*cutoff); --v )
4647  {
4648  int flexenergy;
4649  int minbegin;
4650  int lbenergy;
4651  int lbcand;
4652  int i;
4653 
4654  lct = lcts[v];
4655 
4656  /* if the latest completion time is larger then hmax an infeasibility cannot be detected, since after hmax an
4657  * infinity capacity is available; hence we skip that
4658  */
4659  if( lct > hmax )
4660  continue;
4661 
4662  /* if the latest completion time is smaller then hmin we have to stop */
4663  if( lct <= hmin )
4664  {
4665  assert(v == 0 || lcts[v-1] <= lcts[v]);
4666  break;
4667  }
4668 
4669  /* if the latest completion time equals to previous end time, we can continue since this particular interval
4670  * induced by end was just analyzed
4671  */
4672  if( lct == end )
4673  continue;
4674 
4675  assert(lct < end);
4676 
4677  /* In case we only want to detect an overload (meaning no bound propagation) we can skip the interval; this is
4678  * the case if the free energy (the energy which is not occupied by any core) is smaller than the previous minimum
4679  * free energy; if so it means that in the next iterate the free-energy cannot be negative
4680  */
4681  if( !conshdlrdata->ttefinfer && end <= hmax && minavailable < maxavailable )
4682  {
4683  int freeenergy;
4684 
4685  assert(coreEnergyAfterLct[v] >= coreEnergyAfterEnd);
4686  assert(coreEnergyAfterEnd >= 0);
4687 
4688  /* compute the energy which is not consumed by the cores with in the interval [lct, end) */
4689  freeenergy = capacity * (end - lct) - coreEnergyAfterLct[v] + coreEnergyAfterEnd;
4690 
4691  if( freeenergy <= minavailable )
4692  {
4693  SCIPdebugMessage("skip latest completion time <%d> (minimum available energy <%d>, free energy <%d>)\n", lct, minavailable, freeenergy);
4694  continue;
4695  }
4696  }
4697 
4698  SCIPdebugMessage("check intervals ending with <%d>\n", lct);
4699 
4700  end = lct;
4701  coreEnergyAfterEnd = coreEnergyAfterLct[v];
4702 
4703  flexenergy = 0;
4704  minavailable = maxavailable;
4705  minbegin = hmax;
4706  lbcand = -1;
4707  lbenergy = 0;
4708 
4709  /* loop over the job in non-increasing order w.r.t. the earliest start time; these earliest start time are
4710  * defining the beginning of the time interval under investigation; Thereby, the time interval gets wider and
4711  * wider
4712  */
4713  for( i = nests-1; i >= 0; --i )
4714  {
4715  SCIP_VAR* var;
4716  int freeenergy;
4717  int duration;
4718  int demand;
4719  int begin;
4720  int idx;
4721  int lst;
4722 
4723  idx = perm[i];
4724  assert(idx >= 0);
4725  assert(idx < nvars);
4726  assert(!(*cutoff));
4727 
4728  /* the earliest start time of the job */
4729  est = ests[i];
4730 
4731  /* if the job starts after the current end, we can skip it and do not need to consider it again since the
4732  * latest completion times (which define end) are scant in non-increasing order
4733  */
4734  if( end <= est )
4735  {
4736  nests--;
4737  continue;
4738  }
4739 
4740  /* check if the interval has a size such that the total energy fits, if so we can skip all intervals with the
4741  * current ending time
4742  */
4743  if( (end - est) * capacity >= totalenergy )
4744  break;
4745 
4746  var = vars[idx];
4747  assert(var != NULL);
4748 
4749  duration = durations[idx];
4750  assert(duration > 0);
4751 
4752  demand = demands[idx];
4753  assert(demand > 0);
4754 
4755  lct = convertBoundToInt(scip, SCIPvarGetUbLocal(var)) + duration;
4756 
4757  /* the latest start time of the free part of the job */
4758  lst = lsts[idx];
4759 
4760  /* in case the earliest start time is equal to minbegin, the job lies completely within the time window under
4761  * investigation; hence the overload check will do the the job
4762  */
4763  assert(est <= minbegin);
4764  if( minavailable < maxavailable && est < minbegin )
4765  {
4766  assert(!(*cutoff));
4767 
4768  /* try to tighten the upper bound */
4769  SCIP_CALL( tightenUbTTEF(scip, conshdlrdata, nvars, vars, durations, demands, capacity, hmin, hmax,
4770  var, duration, demand, est, lst, lct, minbegin, end, minavailable, &(newubs[idx]), &(ubinferinfos[idx]),
4771  initialized, explanation, cutoff) );
4772 
4773  if( *cutoff )
4774  break;
4775  }
4776 
4777  SCIPdebugMessage("check variable <%s>[%g,%g] (duration %d, demands %d, est <%d>, lst of free part <%d>\n",
4778  SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var), duration, demand, est, lst);
4779 
4780  begin = est;
4781  assert(convertBoundToInt(scip, SCIPvarGetLbLocal(var)) == est);
4782 
4783  /* if the earliest start time is smaller than hmin we can stop here since the next job will not decrease the
4784  * free energy
4785  */
4786  if( begin < hmin )
4787  break;
4788 
4789  /* compute the contribution to the flexible energy */
4790  if( lct <= end )
4791  {
4792  /* if the jobs has to finish before the end, all the energy has to be scheduled */
4793  assert(lst >= begin);
4794  assert(flexenergies[idx] >= 0);
4795  flexenergy += flexenergies[idx];
4796  }
4797  else
4798  {
4799  /* the job partly overlaps with the end */
4800  int candenergy;
4801  int energy;
4802 
4803  /* compute the flexible energy which is part of the time interval for sure if the job is scheduled
4804  * w.r.t. latest start time
4805  *
4806  * @note we need to be aware of the effective horizon
4807  */
4808  energy = MIN(flexenergies[idx], demands[idx] * MAX(0, (end - lst)));
4809  assert(end - lst < duration);
4810  assert(energy >= 0);
4811 
4812  /* adjust the flexible energy of the time interval */
4813  flexenergy += energy;
4814 
4815  /* compute the flexible energy of the job which is not part of flexible energy of the time interval */
4816  candenergy = MIN(flexenergies[idx], demands[idx] * (end - begin)) - energy;
4817  assert(candenergy >= 0);
4818 
4819  /* check if we found a better candidate */
4820  if( candenergy > lbenergy )
4821  {
4822  lbenergy = candenergy;
4823  lbcand = idx;
4824  }
4825  }
4826 
4827  SCIPdebugMessage("time window [%d,%d) flexible energy <%d>\n", begin, end, flexenergy);
4828  assert(coreEnergyAfterEst[i] >= coreEnergyAfterEnd);
4829 
4830  /* compute the energy which is not used yet */
4831  freeenergy = capacity * (end - begin) - flexenergy - coreEnergyAfterEst[i] + coreEnergyAfterEnd;
4832 
4833  /* check overload */
4834  if( freeenergy < 0 )
4835  {
4836  SCIPdebugMessage("analyze overload within time window [%d,%d) capacity %d\n", begin, end, capacity);
4837 
4838  /* initialize conflict analysis if conflict analysis is applicable */
4840  {
4841  /* analyze infeasibilty */
4843 
4844  SCIP_CALL( analyzeEnergyRequirement(scip, nvars, vars, durations, demands, capacity,
4845  begin, end, NULL, SCIP_BOUNDTYPE_UPPER, NULL, SCIP_UNKNOWN,
4846  conshdlrdata->usebdwidening, explanation) );
4847 
4848  (*initialized) = TRUE;
4849  }
4850 
4851  (*cutoff) = TRUE;
4852 
4853  /* for the statistic we count the number of times a cutoff was detected due the time-time-edge-finding */
4854  SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->ncutoffoverloadTTEF++ );
4855 
4856  break;
4857  }
4858 
4859  /* check if the available energy is not sufficent to schedule the flexible energy of the best candidate job */
4860  if( lbenergy > 0 && freeenergy < lbenergy )
4861  {
4862  int energy;
4863  int newlb;
4864  int ect;
4865 
4866  ect = convertBoundToInt(scip, SCIPvarGetLbLocal(vars[lbcand])) + durations[lbcand];
4867  lst = convertBoundToInt(scip, SCIPvarGetUbLocal(vars[lbcand]));
4868 
4869  /* remove the energy of our job from the ... */
4870  energy = freeenergy + (computeCoreWithInterval(begin, end, ect, lst) + MAX(0, end - lsts[lbcand])) * demands[lbcand];
4871 
4872  newlb = end - (int)(energy / demands[lbcand]);
4873 
4874  if( newlb > lst )
4875  {
4876  /* initialize conflict analysis if conflict analysis is applicable */
4878  {
4879  SCIP_Real relaxedbd;
4880 
4881  /* analyze infeasibilty */
4883 
4884  relaxedbd = lst + 1.0;
4885 
4886  /* added to upper bound (which was overcut be new lower bound) of the variable */
4887  SCIP_CALL( SCIPaddConflictUb(scip, vars[lbcand], NULL) );
4888 
4889  SCIP_CALL( analyzeEnergyRequirement(scip, nvars, vars, durations, demands, capacity,
4890  begin, end, vars[lbcand], SCIP_BOUNDTYPE_LOWER, NULL, relaxedbd,
4891  conshdlrdata->usebdwidening, explanation) );
4892 
4893  (*initialized) = TRUE;
4894  }
4895 
4896  (*cutoff) = TRUE;
4897  break;
4898  }
4899  else if( newlb > newlbs[lbcand] )
4900  {
4901  INFERINFO inferinfo;
4902 
4903  /* construct inference information */
4904  inferinfo = getInferInfo(PROPRULE_3_TTEF, begin, end);
4905 
4906  /* buffer upper bound change */
4907  lbinferinfos[lbcand] = inferInfoToInt(inferinfo);
4908  newlbs[lbcand] = newlb;
4909  }
4910  }
4911 
4912  /* check if the current interval has a smaller free energy */
4913  if( minavailable > freeenergy )
4914  {
4915  minavailable = freeenergy;
4916  minbegin = begin;
4917  }
4918  assert(minavailable >= 0);
4919  }
4920  }
4921 
4922  return SCIP_OKAY;
4923 }
4924 
4925 /** propagate the lower bounds and "opportunistically" the upper bounds using the time-table edge-finding algorithm */
4926 static
4928  SCIP* scip, /**< SCIP data structure */
4929  SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
4930  int nvars, /**< number of start time variables (activities) */
4931  SCIP_VAR** vars, /**< array of start time variables */
4932  int* durations, /**< array of durations */
4933  int* demands, /**< array of demands */
4934  int capacity, /**< cumulative capacity */
4935  int hmin, /**< left bound of time axis to be considered (including hmin) */
4936  int hmax, /**< right bound of time axis to be considered (not including hmax) */
4937  int* newlbs, /**< array to buffer new lower bounds */
4938  int* newubs, /**< array to buffer new upper bounds */
4939  int* lbinferinfos, /**< array to store the inference information for the lower bound changes */
4940  int* ubinferinfos, /**< array to store the inference information for the upper bound changes */
4941  int* ects, /**< array of earliest completion time of the flexible part in the same order as the variables */
4942  int* flexenergies, /**< array of flexible energies in the same order as the variables */
4943  int* perm, /**< permutation of the variables w.r.t. the non-decreasing order of the latest completion times */
4944  int* ests, /**< array with earliest strart times sorted in non-decreasing order */
4945  int* lcts, /**< array with latest completion times sorted in non-decreasing order */
4946  int* coreEnergyAfterEst, /**< core energy after the earliest start times */
4947  int* coreEnergyAfterLct, /**< core energy after the latest completion times */
4948  SCIP_Bool* initialized, /**< was conflict analysis initialized */
4949  SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */
4950  SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */
4951  )
4952 {
4953  int coreEnergyAfterStart;
4954  int maxavailable;
4955  int minavailable;
4956  int totalenergy;
4957  int nlcts;
4958  int begin;
4959  int minest;
4960  int maxlct;
4961  int start;
4962  int end;
4963  int v;
4964 
4965  if( *cutoff )
4966  return SCIP_OKAY;
4967 
4968  begin = hmin - 1;
4969  coreEnergyAfterStart = -1;
4970 
4971  minest = INT_MAX;
4972  maxlct = INT_MIN;
4973 
4974  /* compute earliest start and latest completion time of all jobs */
4975  for( v = 0; v < nvars; ++v )
4976  {
4977  start = convertBoundToInt(scip, SCIPvarGetLbLocal(vars[v]));
4978  end = convertBoundToInt(scip, SCIPvarGetUbLocal(vars[v])) + durations[v];
4979 
4980  minest = MIN(minest, start);
4981  maxlct = MAX(maxlct, end);
4982  }
4983 
4984  /* adjust the effective time horizon */
4985  hmin = MAX(hmin, minest);
4986  hmax = MIN(hmax, maxlct);
4987 
4988  maxavailable = (hmax - hmin) * capacity;
4989  minavailable = maxavailable;
4990  totalenergy = computeTotalEnergy(durations, demands, nvars);
4991 
4992  /* check if the smallest interval has a size such that the total energy fits, if so we can skip the propagator */
4993  if( (lcts[0] - ests[nvars-1]) * capacity >= totalenergy )
4994  return SCIP_OKAY;
4995 
4996  nlcts = 0;
4997 
4998  /* loop over all variable in non-decreasing order w.r.t. the earliest start times; thereby, the earliest start times
4999  * define the start of the time interval under investigation
5000  */
5001  for( v = 0; v < nvars; ++v )
5002  {
5003  int flexenergy;
5004  int minend;
5005  int ubenergy;
5006  int ubcand;
5007  int est;
5008  int i;
5009 
5010  est = ests[v];
5011 
5012  /* if the earliest start time is smaller then hmin an infeasibility cannot be detected, since before hmin an
5013  * infinity capacity is available; hence we skip that
5014  */
5015  if( est < hmin )
5016  continue;
5017 
5018  /* if the earliest start time is larger or equal then hmax we have to stop */
5019  if( est >= hmax )
5020  break;
5021 
5022  /* if the latest earliest start time equals to previous start time, we can continue since this particular interval
5023  * induced by start was just analyzed
5024  */
5025  if( est == begin )
5026  continue;
5027 
5028  assert(est > begin);
5029 
5030  SCIPdebugMessage("check intervals starting with <%d>\n", est);
5031 
5032  begin = est;
5033  coreEnergyAfterStart = coreEnergyAfterEst[v];
5034 
5035  flexenergy = 0;
5036  minavailable = maxavailable;
5037  minend = hmin;
5038  ubcand = -1;
5039  ubenergy = 0;
5040 
5041  /* loop over the job in non-decreasing order w.r.t. the latest completion time; these latest completion times are
5042  * defining the ending of the time interval under investigation; thereby, the time interval gets wider and wider
5043  */
5044  for( i = nlcts; i < nvars; ++i )
5045  {
5046  SCIP_VAR* var;
5047  int freeenergy;
5048  int duration;
5049  int demand;
5050  int idx;
5051  int lct;
5052  int ect;
5053 
5054  idx = perm[i];
5055  assert(idx >= 0);
5056  assert(idx < nvars);
5057  assert(!(*cutoff));
5058 
5059  /* the earliest start time of the job */
5060  lct = lcts[i];
5061 
5062  /* if the job has a latest completion time before the the current start, we can skip it and do not need to
5063  * consider it again since the earliest start times (which define the start) are scant in non-decreasing order
5064  */
5065  if( lct <= begin )
5066  {
5067  nlcts++;
5068  continue;
5069  }
5070 
5071  /* check if the interval has a size such that the total energy fits, if so we can skip all intervals which
5072  * start with current beginning time
5073  */
5074  if( (lct - begin) * capacity >= totalenergy )
5075  break;
5076 
5077  var = vars[idx];
5078  assert(var != NULL);
5079 
5080  duration = durations[idx];
5081  assert(duration > 0);
5082 
5083  demand = demands[idx];
5084  assert(demand > 0);
5085 
5086  est = convertBoundToInt(scip, SCIPvarGetLbLocal(var));
5087 
5088  /* the earliest completion time of the flexible part of the job */
5089  ect = ects[idx];
5090 
5091  /* in case the latest completion time is equal to minend, the job lies completely within the time window under
5092  * investigation; hence the overload check will do the the job
5093  */
5094  assert(lct >= minend);
5095  if( minavailable < maxavailable && lct > minend )
5096  {
5097  assert(!(*cutoff));
5098 
5099  /* try to tighten the upper bound */
5100  SCIP_CALL( tightenLbTTEF(scip, conshdlrdata, nvars, vars, durations, demands, capacity, hmin, hmax,
5101  var, duration, demand, est, ect, lct, begin, minend, minavailable, &(newlbs[idx]), &(lbinferinfos[idx]),
5102  initialized, explanation, cutoff) );
5103 
5104  if( *cutoff )
5105  return SCIP_OKAY;
5106  }
5107 
5108  SCIPdebugMessage("check variable <%s>[%g,%g] (duration %d, demands %d, est <%d>, ect of free part <%d>\n",
5109  SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var), duration, demand, est, ect);
5110 
5111  end = lct;
5112  assert(convertBoundToInt(scip, SCIPvarGetUbLocal(var)) + duration == lct);
5113 
5114  /* if the latest completion time is larger than hmax we can stop here since the next job will not decrease the
5115  * free energy
5116  */
5117  if( end > hmax )
5118  break;
5119 
5120  /* compute the contribution to the flexible energy */
5121  if( est >= begin )
5122  {
5123  /* if the jobs has to finish before the end, all the energy has to be scheduled */
5124  assert(ect <= end);
5125  assert(flexenergies[idx] >= 0);
5126  flexenergy += flexenergies[idx];
5127  }
5128  else
5129  {
5130  /* the job partly overlaps with the end */
5131  int candenergy;
5132  int energy;
5133 
5134  /* compute the flexible energy which is part of the time interval for sure if the job is scheduled
5135  * w.r.t. latest start time
5136  *
5137  * @note we need to be aware of the effective horizon
5138  */
5139  energy = MIN(flexenergies[idx], demands[idx] * MAX(0, (ect - begin)));
5140  assert(ect - begin < duration);
5141  assert(energy >= 0);
5142 
5143  /* adjust the flexible energy of the time interval */
5144  flexenergy += energy;
5145 
5146  /* compute the flexible energy of the job which is not part of flexible energy of the time interval */
5147  candenergy = MIN(flexenergies[idx], demands[idx] * (end - begin)) - energy;
5148  assert(candenergy >= 0);
5149 
5150  /* check if we found a better candidate */
5151  if( candenergy > ubenergy )
5152  {
5153  ubenergy = candenergy;
5154  ubcand = idx;
5155  }
5156  }
5157 
5158  SCIPdebugMessage("time window [%d,%d) flexible energy <%d>\n", begin, end, flexenergy);
5159  assert(coreEnergyAfterLct[i] <= coreEnergyAfterStart);
5160 
5161  /* compute the energy which is not used yet */
5162  freeenergy = capacity * (end - begin) - flexenergy - coreEnergyAfterStart + coreEnergyAfterLct[i];
5163 
5164  /* check overload */
5165  if( freeenergy < 0 )
5166  {
5167  SCIPdebugMessage("analyze overload within time window [%d,%d) capacity %d\n", begin, end, capacity);
5168 
5169  /* initialize conflict analysis if conflict analysis is applicable */
5171  {
5172  /* analyze infeasibilty */
5174 
5175  SCIP_CALL( analyzeEnergyRequirement(scip, nvars, vars, durations, demands, capacity,
5176  begin, end, NULL, SCIP_BOUNDTYPE_UPPER, NULL, SCIP_UNKNOWN,
5177  conshdlrdata->usebdwidening, explanation) );
5178 
5179  (*initialized) = TRUE;
5180  }
5181 
5182  (*cutoff) = TRUE;
5183 
5184  /* for the statistic we count the number of times a cutoff was detected due the time-time-edge-finding */
5185  SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->ncutoffoverloadTTEF++ );
5186 
5187  return SCIP_OKAY;
5188  }
5189 
5190  /* check if the available energy is not sufficent to schedule the flexible energy of the best candidate job */
5191  if( ubenergy > 0 && freeenergy < ubenergy )
5192  {
5193  int energy;
5194  int newub;
5195  int lst;
5196 
5197  duration = durations[ubcand];
5198  assert(duration > 0);
5199 
5200  ect = convertBoundToInt(scip, SCIPvarGetLbLocal(vars[ubcand])) + duration;
5201  lst = convertBoundToInt(scip, SCIPvarGetUbLocal(vars[ubcand]));
5202 
5203  /* remove the energy of our job from the ... */
5204  energy = freeenergy + (computeCoreWithInterval(begin, end, ect, lst) + MAX(0, ects[ubcand] - begin)) * demands[ubcand];
5205 
5206  newub = begin - duration + (int)(energy / demands[ubcand]);
5207 
5208  if( newub < ect - duration )
5209  {
5210  /* initialize conflict analysis if conflict analysis is applicable */
5212  {
5213  SCIP_Real relaxedbd;
5214  /* analyze infeasibilty */
5216 
5217  relaxedbd = ect - duration - 1.0;
5218 
5219  /* added to lower bound (which was undercut be new upper bound) of the variable */
5220  SCIP_CALL( SCIPaddConflictUb(scip, vars[ubcand], NULL) );
5221 
5222  SCIP_CALL( analyzeEnergyRequirement(scip, nvars, vars, durations, demands, capacity,
5223  begin, end, vars[ubcand], SCIP_BOUNDTYPE_UPPER, NULL, relaxedbd,
5224  conshdlrdata->usebdwidening, explanation) );
5225 
5226  (*initialized) = TRUE;
5227  }
5228 
5229  (*cutoff) = TRUE;
5230  return SCIP_OKAY;
5231  }
5232  else if( newub < newubs[ubcand] )
5233  {
5234  INFERINFO inferinfo;
5235 
5236  /* construct inference information */
5237  inferinfo = getInferInfo(PROPRULE_3_TTEF, begin, end);
5238 
5239  /* buffer upper bound change */
5240  ubinferinfos[ubcand] = inferInfoToInt(inferinfo);
5241  newubs[ubcand] = newub;
5242  }
5243  }
5244 
5245  /* check if the current interval has a smaller free energy */
5246  if( minavailable > freeenergy )
5247  {
5248  minavailable = freeenergy;
5249  minend = end;
5250  }
5251  assert(minavailable >= 0);
5252  }
5253  }
5254 
5255  return SCIP_OKAY;
5256 }
5257 
5258 /** checks whether the instance is infeasible due to a overload within a certain time frame using the idea of time-table
5259  * edge-finding
5260  *
5261  * @note The algorithm is based on the following two papers:
5262  * - Petr Vilim, "Timetable Edge Finding Filtering Algorithm for Discrete Cumulative Resources", In: Tobias
5263  * Achterberg and J. Christopher Beck (Eds.), Integration of AI and OR Techniques in Constraint Programming for
5264  * Combinatorial Optimization Problems (CPAIOR 2011), LNCS 6697, pp 230--245
5265  * - Andreas Schutt, Thibaut Feydy, and Peter J. Stuckey, "Explaining Time-Table-Edge-Finding Propagation for the
5266  * Cumulative Resource Constraint (submitted to CPAIOR 2013)
5267  */
5268 static
5270  SCIP* scip, /**< SCIP data structure */
5271  SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
5272  SCIP_PROFILE* profile, /**< current core profile */
5273  int nvars, /**< number of start time variables (activities) */
5274  SCIP_VAR** vars, /**< array of start time variables */
5275  int* durations, /**< array of durations */
5276  int* demands, /**< array of demands */
5277  int capacity, /**< cumulative capacity */
5278  int hmin, /**< left bound of time axis to be considered (including hmin) */
5279  int hmax, /**< right bound of time axis to be considered (not including hmax) */
5280  SCIP_CONS* cons, /**< constraint which is propagated (needed to SCIPinferVar**Cons()) */
5281  int* nchgbds, /**< pointer to store the number of bound changes */
5282  SCIP_Bool* initialized, /**< was conflict analysis initialized */
5283  SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */
5284  SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */
5285  )
5286 {
5287  int* coreEnergyAfterEst;
5288  int* coreEnergyAfterLct;
5289  int* flexenergies;
5290  int* permests;
5291  int* permlcts;
5292  int* lcts;
5293  int* ests;
5294  int* ects;
5295  int* lsts;
5296 
5297  int* newlbs;
5298  int* newubs;
5299  int* lbinferinfos;
5300  int* ubinferinfos;
5301 
5302  int v;
5303 
5304  /* check if a cutoff was already detected */
5305  if( (*cutoff) )
5306  return SCIP_OKAY;
5307 
5308  /* check if at least the basic overload checking should be perfomed */
5309  if( !conshdlrdata->ttefcheck )
5310  return SCIP_OKAY;
5311 
5312  SCIPdebugMessage("run time-table edge-finding overload checking\n");
5313 
5314  SCIP_CALL( SCIPallocBufferArray(scip, &coreEnergyAfterEst, nvars) );
5315  SCIP_CALL( SCIPallocBufferArray(scip, &coreEnergyAfterLct, nvars) );
5316  SCIP_CALL( SCIPallocBufferArray(scip, &flexenergies, nvars) );
5317  SCIP_CALL( SCIPallocBufferArray(scip, &permlcts, nvars) );
5318  SCIP_CALL( SCIPallocBufferArray(scip, &permests, nvars) );
5319  SCIP_CALL( SCIPallocBufferArray(scip, &lcts, nvars) );
5320  SCIP_CALL( SCIPallocBufferArray(scip, &ests, nvars) );
5321  SCIP_CALL( SCIPallocBufferArray(scip, &ects, nvars) );
5322  SCIP_CALL( SCIPallocBufferArray(scip, &lsts, nvars) );
5323 
5324  SCIP_CALL( SCIPallocBufferArray(scip, &newlbs, nvars) );
5325  SCIP_CALL( SCIPallocBufferArray(scip, &newubs, nvars) );
5326  SCIP_CALL( SCIPallocBufferArray(scip, &lbinferinfos, nvars) );
5327  SCIP_CALL( SCIPallocBufferArray(scip, &ubinferinfos, nvars) );
5328 
5329  /* we need to buffer the bound changes since the propagation algorithm cannot handle new bound dynamically */
5330  for( v = 0; v < nvars; ++v )
5331  {
5332  newlbs[v] = convertBoundToInt(scip, SCIPvarGetLbLocal(vars[v]));
5333  newubs[v] = convertBoundToInt(scip, SCIPvarGetUbLocal(vars[v]));
5334  lbinferinfos[v] = 0;
5335  ubinferinfos[v] = 0;
5336  }
5337 
5338  /* collect earliest start times, latest completion time, and free energy contributions */
5339  collectDataTTEF(scip, nvars, vars, durations, demands, hmin, hmax, permests, ests, permlcts, lcts, ects, lsts, flexenergies);
5340 
5341  /* sort the earliest start times and latest completion in non-decreasing order */
5342  SCIPsortIntInt(ests, permests, nvars);
5343  SCIPsortIntInt(lcts, permlcts, nvars);
5344 
5345  /* compute for the different earliest start and latest completion time the core energy of the corresponding time
5346  * points
5347  */
5348  SCIP_CALL( computeCoreEngeryAfter(scip, profile, nvars, ests, lcts, coreEnergyAfterEst, coreEnergyAfterLct) );
5349 
5350  /* propagate the upper bounds and "opportunistically" the lower bounds */
5351  SCIP_CALL( propagateUbTTEF(scip, conshdlrdata, nvars, vars, durations, demands, capacity, hmin, hmax,
5352  newlbs, newubs, lbinferinfos, ubinferinfos, lsts, flexenergies,
5353  permests, ests, lcts, coreEnergyAfterEst, coreEnergyAfterLct, initialized, explanation, cutoff) );
5354 
5355  /* propagate the lower bounds and "opportunistically" the upper bounds */
5356  SCIP_CALL( propagateLbTTEF(scip, conshdlrdata, nvars, vars, durations, demands, capacity, hmin, hmax,
5357  newlbs, newubs, lbinferinfos, ubinferinfos, ects, flexenergies,
5358  permlcts, ests, lcts, coreEnergyAfterEst, coreEnergyAfterLct, initialized, explanation, cutoff) );
5359 
5360  /* apply the buffer bound changes */
5361  for( v = 0; v < nvars && !(*cutoff); ++v )
5362  {
5363  SCIP_Bool infeasible;
5364  SCIP_Bool tightened;
5365 
5366  SCIP_CALL( SCIPinferVarLbCons(scip, vars[v], (SCIP_Real)newlbs[v], cons, lbinferinfos[v], TRUE, &infeasible, &tightened) );
5367 
5368  /* since we change first the lower bound of the variable an infeasibilty should be detected */
5369  assert(!infeasible);
5370 
5371  if( tightened )
5372  {
5373  (*nchgbds)++;
5374 
5375  /* for the statistic we count the number of times a cutoff was detected due the time-time */
5377  }
5378 
5379 
5380  SCIP_CALL( SCIPinferVarUbCons(scip, vars[v], (SCIP_Real)newubs[v], cons, ubinferinfos[v], TRUE, &infeasible, &tightened) );
5381 
5382  /* since upper bound was compute w.r.t. the "old" bound the previous lower bound update together with this upper
5383  * bound update can be infeasible
5384  */
5385  if( infeasible )
5386  {
5388  {
5389  INFERINFO inferinfo;
5390  SCIP_VAR* var;
5391  int begin;
5392  int end;
5393 
5394  var = vars[v];
5395  assert(var != NULL);
5396 
5397  /* initialize conflict analysis */
5399 
5400  /* convert int to inference information */
5401  inferinfo = intToInferInfo(ubinferinfos[v]);
5402 
5403  /* collect time window from inference information */
5404  begin = inferInfoGetData1(inferinfo);
5405  end = inferInfoGetData2(inferinfo);
5406  assert(begin < end);
5407 
5408  /* added to lower bound (which was undercut be new upper bound) of the variable */
5409  SCIP_CALL( SCIPaddConflictLb(scip, var, NULL) );
5410 
5411  /* analysis the upper bound change */
5412  SCIP_CALL( analyzeEnergyRequirement(scip, nvars, vars, durations, demands, capacity,
5413  begin, end, var, SCIP_BOUNDTYPE_UPPER, NULL, SCIPvarGetLbLocal(vars[v]) - 1.0,
5414  conshdlrdata->usebdwidening, explanation) );
5415 
5416  (*initialized) = TRUE;
5417  }
5418 
5419  /* for the statistic we count the number of times a cutoff was detected due the time-time */
5420  SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->ncutoffoverloadTTEF++ );
5421 
5422  (*cutoff) = TRUE;
5423  break;
5424  }
5425 
5426  if( tightened )
5427  {
5428  (*nchgbds)++;
5429 
5430  /* for the statistic we count the number of times a cutoff was detected due the time-time */
5432  }
5433  }
5434 
5435  SCIPfreeBufferArray(scip, &ubinferinfos);
5436  SCIPfreeBufferArray(scip, &lbinferinfos);
5437  SCIPfreeBufferArray(scip, &newubs);
5438  SCIPfreeBufferArray(scip, &newlbs);
5439 
5440  /* free buffer arrays */
5441  SCIPfreeBufferArray(scip, &lsts);
5442  SCIPfreeBufferArray(scip, &ects);
5443  SCIPfreeBufferArray(scip, &ests);
5444  SCIPfreeBufferArray(scip, &lcts);
5445  SCIPfreeBufferArray(scip, &permests);
5446  SCIPfreeBufferArray(scip, &permlcts);
5447  SCIPfreeBufferArray(scip, &flexenergies);
5448  SCIPfreeBufferArray(scip, &coreEnergyAfterLct);
5449  SCIPfreeBufferArray(scip, &coreEnergyAfterEst);
5450 
5451  return SCIP_OKAY;
5452 }
5453 
5454 /** a cumulative condition is not satisfied if its capacity is exceeded at a time where jobs cannot be shifted (core)
5455  * anymore we build up a cumulative profile of all cores of jobs and try to improve bounds of all jobs; also known as
5456  * time table propagator
5457  */
5458 static
5460  SCIP* scip, /**< SCIP data structure */
5461  SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
5462  SCIP_PROFILE* profile, /**< core profile */
5463  int nvars, /**< number of start time variables (activities) */
5464  SCIP_VAR** vars, /**< array of start time variables */
5465  int* durations, /**< array of durations */
5466  int* demands, /**< array of demands */
5467  int capacity, /**< cumulative capacity */
5468  int hmin, /**< left bound of time axis to be considered (including hmin) */
5469  int hmax, /**< right bound of time axis to be considered (not including hmax) */
5470  SCIP_CONS* cons, /**< constraint which is propagated (needed to SCIPinferVar**Cons()) */
5471  int* nchgbds, /**< pointer to store the number of bound changes */
5472  SCIP_Bool* initialized, /**< was conflict analysis initialized */
5473  SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */
5474  SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */
5475  )
5476 {
5477  SCIP_Bool infeasible;
5478  int v;
5479 
5480  assert(scip != NULL);
5481  assert(nvars > 0);
5482  assert(cons != NULL);
5483  assert(cutoff != NULL);
5484 
5485  /* check if already a cutoff was detected */
5486  if( (*cutoff) )
5487  return SCIP_OKAY;
5488 
5489  /* check if the time tabling should infer bounds */
5490  if( !conshdlrdata->ttinfer )
5491  return SCIP_OKAY;
5492 
5493  assert(*initialized == FALSE);
5494 
5495  SCIPdebugMessage("propagate cores of cumulative condition of constraint <%s>[%d,%d) <= %d\n",
5496  SCIPconsGetName(cons), hmin, hmax, capacity);
5497 
5498  infeasible = FALSE;
5499 
5500  /* if core profile is empty; nothing to do */
5501  if( SCIPprofileGetNTimepoints(profile) <= 1 )
5502  return SCIP_OKAY;
5503 
5504  /* start checking each job whether the bounds can be improved */
5505  for( v = 0; v < nvars; ++v )
5506  {
5507  SCIP_VAR* var;
5508  int demand;
5509  int duration;
5510  int begin;
5511  int end;
5512  int est;
5513  int lst;
5514 
5515  var = vars[v];
5516  assert(var != NULL);
5517 
5518  duration = durations[v];
5519  assert(duration > 0);
5520 
5521  /* collect earliest and latest start time */
5522  est = convertBoundToInt(scip, SCIPvarGetLbLocal(var));
5523  lst = convertBoundToInt(scip, SCIPvarGetUbLocal(var));
5524 
5525  /* check if the start time variables is already fixed; in that case we can ignore the job */
5526  if( est == lst )
5527  continue;
5528 
5529  /* check if the job runs completely outside of the effective horizon [hmin, hmax); if so skip it */
5530  if( lst + duration <= hmin || est >= hmax )
5531  continue;
5532 
5533  /* compute core interval w.r.t. effective time horizon */
5534  begin = MAX(hmin, lst);
5535  end = MIN(hmax, est + duration);
5536 
5537  demand = demands[v];
5538  assert(demand > 0);
5539 
5540  /* if the job has a core, remove it first */
5541  if( begin < end )
5542  {
5543  SCIPdebugMessage("variable <%s>[%g,%g] (duration %d, demand %d): remove core [%d,%d)\n",
5544  SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var), duration, demand, begin, end);
5545 
5546  SCIP_CALL( SCIPprofileDeleteCore(profile, begin, end, demand) );
5547  }
5548 
5549  /* first try to update the earliest start time */
5550  SCIP_CALL( coretimesUpdateLb(scip, nvars, vars, durations, demands, capacity, hmin, hmax, cons,
5551  profile, v, nchgbds, conshdlrdata->usebdwidening, initialized, explanation, cutoff) );
5552 
5553  if( *cutoff )
5554  break;
5555 
5556  /* second try to update the latest start time */
5557  SCIP_CALL( coretimesUpdateUb(scip, var, duration, demand, capacity, cons,
5558  profile, v, nchgbds) );
5559 
5560  if( *cutoff )
5561  break;
5562 
5563  /* collect the potentially updated earliest and latest start time */
5564  est = convertBoundToInt(scip, SCIPvarGetLbLocal(var));
5565  lst = convertBoundToInt(scip, SCIPvarGetUbLocal(var));
5566 
5567  /* compute core interval w.r.t. effective time horizon */
5568  begin = MAX(hmin, lst);
5569  end = MIN(hmax, est + duration);
5570 
5571  /* after updating the bound we might have a new core */
5572  if( begin < end )
5573  {
5574  int pos;
5575 
5576  SCIPdebugMessage("variable <%s>[%d,%d] (duration %d, demand %d): add core [%d,%d)\n",
5577  SCIPvarGetName(var), est, lst, duration, demand, begin, end);
5578 
5579  SCIP_CALL( SCIPprofileInsertCore(profile, begin, end, demand, &pos, &infeasible) );
5580 
5581  if( infeasible )
5582  {
5583  /* use conflict analysis to analysis the core insertion which was infeasible */
5584  SCIP_CALL( analyseInfeasibelCoreInsertion(scip, nvars, vars, durations, demands, capacity, hmin, hmax,
5585  var, duration, demand, SCIPprofileGetTime(profile, pos), conshdlrdata->usebdwidening, initialized, explanation) );
5586 
5587  if( explanation != NULL )
5588  explanation[v] = TRUE;
5589 
5590  (*cutoff) = TRUE;
5591 
5592  /* for the statistic we count the number of times a cutoff was detected due the time-time */
5593  SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->ncutofftimetable++ );
5594 
5595  break;
5596  }
5597  }
5598  }
5599 
5600  return SCIP_OKAY;
5601 }
5602 
5603 
5604 /** node data structure for the binary tree used for edgefinding (with overload checking) */
5605 struct SCIP_NodeData
5606 {
5607  SCIP_VAR* var; /**< start time variable of the job if the node data belongs to a leaf, otherwise NULL */
5608  SCIP_Real key; /**< key which is to insert the corresponding search node */
5609  int est; /**< earliest start time if the node data belongs to a leaf */
5610  int lct; /**< latest completion time if the node data belongs to a leaf */
5611  int demand; /**< demand of the job if the node data belongs to a leaf */
5612  int duration; /**< duration of the job if the node data belongs to a leaf */
5613  int leftadjust; /**< left adjustments of the duration w.r.t. hmin */
5614  int rightadjust; /**< right adjustments of the duration w.r.t. hmax */
5615  int enveloptheta; /**< the maximal energy of a subset of jobs part of the theta set */
5616  int energytheta; /**< energy of the subset of the jobs which are part of theta set */
5617  int energylambda;
5618  int enveloplambda;
5619  int idx; /**< index of the start time variable in the (global) variable array */
5620  SCIP_Bool intheta; /**< belongs the node to the theta set (otherwise to the lambda set) */
5621 };
5622 typedef struct SCIP_NodeData SCIP_NODEDATA;
5623 
5624 /** creates a node data structure */
5625 static
5627  SCIP* scip, /**< SCIP data structure */
5628  SCIP_NODEDATA** nodedata /**< pointer to store the create node data */
5629  )
5630 {
5631  SCIP_CALL( SCIPallocBuffer(scip, nodedata) );
5632  (*nodedata)->var = NULL;
5633  (*nodedata)->key = SCIP_INVALID;
5634  (*nodedata)->est = INT_MIN;
5635  (*nodedata)->lct = INT_MAX;
5636  (*nodedata)->duration = 0;
5637  (*nodedata)->demand = 0;
5638  (*nodedata)->enveloptheta = -1;
5639  (*nodedata)->energytheta = 0;
5640  (*nodedata)->enveloplambda = -1;
5641  (*nodedata)->energylambda = -1;
5642  (*nodedata)->idx = -1;
5643  (*nodedata)->intheta = TRUE;
5644 
5645  return SCIP_OKAY;
5646 }
5647 
5648 /** frees a node data structure */
5649 static
5651  SCIP* scip, /**< SCIP data structure */
5652  SCIP_NODEDATA** nodedata /**< pointer to store node data which should be freed */
5653  )
5654 {
5655  if( *nodedata != NULL )
5656  {
5657  SCIPfreeBuffer(scip, nodedata);
5658  }
5659 }
5660 
5661 /** update node data structure strating form the given node along the path to the root node */
5662 static
5664  SCIP* scip, /**< SCIP data structure */
5665  SCIP_BTNODE* node /**< search node which inserted */
5666  )
5667 {
5668  SCIP_BTNODE* left;
5669  SCIP_BTNODE* right;
5670  SCIP_NODEDATA* nodedata;
5671  SCIP_NODEDATA* leftdata;
5672  SCIP_NODEDATA* rightdata;
5673 
5674  SCIPdebugMessage("update envelop starting from node <%p>\n", (void*)node);
5675 
5676  if( SCIPbtnodeIsLeaf(node) )
5677  node = SCIPbtnodeGetParent(node);
5678 
5679  while( node != NULL )
5680  {
5681  /* get node data */
5682  nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(node);
5683  assert(nodedata != NULL);
5684 
5685  /* collect node data from left node */
5686  left = SCIPbtnodeGetLeftchild(node);
5687  assert(left != NULL);
5688  leftdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(left);
5689  assert(leftdata != NULL);
5690 
5691  /* collect node data from right node */
5692  right = SCIPbtnodeGetRightchild(node);
5693  assert(right != NULL);
5694  rightdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(right);
5695  assert(rightdata != NULL);
5696 
5697  /* update envelop and energy */
5698  if( leftdata->enveloptheta >= 0 )
5699  {
5700  assert(rightdata->energytheta != -1);
5701  nodedata->enveloptheta = MAX(leftdata->enveloptheta + rightdata->energytheta, rightdata->enveloptheta);
5702  }
5703  else
5704  nodedata->enveloptheta = rightdata->enveloptheta;
5705 
5706  assert(leftdata->energytheta != -1);
5707  assert(rightdata->energytheta != -1);
5708  nodedata->energytheta = leftdata->energytheta + rightdata->energytheta;
5709 
5710  if( leftdata->enveloplambda >= 0 )
5711  {
5712  assert(rightdata->energytheta != -1);
5713  nodedata->enveloplambda = MAX(leftdata->enveloplambda + rightdata->energytheta, rightdata->enveloplambda);
5714  }
5715  else
5716  nodedata->enveloplambda = rightdata->enveloplambda;
5717 
5718  if( leftdata->enveloptheta >= 0 && rightdata->energylambda >= 0 )
5719  nodedata->enveloplambda = MAX(nodedata->enveloplambda, leftdata->enveloptheta + rightdata->energylambda);
5720 
5721  SCIPdebugMessage("node <%p> lambda envelop %d\n", (void*)node, nodedata->enveloplambda);
5722 
5723  if( leftdata->energylambda >= 0 && rightdata->energylambda >= 0 )
5724  {
5725  assert(rightdata->energytheta != -1);
5726  assert(leftdata->energytheta != -1);
5727  nodedata->energylambda = MAX(leftdata->energylambda + rightdata->energytheta, leftdata->energytheta + rightdata->energylambda);
5728  }
5729  else if( rightdata->energylambda >= 0 )
5730  {
5731  assert(leftdata->energytheta != -1);
5732  nodedata->energylambda = leftdata->energytheta + rightdata->energylambda;
5733  }
5734  else if( leftdata->energylambda >= 0 )
5735  {
5736  assert(rightdata->energytheta != -1);
5737  nodedata->energylambda = leftdata->energylambda + rightdata->energytheta;
5738  }
5739  else
5740  nodedata->energylambda = -1;
5741 
5742  /* go to parent */
5743  node = SCIPbtnodeGetParent(node);
5744  }
5745 
5746  SCIPdebugMessage("updating done\n");
5747 
5748  return SCIP_OKAY;
5749 }
5750 
5751 /** updates the key of the first parent on the trace which comes from left */
5752 static
5754  SCIP_BTNODE* node, /**< node to start the trace */
5755  SCIP_Real key /**< update search key */
5756  )
5757 {
5758  assert(node != NULL);
5759 
5760  while( !SCIPbtnodeIsRoot(node) )
5761  {
5762  SCIP_BTNODE* parent;
5763 
5764  parent = SCIPbtnodeGetParent(node);
5765  assert(parent != NULL);
5766 
5767  if( SCIPbtnodeIsLeftchild(node) )
5768  {
5769  SCIP_NODEDATA* nodedata;
5770 
5771  nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(parent);
5772  assert(nodedata != NULL);
5773 
5774  nodedata->key = key;
5775  return;
5776  }
5777 
5778  node = parent;
5779  }
5780 }
5781 
5782 
5783 /** deletes the given node and updates all envelops */
5784 static
5786  SCIP* scip, /**< SCIP data structure */
5787  SCIP_BT* tree, /**< binary tree */
5788  SCIP_BTNODE* node /**< node to be deleted */
5789  )
5790 {
5791  SCIP_BTNODE* parent;
5792  SCIP_BTNODE* grandparent;
5793  SCIP_BTNODE* sibling;
5794 
5795  assert(scip != NULL);
5796  assert(tree != NULL);
5797  assert(node != NULL);
5798 
5799  assert(SCIPbtnodeIsLeaf(node));
5800  assert(!SCIPbtnodeIsRoot(node));
5801 
5802  SCIPdebugMessage("delete node <%p>\n", (void*)node);
5803 
5804  parent = SCIPbtnodeGetParent(node);
5805  assert(parent != NULL);
5806  if( SCIPbtnodeIsLeftchild(node) )
5807  {
5808  sibling = SCIPbtnodeGetRightchild(parent);
5809  SCIPbtnodeSetRightchild(parent, NULL);
5810  }
5811  else
5812  {
5813  sibling = SCIPbtnodeGetLeftchild(parent);
5814  SCIPbtnodeSetLeftchild(parent, NULL);
5815  }
5816  assert(sibling != NULL);
5817 
5818  grandparent = SCIPbtnodeGetParent(parent);
5819 
5820  if( grandparent != NULL )
5821  {
5822  /* reset parent of sibling */
5823  SCIPbtnodeSetParent(sibling, grandparent);
5824 
5825  /* reset child of grandparent to sibling */
5826  if( SCIPbtnodeIsLeftchild(parent) )
5827  {
5828  SCIPbtnodeSetLeftchild(grandparent, sibling);
5829  }
5830  else
5831  {
5832  SCIP_NODEDATA* nodedata;
5833 
5834  assert(SCIPbtnodeIsRightchild(parent));
5835  SCIPbtnodeSetRightchild(grandparent, sibling);
5836 
5837  nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(sibling);
5838 
5839  updateKeyOnTrace(grandparent, nodedata->key);
5840  }
5841 
5842  SCIP_CALL( updateEnvelop(scip, grandparent) );
5843  }
5844  else
5845  {
5846  SCIPbtnodeSetParent(sibling, NULL);
5847 
5848  SCIPbtSetRoot(tree, sibling);
5849  }
5850 
5851 
5852  SCIPbtnodeFree(tree, &parent);
5853 
5854  return SCIP_OKAY;
5855 }
5856 
5857 /** moves a node form the theta set into the lambda set and updates the envelops */
5858 static
5860  SCIP* scip, /**< SCIP data structure */
5861  SCIP_BT* tree, /**< binary tree */
5862  SCIP_BTNODE* node /**< node to move into the lambda set */
5863  )
5864 {
5865  SCIP_NODEDATA* nodedata;
5866 
5867  assert(scip != NULL);
5868  assert(tree != NULL);
5869  assert(node != NULL);
5870 
5871  nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(node);
5872  assert(nodedata != NULL);
5873  assert(nodedata->intheta);
5874 
5875  /* move the contributions form the theta set into the lambda set */
5876  assert(nodedata->enveloptheta != -1);
5877  assert(nodedata->energytheta != -1);
5878  assert(nodedata->enveloplambda == -1);
5879  assert(nodedata->energylambda == -1);
5880  nodedata->enveloplambda = nodedata->enveloptheta;
5881  nodedata->energylambda = nodedata->energytheta;
5882 
5883  nodedata->enveloptheta = -1;
5884  nodedata->energytheta = 0;
5885  nodedata->intheta = FALSE;
5886 
5887  /* update the energy and envelop values on trace */
5888  SCIP_CALL( updateEnvelop(scip, node) );
5889 
5890  return SCIP_OKAY;
5891 }
5892 
5893 /** inserts a node into the theta set and update the envelops */
5894 static
5896  SCIP* scip, /**< SCIP data structure */
5897  SCIP_BT* tree, /**< binary tree */
5898  SCIP_BTNODE* node, /**< node to insert */
5899  SCIP_NODEDATA** nodedatas, /**< array of node datas */
5900  int* nnodedatas /**< pointer to number of node datas */
5901  )
5902 {
5903  /* if the tree is empty the node will be the root node */
5904  if( SCIPbtIsEmpty(tree) )
5905  {
5906  SCIPbtSetRoot(tree, node);
5907  }
5908  else
5909  {
5910  SCIP_NODEDATA* newnodedata;
5911  SCIP_NODEDATA* leafdata;
5912  SCIP_NODEDATA* nodedata;
5913  SCIP_BTNODE* leaf;
5914  SCIP_BTNODE* newnode;
5915  SCIP_BTNODE* parent;
5916 
5917  leaf = SCIPbtGetRoot(tree);
5918  assert(leaf != NULL);
5919 
5920  leafdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(leaf);
5921  assert(leafdata != NULL);
5922 
5923  nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(node);
5924  assert(nodedata != NULL);
5925  assert(nodedata->intheta);
5926 
5927  /* find the position to insert the node */
5928  while( !SCIPbtnodeIsLeaf(leaf) )
5929  {
5930  if( nodedata->key < leafdata->key )
5931  leaf = SCIPbtnodeGetLeftchild(leaf);
5932  else
5933  leaf = SCIPbtnodeGetRightchild(leaf);
5934 
5935  leafdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(leaf);
5936  assert(leafdata != NULL);
5937  }
5938 
5939  assert(leaf != NULL);
5940  assert(leaf != node);
5941 
5942  /* create node data */
5943  SCIP_CALL( createNodedata(scip, &newnodedata) );
5944 
5945  /* create a new node */
5946  SCIP_CALL( SCIPbtnodeCreate(tree, &newnode, newnodedata) );
5947  assert(newnode != NULL);
5948 
5949  /* store node data to be able to delete them latter */
5950  nodedatas[*nnodedatas] = newnodedata;
5951  (*nnodedatas)++;
5952 
5953  parent = SCIPbtnodeGetParent(leaf);
5954 
5955  if( parent != NULL )
5956  {
5957  SCIPbtnodeSetParent(newnode, parent);
5958 
5959  /* check if the node is the left child */
5960  if( SCIPbtnodeGetLeftchild(parent) == leaf )
5961  {
5962  SCIPbtnodeSetLeftchild(parent, newnode);
5963  }
5964  else
5965  {
5966  SCIPbtnodeSetRightchild(parent, newnode);
5967  }
5968  }
5969  else
5970  SCIPbtSetRoot(tree, newnode);
5971 
5972  if( nodedata->key < leafdata->key )
5973  {
5974  /* node is on the left */
5975  SCIPbtnodeSetLeftchild(newnode, node);
5976  SCIPbtnodeSetRightchild(newnode, leaf);
5977  newnodedata->key = nodedata->key;
5978  }
5979  else
5980  {
5981  /* leaf is on the left */
5982  SCIPbtnodeSetLeftchild(newnode, leaf);
5983  SCIPbtnodeSetRightchild(newnode, node);
5984  newnodedata->key = leafdata->key;
5985  }
5986 
5987  SCIPbtnodeSetParent(leaf, newnode);
5988  SCIPbtnodeSetParent(node, newnode);
5989  }
5990 
5991  /* update envelop */
5992  SCIP_CALL( updateEnvelop(scip, node) );
5993 
5994  return SCIP_OKAY;
5995 }
5996 
5997 /** returns the leaf responsible for the lambda energy */
5998 static
6000  SCIP_BTNODE* node /**< node which defines the subtree beases on the lambda energy */
6001  )
6002 {
6003  SCIP_BTNODE* left;
6004  SCIP_BTNODE* right;
6005  SCIP_NODEDATA* nodedata;
6006  SCIP_NODEDATA* leftdata;
6007  SCIP_NODEDATA* rightdata;
6008 
6009  assert(node != NULL);
6010 
6011  nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(node);
6012  assert(nodedata != NULL);
6013 
6014  /* check if the node is the (responsible) leaf */
6015  if( SCIPbtnodeIsLeaf(node) )
6016  {
6017  assert(!nodedata->intheta);
6018  return node;
6019  }
6020 
6021  left = SCIPbtnodeGetLeftchild(node);
6022  assert(left != NULL);
6023 
6024  leftdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(left);
6025  assert(leftdata != NULL);
6026 
6027  right = SCIPbtnodeGetRightchild(node);
6028  assert(right != NULL);
6029 
6030  rightdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(right);
6031  assert(rightdata != NULL);
6032 
6033  assert(nodedata->energylambda != -1);
6034  assert(rightdata->energytheta != -1);
6035 
6036  if( leftdata->energylambda >= 0 && nodedata->energylambda == leftdata->energylambda + rightdata->energytheta )
6038 
6039  assert(leftdata->energytheta != -1);
6040  assert(rightdata->energylambda != -1);
6041  assert(nodedata->energylambda == leftdata->energytheta + rightdata->energylambda);
6042 
6044 }
6045 
6046 /** returns the leaf responsible for the lambda envelop */
6047 static
6049  SCIP_BTNODE* node /**< node which defines the subtree beases on the lambda envelop */
6050  )
6051 {
6052  SCIP_BTNODE* left;
6053  SCIP_BTNODE* right;
6054  SCIP_NODEDATA* nodedata;
6055  SCIP_NODEDATA* leftdata;
6056  SCIP_NODEDATA* rightdata;
6057 
6058  assert(node != NULL);
6059 
6060  nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(node);
6061  assert(nodedata != NULL);
6062 
6063  /* check if the node is the (responsible) leaf */
6064  if( SCIPbtnodeIsLeaf(node) )
6065  {
6066  assert(!nodedata->intheta);
6067  return node;
6068  }
6069 
6070  left = SCIPbtnodeGetLeftchild(node);
6071  assert(left != NULL);
6072 
6073  leftdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(left);
6074  assert(leftdata != NULL);
6075 
6076  right = SCIPbtnodeGetRightchild(node);
6077  assert(right != NULL);
6078 
6079  rightdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(right);
6080  assert(rightdata != NULL);
6081 
6082  assert(nodedata->enveloplambda != -1);
6083  assert(rightdata->energytheta != -1);
6084 
6085  /* check if the left or right child is the one defining the envelop for the lambda set */
6086  if( leftdata->enveloplambda >= 0 && nodedata->enveloplambda == leftdata->enveloplambda + rightdata->energytheta )
6088  else if( leftdata->enveloptheta >= 0 && rightdata->energylambda >= 0
6089  && nodedata->enveloplambda == leftdata->enveloptheta + rightdata->energylambda )
6091 
6092  assert(rightdata->enveloplambda != -1);
6093  assert(nodedata->enveloplambda == rightdata->enveloplambda);
6094 
6096 }
6097 
6098 
6099 /** reports all elements from set theta to generate a conflicting set */
6100 static
6102  SCIP_BTNODE* node, /**< node within a theta subtree */
6103  SCIP_BTNODE** omegaset, /**< array to store the collected jobs */
6104  int* nelements, /**< pointer to store the number of elements in omegaset */
6105  int* est, /**< pointer to store the earliest start time of the omega set */
6106  int* lct, /**< pointer to store the latest start time of the omega set */
6107  int* energy /**< pointer to store the energy of the omega set */
6108  )
6109 {
6110  SCIP_NODEDATA* nodedata;
6111 
6112  nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(node);
6113  assert(nodedata != NULL);
6114 
6115  if( !SCIPbtnodeIsLeaf(node) )
6116  {
6117  collectThetaSubtree(SCIPbtnodeGetLeftchild(node), omegaset, nelements, est, lct, energy);
6118  collectThetaSubtree(SCIPbtnodeGetRightchild(node), omegaset, nelements, est, lct, energy);
6119  }
6120  else if( nodedata->intheta )
6121  {
6122  assert(nodedata->var != NULL);
6123  SCIPdebugMessage("add variable <%s> as elements %d to omegaset\n", SCIPvarGetName(nodedata->var), *nelements);
6124 
6125  omegaset[*nelements] = node;
6126  (*est) = MIN(*est, nodedata->est);
6127  (*lct) = MAX(*lct, nodedata->lct);
6128  (*energy) += (nodedata->duration - nodedata->leftadjust - nodedata->rightadjust) * nodedata->demand;
6129  (*nelements)++;
6130  }
6131 }
6132 
6133 
6134 /** collect the jobs (omega set) which are contribute to theta envelop from the theta set */
6135 static
6137  SCIP_BTNODE* node, /**< node whose theta envelop needs to be backtracked */
6138  SCIP_BTNODE** omegaset, /**< array to store the collected jobs */
6139  int* nelements, /**< pointer to store the number of elements in omegaset */
6140  int* est, /**< pointer to store the earliest start time of the omega set */
6141  int* lct, /**< pointer to store the latest start time of the omega set */
6142  int* energy /**< pointer to store the energy of the omega set */
6143  )
6144 {
6145  assert(node != NULL);
6146 
6147  if( SCIPbtnodeIsLeaf(node) )
6148  {
6149  collectThetaSubtree(node, omegaset, nelements, est, lct, energy);
6150  }
6151  else
6152  {
6153  SCIP_BTNODE* left;
6154  SCIP_BTNODE* right;
6155  SCIP_NODEDATA* nodedata;
6156  SCIP_NODEDATA* leftdata;
6157  SCIP_NODEDATA* rightdata;
6158 
6159  nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(node);
6160  assert(nodedata != NULL);
6161 
6162 
6163  left = SCIPbtnodeGetLeftchild(node);
6164  assert(left != NULL);
6165 
6166  leftdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(left);
6167  assert(leftdata != NULL);
6168 
6169  right = SCIPbtnodeGetRightchild(node);
6170  assert(right != NULL);
6171 
6172  rightdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(right);
6173  assert(rightdata != NULL);
6174 
6175  nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(node);
6176  assert(nodedata != NULL);
6177 
6178  assert(nodedata->enveloptheta != -1);
6179  assert(rightdata->energytheta != -1);
6180 
6181  if( leftdata->enveloptheta >= 0 && nodedata->enveloptheta == leftdata->enveloptheta + rightdata->energytheta )
6182  {
6183  traceThetaEnvelop(left, omegaset, nelements, est, lct, energy);
6184  collectThetaSubtree(right, omegaset, nelements, est, lct, energy);
6185  }
6186  else
6187  {
6188  assert(rightdata->enveloptheta != -1);
6189  assert(nodedata->enveloptheta == rightdata->enveloptheta);
6190  traceThetaEnvelop(right, omegaset, nelements, est, lct, energy);
6191  }
6192  }
6193 }
6194 
6195 /** collect the jobs (omega set) which are contribute to lambda envelop from the theta set */
6196 static
6198  SCIP_BTNODE* node, /**< node whose lambda envelop needs to be backtracked */
6199  SCIP_BTNODE** omegaset, /**< array to store the collected jobs */
6200  int* nelements, /**< pointer to store the number of elements in omega set */
6201  int* est, /**< pointer to store the earliest start time of the omega set */
6202  int* lct, /**< pointer to store the latest start time of the omega set */
6203  int* energy /**< pointer to store the energy of the omega set */
6204  )
6205 {
6206  SCIP_BTNODE* left;
6207  SCIP_BTNODE* right;
6208  SCIP_NODEDATA* nodedata;
6209  SCIP_NODEDATA* leftdata;
6210  SCIP_NODEDATA* rightdata;
6211 
6212  assert(node != NULL);
6213 
6214  nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(node);
6215  assert(nodedata != NULL);
6216 
6217  /* check if the node is a leaf */
6218  if( SCIPbtnodeIsLeaf(node) )
6219  return;
6220 
6221  left = SCIPbtnodeGetLeftchild(node);
6222  assert(left != NULL);
6223 
6224  leftdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(left);
6225  assert(leftdata != NULL);
6226 
6227  right = SCIPbtnodeGetRightchild(node);
6228  assert(right != NULL);
6229 
6230  rightdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(right);
6231  assert(rightdata != NULL);
6232 
6233  assert(nodedata->energylambda != -1);
6234  assert(rightdata->energytheta != -1);
6235 
6236  if( leftdata->energylambda >= 0 && nodedata->energylambda == leftdata->energylambda + rightdata->energytheta )
6237  {
6238  traceLambdaEnergy(left, omegaset, nelements, est, lct, energy);
6239  collectThetaSubtree(right, omegaset, nelements, est, lct, energy);
6240  }
6241  else
6242  {
6243  assert(leftdata->energytheta != -1);
6244  assert(rightdata->energylambda != -1);
6245  assert(nodedata->energylambda == leftdata->energytheta + rightdata->energylambda);
6246 
6247  collectThetaSubtree(left, omegaset, nelements, est, lct, energy);
6248  traceLambdaEnergy(right, omegaset, nelements, est, lct, energy);
6249  }
6250 }
6251 
6252 /** collect the jobs (omega set) which are contribute to lambda envelop from the theta set */
6253 static
6255  SCIP_BTNODE* node, /**< node whose lambda envelop needs to be backtracked */
6256  SCIP_BTNODE** omegaset, /**< array to store the collected jobs */
6257  int* nelements, /**< pointer to store the number of elements in omega set */
6258  int* est, /**< pointer to store the earliest start time of the omega set */
6259  int* lct, /**< pointer to store the latest start time of the omega set */
6260  int* energy /**< pointer to store the energy of the omega set */
6261  )
6262 {
6263  SCIP_BTNODE* left;
6264  SCIP_BTNODE* right;
6265  SCIP_NODEDATA* nodedata;
6266  SCIP_NODEDATA* leftdata;
6267  SCIP_NODEDATA* rightdata;
6268 
6269  assert(node != NULL);
6270 
6271  nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(node);
6272  assert(nodedata != NULL);
6273 
6274  /* check if the node is a leaf */
6275  if( SCIPbtnodeIsLeaf(node) )
6276  {
6277  assert(!nodedata->intheta);
6278  return;
6279  }
6280 
6281  left = SCIPbtnodeGetLeftchild(node);
6282  assert(left != NULL);
6283 
6284  leftdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(left);
6285  assert(leftdata != NULL);
6286 
6287  right = SCIPbtnodeGetRightchild(node);
6288  assert(right != NULL);
6289 
6290  rightdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(right);
6291  assert(rightdata != NULL);
6292 
6293  assert(nodedata->enveloplambda != -1);
6294  assert(rightdata->energytheta != -1);
6295 
6296  if( leftdata->enveloplambda >= 0 && nodedata->enveloplambda == leftdata->enveloplambda + rightdata->energytheta )
6297  {
6298  traceLambdaEnvelop(left, omegaset, nelements, est, lct, energy);
6299  collectThetaSubtree(right, omegaset, nelements, est, lct, energy);
6300  }
6301  else
6302  {
6303  if( leftdata->enveloptheta >= 0 && rightdata->energylambda >= 0
6304  && nodedata->enveloplambda == leftdata->enveloptheta + rightdata->energylambda )
6305  {
6306  traceThetaEnvelop(left, omegaset, nelements, est, lct, energy);
6307  traceLambdaEnergy(right, omegaset, nelements, est, lct, energy);
6308  }
6309  else
6310  {
6311  assert(rightdata->enveloplambda != -1);
6312  assert(nodedata->enveloplambda == rightdata->enveloplambda);
6313  traceLambdaEnvelop(right, omegaset, nelements, est, lct, energy);
6314  }
6315  }
6316 }
6317 
6318 /** compute the energy contribution by job which corresponds to the given leaf */
6319 static
6321  SCIP_BTNODE* node /**< leaf */
6322  )
6323 {
6324  SCIP_NODEDATA* nodedata;
6325  int duration;
6326 
6327  nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(node);
6328  assert(nodedata != NULL);
6329  assert(nodedata->var != NULL);
6330 
6331  duration = nodedata->duration - nodedata->leftadjust - nodedata->rightadjust;
6332  assert(duration > 0);
6333 
6334  SCIPdebugMessage("variable <%s>: loc=[%g,%g] glb=[%g,%g] (duration %d, demand %d)\n",
6335  SCIPvarGetName(nodedata->var), SCIPvarGetLbLocal(nodedata->var), SCIPvarGetUbLocal(nodedata->var),
6336  SCIPvarGetLbGlobal(nodedata->var), SCIPvarGetUbGlobal(nodedata->var), duration, nodedata->demand);
6337 
6338  /* return energy which is contributed by the start time variable */
6339  return nodedata->demand * duration;
6340 }
6341 
6342 /** comparison method for two node datas w.r.t. the earliest start time */
6343 static
6345 {
6346  int est1;
6347  int est2;
6348 
6349  est1 = ((SCIP_NODEDATA*)SCIPbtnodeGetData((SCIP_BTNODE*)elem1))->est;
6350  est2 = ((SCIP_NODEDATA*)SCIPbtnodeGetData((SCIP_BTNODE*)elem2))->est;
6351 
6352  return (est1 - est2);
6353 }
6354 
6355 /** comparison method for two node datas w.r.t. the latest completion time */
6356 static
6357 SCIP_DECL_SORTPTRCOMP(compNodedataLct)
6358 {
6359  int lct1;
6360  int lct2;
6361 
6362  lct1 = ((SCIP_NODEDATA*)elem1)->lct;
6363  lct2 = ((SCIP_NODEDATA*)elem2)->lct;
6364 
6365  return (lct1 - lct2);
6366 }
6367 
6368 
6369 /** an overload was detected; initialized conflict analysis, add an initial reason
6370  *
6371  * @note the conflict analysis is not performend, only the initialized SCIP_Bool pointer is set to TRUE
6372  */
6373 static
6375  SCIP* scip, /**< SCIP data structure */
6376  SCIP_BTNODE** leaves, /**< responsible leaves for the overload */
6377  int capacity, /**< cumulative capacity */
6378  int nleaves, /**< number of responsible leaves */
6379  int est, /**< earliest start time of the ...... */
6380  int lct, /**< latest completly time of the .... */
6381  int reportedenergy, /**< energy which already reported */
6382  SCIP_Bool propest, /**< should the earliest start times be propagated, otherwise the latest completion times */
6383  int shift, /**< shift applied to all jobs before adding them to the tree */
6384  SCIP_Bool usebdwidening, /**< should bound widening be used during conflict analysis? */
6385  SCIP_Bool* initialized, /**< was conflict analysis initialized */
6386  SCIP_Bool* explanation /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */
6387  )
6388 {
6389  int energy;
6390  int j;
6391 
6392  /* do nothing if conflict analysis is not applicable */
6394  return SCIP_OKAY;
6395 
6396  SCIPdebugMessage("est=%d, lct=%d, propest %u, reportedenergy %d, shift %d\n", est, lct, propest, reportedenergy, shift);
6397 
6398  /* compute energy of initial time window */
6399  energy = (lct - est) * capacity;
6400 
6401  /* sort the start time variables which were added to search tree w.r.t. earliest start time */
6402  SCIPsortDownPtr((void**)leaves, compNodeEst, nleaves);
6403 
6404  /* collect the energy of the responsible leaves until the cumulative energy is large enough to detect an overload;
6405  * thereby, compute the time window of interest
6406  */
6407  for( j = 0; j < nleaves && reportedenergy <= energy; ++j )
6408  {
6409  SCIP_NODEDATA* nodedata;
6410 
6411  nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(leaves[j]);
6412  assert(nodedata != NULL);
6413 
6414  reportedenergy += computeEnergyContribution(leaves[j]);
6415 
6416  /* adjust energy if the earliest start time decrease */
6417  if( nodedata->est < est )
6418  {
6419  est = nodedata->est;
6420  energy = (lct - est) * capacity;
6421  }
6422  }
6423  assert(reportedenergy > energy);
6424 
6425  SCIPdebugMessage("time window [%d,%d) available energy %d, required energy %d\n", est, lct, energy, reportedenergy);
6426 
6427  /* initialize conflict analysis */
6429 
6430  /* flip earliest start time and latest completion time */
6431  if( !propest )
6432  {
6433  SCIPswapInts(&est, &lct);
6434 
6435  /* shift earliest start time and latest completion time */
6436  lct = shift - lct;
6437  est = shift - est;
6438  }
6439  else
6440  {
6441  /* shift earliest start time and latest completion time */
6442  lct = lct + shift;
6443  est = est + shift;
6444  }
6445 
6446  nleaves = j;
6447 
6448  /* report the variables and relax their bounds to final time interval [est,lct) which was been detected to be
6449  * overloaded
6450  */
6451  for( j = nleaves-1; j >= 0; --j )
6452  {
6453  SCIP_NODEDATA* nodedata;
6454 
6455  nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(leaves[j]);
6456  assert(nodedata != NULL);
6457  assert(nodedata->var != NULL);
6458 
6459  /* check if bound widening should be used */
6460  if( usebdwidening )
6461  {
6462  SCIP_CALL( SCIPaddConflictRelaxedUb(scip, nodedata->var, NULL, (SCIP_Real)(est - nodedata->leftadjust)) );
6463  SCIP_CALL( SCIPaddConflictRelaxedLb(scip, nodedata->var, NULL, (SCIP_Real)(lct - nodedata->duration + nodedata->rightadjust)) );
6464  }
6465  else
6466  {
6467  SCIP_CALL( SCIPaddConflictLb(scip, nodedata->var, NULL) );
6468  SCIP_CALL( SCIPaddConflictUb(scip, nodedata->var, NULL) );
6469  }
6470 
6471  if( explanation != NULL )
6472  explanation[nodedata->idx] = TRUE;
6473  }
6474 
6475  (*initialized) = TRUE;
6476 
6477  return SCIP_OKAY;
6478 }
6479 
6480 /** computes a new latest starting time of the job in 'respleaf' due to the energy consumption and stores the
6481  * responsible interval bounds in *est_omega and *lct_omega
6482  */
6483 static
6485  SCIP* scip, /**< SCIP data structure */
6486  int duration, /**< duration of the job to move */
6487  int demand, /**< demand of the job to move */
6488  int capacity, /**< cumulative capacity */
6489  int est, /**< earliest start time of the omega set */
6490  int lct, /**< latest start time of the omega set */
6491  int energy /**< energy of the omega set */
6492  )
6493 {
6494  int newest;
6495 
6496  newest = 0;
6497 
6498  assert(scip != NULL);
6499 
6500  if( energy > (capacity - demand) * (lct - est) )
6501  {
6502  if( energy + demand * duration > capacity * (lct - est) )
6503  {
6504  newest = (int)SCIPfeasCeil(scip, (energy - (SCIP_Real)(capacity - demand) * (lct - est)) / (SCIP_Real)demand);
6505  newest += est;
6506  }
6507  }
6508 
6509  return newest;
6510 }
6511 
6512 /** propagates start time using an edge finding algorithm which is based on binary trees (theta lambda trees)
6513  *
6514  * @note The algorithm is based on the paper: Petr Vilim, "Edge Finding Filtering Algorithm for Discrete Cumulative
6515  * Resources in O(kn log n)". *I.P. Gent (Ed.): CP 2009, LNCS 5732, pp. 802–816, 2009.
6516  */
6517 static
6519  SCIP* scip, /**< SCIP data structure */
6520  SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
6521  SCIP_CONS* cons, /**< constraint which is propagated */
6522  SCIP_BT* tree, /**< binary tree constaining the theta and lambda sets */
6523  SCIP_BTNODE** leaves, /**< array of all leaves for each job one */
6524  int capacity, /**< cumulative capacity */
6525  int ncands, /**< number of candidates */
6526  SCIP_Bool propest, /**< should the earliest start times be propagated, otherwise the latest completion times */
6527  int shift, /**< shift applied to all jobs before adding them to the tree */
6528  SCIP_Bool* initialized, /**< was conflict analysis initialized */
6529  SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */
6530  int* nchgbds, /**< pointer to store the number of bound changes */
6531  SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */
6532  )
6533 {
6534  SCIP_NODEDATA* rootdata;
6535  int j;
6536 
6537  assert(!SCIPbtIsEmpty(tree));
6538 
6539  rootdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(SCIPbtGetRoot(tree));
6540  assert(rootdata != NULL);
6541 
6542  /* iterate over all added candidate (leaves) in non-increasing order w.r.t. their latest completion time */
6543  for( j = ncands-1; j >= 0 && !(*cutoff); --j )
6544  {
6545  SCIP_NODEDATA* nodedata;
6546 
6547  if( SCIPbtnodeIsRoot(leaves[j]) )
6548  break;
6549 
6550  nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(leaves[j]);
6551  assert(nodedata->est != -1);
6552 
6553  /* check if the root lambda envelop exeeds the available capacity */
6554  while( !(*cutoff) && rootdata->enveloplambda > capacity * nodedata->lct )
6555  {
6556  SCIP_BTNODE** omegaset;
6557  SCIP_BTNODE* leaf;
6558  SCIP_NODEDATA* leafdata;
6559  int nelements;
6560  int energy;
6561  int newest;
6562  int est;
6563  int lct;
6564 
6565  assert(!(*cutoff));
6566 
6567  /* find responsible leaf for the lambda envelope */
6569  assert(leaf != NULL);
6570  assert(SCIPbtnodeIsLeaf(leaf));
6571 
6572  leafdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(leaf);
6573  assert(leafdata != NULL);
6574  assert(!leafdata->intheta);
6575  assert(leafdata->duration > 0);
6576  assert(leafdata->est >= 0);
6577 
6578  /* check if the job has to be removed since its latest completion is to large */
6579  if( leafdata->est + leafdata->duration >= nodedata->lct )
6580  {
6581  SCIP_CALL( deleteLambdaLeaf(scip, tree, leaf) );
6582 
6583  /* the root might changed therefore we need to collect the new root node datas */
6584  rootdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(SCIPbtGetRoot(tree));
6585  assert(rootdata != NULL);
6586 
6587  continue;
6588  }
6589 
6590  /* compute omega set */
6591  SCIP_CALL( SCIPallocBufferArray(scip, &omegaset, ncands) );
6592 
6593  nelements = 0;
6594  est = INT_MAX;
6595  lct = INT_MIN;
6596  energy = 0;
6597 
6598  /* collect the omega set from theta set */
6599  traceLambdaEnvelop(SCIPbtGetRoot(tree), omegaset, &nelements, &est, &lct, &energy);
6600  assert(nelements > 0);
6601  assert(nelements < ncands);
6602 
6603  newest = computeEstOmegaset(scip, leafdata->duration, leafdata->demand, capacity, est, lct, energy);
6604 
6605  /* if the computed earliest start time is greater than the latest completion time of the omega set we detected an overload */
6606  if( newest > lct )
6607  {
6608  SCIPdebugMessage("an overload was detected duration edge-finder propagattion\n");
6609 
6610  /* analyze over load */
6611  SCIP_CALL( analyzeConflictOverload(scip, omegaset, capacity, nelements, est, lct, 0, propest, shift,
6612  conshdlrdata->usebdwidening, initialized, explanation) );
6613  (*cutoff) = TRUE;
6614 
6615  /* for the statistic we count the number of times a cutoff was detected due the edge-finder */
6616  SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->ncutoffedgefinder++ );
6617  }
6618  else if( newest > 0 )
6619  {
6620  SCIP_Bool infeasible;
6621  SCIP_Bool tightened;
6622  INFERINFO inferinfo;
6623 
6624  if( propest )
6625  {
6626  /* constuct inference information; store used propagation rule and the the time window of the omega set */
6627  inferinfo = getInferInfo(PROPRULE_2_EDGEFINDING, est + shift, lct + shift);
6628 
6629  SCIPdebugMessage("variable <%s> adjust lower bound from %g to %d\n",
6630  SCIPvarGetName(leafdata->var), SCIPvarGetLbLocal(leafdata->var), newest + shift);
6631 
6632  SCIP_CALL( SCIPinferVarLbCons(scip, leafdata->var, (SCIP_Real)(newest + shift),
6633  cons, inferInfoToInt(inferinfo), TRUE, &infeasible, &tightened) );
6634 
6635  /* for the statistic we count the number of times a lower bound was tightened due the edge-finder */
6637  }
6638  else
6639  {
6640  /* constuct inference information; store used propagation rule and the the time window of the omega set */
6641  inferinfo = getInferInfo(PROPRULE_2_EDGEFINDING, shift - lct, shift - est);
6642 
6643  SCIPdebugMessage("variable <%s> adjust upper bound from %g to %d\n",
6644  SCIPvarGetName(leafdata->var), SCIPvarGetUbLocal(leafdata->var), shift - newest - leafdata->duration);
6645 
6646  SCIP_CALL( SCIPinferVarUbCons(scip, leafdata->var, (SCIP_Real)(shift - newest - leafdata->duration),
6647  cons, inferInfoToInt(inferinfo), TRUE, &infeasible, &tightened) );
6648 
6649  /* for the statistic we count the number of times a upper bound was tightened due the edge-finder */
6651  }
6652 
6653  /* adjust the earliest start time */
6654  if( tightened )
6655  {
6656  leafdata->est = newest;
6657  (*nchgbds)++;
6658  }
6659 
6660  if( infeasible )
6661  {
6662  /* initialize conflict analysis if conflict analysis is applicable */
6664  {
6665  int i;
6666 
6667  SCIPdebugMessage("edge-finder dectected an infeasibility\n");
6668 
6670 
6671  /* add lower and upper bound of variable which leads to the infeasibilty */
6672  SCIP_CALL( SCIPaddConflictLb(scip, leafdata->var, NULL) );
6673  SCIP_CALL( SCIPaddConflictUb(scip, leafdata->var, NULL) );
6674 
6675  if( explanation != NULL )
6676  explanation[leafdata->idx] = TRUE;
6677 
6678  /* add lower and upper bound of variable which lead to the infeasibilty */
6679  for( i = 0; i < nelements; ++i )
6680  {
6681  nodedata = (SCIP_NODEDATA*)SCIPbtnodeGetData(omegaset[i]);
6682  assert(nodedata != NULL);
6683 
6684  SCIP_CALL( SCIPaddConflictLb(scip, nodedata->var, NULL) );
6685  SCIP_CALL( SCIPaddConflictUb(scip, nodedata->var, NULL) );
6686 
6687  if( explanation != NULL )
6688  explanation[nodedata->idx] = TRUE;
6689  }
6690 
6691  (*initialized) = TRUE;
6692  }
6693 
6694  (*cutoff) = TRUE;
6695 
6696  /* for the statistic we count the number of times a cutoff was detected due the edge-finder */
6697  SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->ncutoffedgefinder++ );
6698  }
6699  }
6700 
6701  /* free omegaset array */
6702  SCIPfreeBufferArray(scip, &omegaset);
6703 
6704  /* delete responsible leaf from lambda */
6705  SCIP_CALL( deleteLambdaLeaf(scip, tree, leaf) );
6706 
6707  /* the root might changed therefore we need to collect the new root node datas */
6708  rootdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(SCIPbtGetRoot(tree));
6709  assert(rootdata != NULL);
6710  }
6711 
6712  /* move current job j from the theta set into the lambda set */
6713  SCIP_CALL( moveNodeToLambda(scip, tree, leaves[j]) );
6714  }
6715 
6716  return SCIP_OKAY;
6717 }
6718 
6719 /** checks whether the instance is infeasible due to a overload within a certain time frame using the idea of theta trees
6720  *
6721  * @note The algorithm is based on the paper: Petr Vilim, "Max Energy Filtering Algorithm for Discrete Cumulative
6722  * Resources". In: Willem Jan van Hoeve and John N. Hooker (Eds.), Integration of AI and OR Techniques in
6723  * Constraint Programming for Combinatorial Optimization Problems (CPAIOR 2009), LNCS 5547, pp 294--308
6724  */
6725 static
6727  SCIP* scip, /**< SCIP data structure */
6728  SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
6729  int nvars, /**< number of start time variables (activities) */
6730  SCIP_VAR** vars, /**< array of start time variables */
6731  int* durations, /**< array of durations */
6732  int* demands, /**< array of demands */
6733  int capacity, /**< cumulative capacity */
6734  int hmin, /**< left bound of time axis to be considered (including hmin) */
6735  int hmax, /**< right bound of time axis to be considered (not including hmax) */
6736  SCIP_CONS* cons, /**< constraint which is propagated */
6737  SCIP_Bool propest, /**< should the earliest start times be propagated, otherwise the latest completion times */
6738  SCIP_Bool* initialized, /**< was conflict analysis initialized */
6739  SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */
6740  int* nchgbds, /**< pointer to store the number of bound changes */
6741  SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */
6742  )
6743 {
6744  SCIP_NODEDATA** nodedatas;
6745  SCIP_BTNODE** leaves;
6746  SCIP_BT* tree;
6747 
6748  int totalenergy;
6749  int nnodedatas;
6750  int ninsertcands;
6751  int ncands;
6752 
6753  int shift;
6754  int j;
6755 
6756  assert(scip != NULL);
6757  assert(cons != NULL);
6758  assert(initialized != NULL);
6759  assert(cutoff != NULL);
6760  assert(*cutoff == FALSE);
6761 
6762  SCIPdebugMessage("check overload of cumulative condition of constraint <%s> (capacity %d)\n", SCIPconsGetName(cons), capacity);
6763 
6764  SCIP_CALL( SCIPallocBufferArray(scip, &nodedatas, 2*nvars) );
6765  SCIP_CALL( SCIPallocBufferArray(scip, &leaves, nvars) );
6766 
6767  ncands = 0;
6768  totalenergy = 0;
6769 
6770  SCIP_CALL( SCIPbtCreate(&tree, SCIPblkmem(scip)) );
6771 
6772  /* compute the shift which we apply to compute .... latest completion time of all jobs */
6773  if( propest )
6774  shift = 0;
6775  else
6776  {
6777  shift = 0;
6778 
6779  /* compute the latest completion time of all jobs which define the shift we apply to run the algorithm for the
6780  * earliest start time propagation to handle the latest completion times
6781  */
6782  for( j = 0; j < nvars; ++j )
6783  {
6784  int lct;
6785 
6786  lct = convertBoundToInt(scip, SCIPvarGetUbLocal(vars[j])) + durations[j];
6787  shift = MAX(shift, lct);
6788  }
6789  }
6790 
6791  /* collect earliest and latest completion times and ignore jobs which do not run completion within the effective
6792  * horizon
6793  */
6794  for( j = 0; j < nvars; ++j )
6795  {
6796  SCIP_NODEDATA* nodedata;
6797  SCIP_VAR* var;
6798  int duration;
6799  int leftadjust;
6800  int rightadjust;
6801  int energy;
6802  int est;
6803  int lct;
6804 
6805  var = vars[j];
6806  assert(var != NULL);
6807 
6808  duration = durations[j];
6809  assert(duration > 0);
6810 
6811  leftadjust = 0;
6812  rightadjust = 0;
6813 
6814  est = convertBoundToInt(scip, SCIPvarGetLbLocal(var));
6815  lct = convertBoundToInt(scip, SCIPvarGetUbLocal(var)) + duration;
6816 
6817  /* adjust the duration, earliest start time, and latest completion time of jobs which do not lie completely in the
6818  * effective horizon [hmin,hmax)
6819  */
6820  if( conshdlrdata->useadjustedjobs )
6821  {
6822  if( est < hmin )
6823  {
6824  leftadjust = (hmin - est);
6825  est = hmin;
6826  }
6827  if( lct > hmax )
6828  {
6829  rightadjust = (lct - hmax);
6830  lct = hmax;
6831  }
6832 
6833  /* only consider jobs which have a (adjusted) duration greater than zero (the amound which will run defenetly
6834  * with the effective time horizon
6835  */
6836  if( duration - leftadjust - rightadjust <= 0 )
6837  continue;
6838  }
6839  else if( est < hmin || lct > hmax )
6840  continue;
6841 
6842  energy = demands[j] * (duration - leftadjust - rightadjust);
6843  assert(energy > 0);
6844 
6845  totalenergy += energy;
6846 
6847  /* flip earliest start time and latest completion time */
6848  if( !propest )
6849  {
6850  SCIPswapInts(&est, &lct);
6851 
6852  /* shift earliest start time and latest completion time */
6853  lct = shift - lct;
6854  est = shift - est;
6855  }
6856  else
6857  {
6858  /* shift earliest start time and latest completion time */
6859  lct = lct - shift;
6860  est = est - shift;
6861  }
6862  assert(est < lct);
6863  assert(est >= 0);
6864  assert(lct >= 0);
6865 
6866  /* create search node data */
6867  SCIP_CALL( createNodedata(scip, &nodedata) );
6868 
6869  /* initialize search node data */
6870  /* adjust earliest start time to make it unique in case several jobs have the same earliest start time */
6871  nodedata->key = est + j / (2.0 * nvars);
6872  nodedata->var = var;
6873  nodedata->est = est;
6874  nodedata->lct = lct;
6875  nodedata->demand = demands[j];
6876  nodedata->duration = duration;
6877  nodedata->leftadjust = leftadjust;
6878  nodedata->rightadjust = rightadjust;
6879 
6880  /* the envelop is the energy of the job plus the total amount of energy which is available in the time period
6881  * before that job can start, that is [0,est). The envelop is later used to compare the energy consumption of a
6882  * particular time interval [a,b] against the time interval [0,b].
6883  */
6884  nodedata->enveloptheta = capacity * est + energy;
6885  nodedata->energytheta = energy;
6886  nodedata->enveloplambda = -1;
6887  nodedata->energylambda = -1;
6888 
6889  nodedata->idx = j;
6890  nodedata->intheta = TRUE;
6891 
6892  nodedatas[ncands] = nodedata;
6893  ncands++;
6894  }
6895 
6896  nnodedatas = ncands;
6897 
6898  /* sort (non-decreasing) the jobs w.r.t. latest completion times */
6899  SCIPsortPtr((void**)nodedatas, compNodedataLct, ncands);
6900 
6901  ninsertcands = 0;
6902 
6903  /* iterate over all jobs in non-decreasing order of their latest completion times and add them to the theta set until
6904  * the root envelop detects an overload
6905  */
6906  for( j = 0; j < ncands; ++j )
6907  {
6908  SCIP_BTNODE* leaf;
6909  SCIP_NODEDATA* rootdata;
6910 
6911  /* check if the new job opens a time window which size is so large that it offers more energy than the total
6912  * energy of all candidate jobs. If so we skip that one.
6913  */
6914  if( (nodedatas[j]->lct - nodedatas[j]->est) * capacity >= totalenergy )
6915  {
6916  /* set the earliest start time to minus one to mark that candidate to be not used */
6917  nodedatas[j]->est = -1;
6918  continue;
6919  }
6920 
6921  /* create search node */
6922  SCIP_CALL( SCIPbtnodeCreate(tree, &leaf, (void*)nodedatas[j]) );
6923 
6924  /* insert new node into the theta set and updete the envelops */
6925  SCIP_CALL( insertThetanode(scip, tree, leaf, nodedatas, &nnodedatas) );
6926  assert(nnodedatas <= 2*nvars);
6927 
6928  /* move the inserted candidates together */
6929  leaves[ninsertcands] = leaf;
6930  ninsertcands++;
6931 
6932  assert(!SCIPbtIsEmpty(tree));
6933  rootdata = (SCIP_NODEDATA*)SCIPbtnodeGetData(SCIPbtGetRoot(tree));
6934  assert(rootdata != NULL);
6935 
6936  /* check if the theta set envelops exceeds the available capacity */
6937  if( rootdata->enveloptheta > capacity * nodedatas[j]->lct )
6938  {
6939  SCIPdebugMessage("detects cutoff due to overload in time window [?,%d) (ncands %d)\n", nodedatas[j]->lct, j);
6940  (*cutoff) = TRUE;
6941 
6942  /* for the statistic we count the number of times a cutoff was detected due the edge-finder */
6943  SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->ncutoffoverload++ );
6944 
6945  break;
6946  }
6947  }
6948 
6949  /* in case an overload was detected and the conflict analysis is applicable, create an initialize explanation */
6950  if( *cutoff )
6951  {
6952  int glbenery;
6953  int est;
6954  int lct;
6955 
6956  glbenery = 0;
6957  est = nodedatas[j]->est;
6958  lct = nodedatas[j]->lct;
6959 
6960  /* scan the remaining candidates for a global contributions within the time window of the last inserted candidate
6961  * which led to an overload
6962  */
6963  for( j = j+1; j < ncands; ++j )
6964  {
6965  SCIP_NODEDATA* nodedata;
6966  int duration;
6967  int glbest;
6968  int glblct;
6969 
6970  nodedata = nodedatas[j];
6971  assert(nodedata != NULL);
6972 
6973  duration = nodedata->duration - nodedata->leftadjust - nodedata->rightadjust;
6974 
6975  /* get latest start time */
6976  glbest = convertBoundToInt(scip, SCIPvarGetLbGlobal(nodedata->var));
6977  glblct = convertBoundToInt(scip, SCIPvarGetUbGlobal(nodedata->var)) + duration;
6978 
6979  /* check if parts of the jobs run with the time window defined by the last inserted job */
6980  if( glbest < est )
6981  duration -= (est - glbest);
6982 
6983  if( glblct > lct )
6984  duration -= (glblct - lct);
6985 
6986  if( duration > 0 )
6987  {
6988  glbenery += nodedata->demand * duration;
6989 
6990  if( explanation != NULL )
6991  explanation[nodedata->idx] = TRUE;
6992  }
6993  }
6994 
6995  /* analyze the overload */
6996  SCIP_CALL( analyzeConflictOverload(scip, leaves, capacity, ninsertcands, est, lct, glbenery, propest, shift,
6997  conshdlrdata->usebdwidening, initialized, explanation) );
6998  }
6999  else if( ninsertcands > 1 && conshdlrdata->efinfer )
7000  {
7001  /* if we have more than one job insterted and edge-finding should be performed we do it */
7002  SCIP_CALL( inferboundsEdgeFinding(scip, conshdlrdata, cons, tree, leaves, capacity, ninsertcands,
7003  propest, shift, initialized, explanation, nchgbds, cutoff) );
7004  }
7005 
7006  /* free the search nodes data */
7007  for( j = nnodedatas - 1; j >= 0; --j )
7008  {
7009  freeNodedata(scip, &nodedatas[j]);
7010  }
7011 
7012  /* free theta tree */
7013  SCIPbtFree(&tree);
7014 
7015  /* free buffer arrays */
7016  SCIPfreeBufferArray(scip, &leaves);
7017  SCIPfreeBufferArray(scip, &nodedatas);
7018 
7019  return SCIP_OKAY;
7020 }
7021 
7022 /** checks whether the instance is infeasible due to a overload within a certain time frame using the idea of theta trees
7023  *
7024  * @note The algorithm is based on the paper: Petr Vilim, "Max Energy Filtering Algorithm for Discrete Cumulative
7025  * Resources". In: Willem Jan van Hoeve and John N. Hooker (Eds.), Integration of AI and OR Techniques in
7026  * Constraint Programming for Combinatorial Optimization Problems (CPAIOR 2009), LNCS 5547, pp 294--308
7027  */
7028 static
7030  SCIP* scip, /**< SCIP data structure */
7031  SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
7032  int nvars, /**< number of start time variables (activities) */
7033  SCIP_VAR** vars, /**< array of start time variables */
7034  int* durations, /**< array of durations */
7035  int* demands, /**< array of demands */
7036  int capacity, /**< cumulative capacity */
7037  int hmin, /**< left bound of time axis to be considered (including hmin) */
7038  int hmax, /**< right bound of time axis to be considered (not including hmax) */
7039  SCIP_CONS* cons, /**< constraint which is propagated */
7040  SCIP_Bool* initialized, /**< was conflict analysis initialized */
7041  SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */
7042  int* nchgbds, /**< pointer to store the number of bound changes */
7043  SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */
7044  )
7045 {
7046  /* check if a cutoff was already detected */
7047  if( (*cutoff) )
7048  return SCIP_OKAY;
7049 
7050  /* check if at least the basic overload checking should be preformed */
7051  if( !conshdlrdata->efcheck )
7052  return SCIP_OKAY;
7053 
7054  /* check for overload, which may result in a cutoff */
7055  SCIP_CALL( checkOverloadViaThetaTree(scip, conshdlrdata, nvars, vars, durations, demands, capacity, hmin, hmax,
7056  cons, TRUE, initialized, explanation, nchgbds, cutoff) );
7057 
7058  /* check if a cutoff was detected */
7059  if( (*cutoff) )
7060  return SCIP_OKAY;
7061 
7062  /* check if bound should be infer */
7063  if( !conshdlrdata->efinfer )
7064  return SCIP_OKAY;
7065 
7066  /* check for overload, which may result in a cutoff */
7067  SCIP_CALL( checkOverloadViaThetaTree(scip, conshdlrdata, nvars, vars, durations, demands, capacity, hmin, hmax,
7068  cons, FALSE, initialized, explanation, nchgbds, cutoff) );
7069 
7070  return SCIP_OKAY;
7071 }
7072 
7073 /** checks if the constraint is redundant; that is the case if its capacity can never be exceeded; therefore we check
7074  * with respect to the lower and upper bounds of the integer start time variables the maximum capacity usage for all
7075  * event points
7076  */
7077 static
7079  SCIP* scip, /**< SCIP data structure */
7080  int nvars, /**< number of start time variables (activities) */
7081  SCIP_VAR** vars, /**< array of start time variables */
7082  int* durations, /**< array of durations */
7083  int* demands, /**< array of demands */
7084  int capacity, /**< cumulative capacity */
7085  int hmin, /**< left bound of time axis to be considered (including hmin) */
7086  int hmax, /**< right bound of time axis to be considered (not including hmax) */
7087  SCIP_Bool* redundant /**< pointer to store whether this constraint is redundant */
7088  )
7089 {
7090 
7091  SCIP_VAR* var;
7092  int* starttimes; /* stores when each job is starting */
7093  int* endtimes; /* stores when each job ends */
7094  int* startindices; /* we will sort the startsolvalues, thus we need to know wich index of a job it corresponds to */
7095  int* endindices; /* we will sort the endsolvalues, thus we need to know wich index of a job it corresponds to */
7096 
7097  int lb;
7098  int ub;
7099  int freecapacity; /* remaining capacity */
7100  int curtime; /* point in time which we are just checking */
7101  int endindex; /* index of endsolvalues with: endsolvalues[endindex] > curtime */
7102  int njobs;
7103  int j;
7104 
7105  assert(scip != NULL);
7106  assert(redundant != NULL);
7107 
7108  (*redundant) = TRUE;
7109 
7110  /* if no activities are associated with this cumulative then this constraint is redundant */
7111  if( nvars == 0 )
7112  return SCIP_OKAY;
7113 
7114  assert(vars != NULL);
7115 
7116  SCIP_CALL( SCIPallocBufferArray(scip, &starttimes, nvars) );
7117  SCIP_CALL( SCIPallocBufferArray(scip, &endtimes, nvars) );
7118  SCIP_CALL( SCIPallocBufferArray(scip, &startindices, nvars) );
7119  SCIP_CALL( SCIPallocBufferArray(scip, &endindices, nvars) );
7120 
7121  njobs = 0;
7122 
7123  /* assign variables, start and endpoints to arrays */
7124  for( j = 0; j < nvars; ++j )
7125  {
7126  assert(durations[j] > 0);
7127  assert(demands[j] > 0);
7128 
7129  var = vars[j];
7130  assert(var != NULL);
7131 
7132  lb = convertBoundToInt(scip, SCIPvarGetLbLocal(var));
7133  ub = convertBoundToInt(scip, SCIPvarGetUbLocal(var));
7134 
7135  /* check if jobs runs completely outside of the effective time horizon */
7136  if( lb >= hmax || ub + durations[j] <= hmin )
7137  continue;
7138 
7139  starttimes[njobs] = MAX(lb, hmin);
7140  startindices[njobs] = j;
7141 
7142  endtimes[njobs] = MIN(ub + durations[j], hmax);
7143  endindices[njobs] = j;
7144  assert(starttimes[njobs] <= endtimes[njobs]);
7145  njobs++;
7146  }
7147 
7148  /* sort the arrays not-decreasing according to startsolvalues and endsolvalues (and sort the indices in the same way) */
7149  SCIPsortIntInt(starttimes, startindices, njobs);
7150  SCIPsortIntInt(endtimes, endindices, njobs);
7151 
7152  endindex = 0;
7153  freecapacity = capacity;
7154 
7155  /* check each start point of a job whether the capacity is violated or not */
7156  for( j = 0; j < njobs; ++j )
7157  {
7158  curtime = starttimes[j];
7159 
7160  /* stop checking, if time point is above hmax */
7161  if( curtime >= hmax )
7162  break;
7163 
7164  /* subtract all capacity needed up to this point */
7165  freecapacity -= demands[startindices[j]];
7166  while( j+1 < njobs && starttimes[j+1] == curtime )
7167  {
7168  ++j;
7169  freecapacity -= demands[startindices[j]];
7170  }
7171 
7172  /* free all capacity usages of jobs the are no longer running */
7173  while( endtimes[endindex] <= curtime )
7174  {
7175  freecapacity += demands[endindices[endindex]];
7176  ++endindex;
7177  }
7178  assert(freecapacity <= capacity);
7179 
7180  /* check freecapacity to be smaller than zero */
7181  if( freecapacity < 0 && curtime >= hmin )
7182  {
7183  (*redundant) = FALSE;
7184  break;
7185  }
7186  } /*lint --e{850}*/
7187 
7188  /* free all buffer arrays */
7189  SCIPfreeBufferArray(scip, &endindices);
7190  SCIPfreeBufferArray(scip, &startindices);
7191  SCIPfreeBufferArray(scip, &endtimes);
7192  SCIPfreeBufferArray(scip, &starttimes);
7193 
7194  return SCIP_OKAY;
7195 }
7196 
7197 /** creates the worst case resource profile, that is, all jobs are inserted with the earliest start and latest
7198  * completion time
7199  */
7200 static
7202  SCIP* scip, /**< SCIP data structure */
7203  SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
7204  SCIP_PROFILE* profile, /**< resource profile */
7205  int nvars, /**< number of variables (jobs) */
7206  SCIP_VAR** vars, /**< array of integer variable which corresponds to starting times for a job */
7207  int* durations, /**< array containing corresponding durations */
7208  int* demands, /**< array containing corresponding demands */
7209  int capacity, /**< cumulative capacity */
7210  int hmin, /**< left bound of time axis to be considered (including hmin) */
7211  int hmax, /**< right bound of time axis to be considered (not including hmax) */
7212  SCIP_Bool* initialized, /**< was conflict analysis initialized */
7213  SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */
7214  SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */
7215  )
7216 {
7217  int v;
7218 
7219  /* insert all cores */
7220  for( v = 0; v < nvars; ++v )
7221  {
7222  SCIP_VAR* var;
7223  SCIP_Bool infeasible;
7224  int duration;
7225  int demand;
7226  int begin;
7227  int end;
7228  int est;
7229  int lst;
7230  int pos;
7231 
7232  var = vars[v];
7233  assert(var != NULL);
7234  assert(SCIPisFeasIntegral(scip, SCIPvarGetLbLocal(var)));
7235  assert(SCIPisFeasIntegral(scip, SCIPvarGetUbLocal(var)));
7236 
7237  duration = durations[v];
7238  assert(duration > 0);
7239 
7240  demand = demands[v];
7241  assert(demand > 0);
7242 
7243  /* collect earliest and latest start time */
7244  est = convertBoundToInt(scip, SCIPvarGetLbLocal(var));
7245  lst = convertBoundToInt(scip, SCIPvarGetUbLocal(var));
7246 
7247  /* check if the job runs completely outside of the effective horizon [hmin, hmax); if so skip it */
7248  if( lst + duration <= hmin || est >= hmax )
7249  continue;
7250 
7251  /* compute core interval w.r.t. effective time horizon */
7252  begin = MAX(hmin, lst);
7253  end = MIN(hmax, est + duration);
7254 
7255  /* check if a core exists */
7256  if( begin >= end )
7257  continue;
7258 
7259  SCIPdebugMessage("variable <%s>[%d,%d] (duration %d, demand %d): add core [%d,%d)\n",
7260  SCIPvarGetName(var), est, lst, duration, demand, begin, end);
7261 
7262  /* insert the core into core resource profile (complexity O(log n)) */
7263  SCIP_CALL( SCIPprofileInsertCore(profile, begin, end, demand, &pos, &infeasible) );
7264 
7265  /* in case the insertion of the core leads to an infeasibility; start the conflict analysis */
7266  if( infeasible )
7267  {
7268  assert(begin <= SCIPprofileGetTime(profile, pos));
7269  assert(end > SCIPprofileGetTime(profile, pos));
7270 
7271  /* use conflict analysis to analysis the core insertion which was infeasible */
7272  SCIP_CALL( analyseInfeasibelCoreInsertion(scip, nvars, vars, durations, demands, capacity, hmin, hmax,
7273  var, duration, demand, SCIPprofileGetTime(profile, pos), conshdlrdata->usebdwidening, initialized, explanation) );
7274 
7275  if( explanation != NULL )
7276  explanation[v] = TRUE;
7277 
7278  (*cutoff) = TRUE;
7279 
7280  /* for the statistic we count the number of times a cutoff was detected due the time-time */
7281  SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->ncutofftimetable++ );
7282 
7283  break;
7284  }
7285  }
7286 
7287  return SCIP_OKAY;
7288 }
7289 
7290 /** propagate the cumulative condition */
7291 static
7293  SCIP* scip, /**< SCIP data structure */
7294  SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
7295  int nvars, /**< number of start time variables (activities) */
7296  SCIP_VAR** vars, /**< array of start time variables */
7297  int* durations, /**< array of durations */
7298  int* demands, /**< array of demands */
7299  int capacity, /**< cumulative capacity */
7300  int hmin, /**< left bound of time axis to be considered (including hmin) */
7301  int hmax, /**< right bound of time axis to be considered (not including hmax) */
7302  SCIP_CONS* cons, /**< constraint which is propagated (needed to SCIPinferVar**Cons()) */
7303  int* nchgbds, /**< pointer to store the number of bound changes */
7304  SCIP_Bool* redundant, /**< pointer to store if the constraint is redundant */
7305  SCIP_Bool* initialized, /**< was conflict analysis initialized */
7306  SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */
7307  SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */
7308  )
7309 {
7310  SCIP_PROFILE* profile;
7311 
7312  assert(nchgbds != NULL);
7313  assert(initialized != NULL);
7314  assert(cutoff != NULL);
7315  assert((*cutoff) == FALSE);
7316 
7317  /**@todo avoid always sorting the variable array */
7318 
7319  /* check if the constraint is redundant */
7320  SCIP_CALL( consCheckRedundancy(scip, nvars, vars, durations, demands, capacity, hmin, hmax, redundant) );
7321 
7322  if( *redundant )
7323  return SCIP_OKAY;
7324 
7325  /* create an empty resource profile for profiling the cores of the jobs */
7326  SCIP_CALL( SCIPprofileCreate(&profile, capacity) );
7327 
7328  /* create core profile (compulsory parts) */
7329  SCIP_CALL( createCoreProfile(scip, conshdlrdata, profile, nvars, vars, durations, demands, capacity, hmin, hmax,
7330  initialized, explanation, cutoff) );
7331 
7332  /* propagate the job cores until nothing else can be detected */
7333  SCIP_CALL( propagateTimetable(scip, conshdlrdata, profile, nvars, vars, durations, demands, capacity, hmin, hmax, cons,
7334  nchgbds, initialized, explanation, cutoff) );
7335 
7336  /* run edge finding propagator */
7337  SCIP_CALL( propagateEdgeFinding(scip, conshdlrdata, nvars, vars, durations, demands, capacity, hmin, hmax,
7338  cons, initialized, explanation, nchgbds, cutoff) );
7339 
7340  /* run time-table edge-finding propagator */
7341  SCIP_CALL( propagateTTEF(scip, conshdlrdata, profile, nvars, vars, durations, demands, capacity, hmin, hmax, cons,
7342  nchgbds, initialized, explanation, cutoff) );
7343 
7344  /* free resource profile */
7345  SCIPprofileFree(&profile);
7346 
7347  return SCIP_OKAY;
7348 }
7349 
7350 /** propagate the cumulative constraint */
7351 static
7353  SCIP* scip, /**< SCIP data structure */
7354  SCIP_CONS* cons, /**< constraint to propagate */
7355  SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
7356  int* nchgbds, /**< pointer to store the number of bound changes */
7357  int* ndelconss, /**< pointer to store the number of deleted constraints */
7358  SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */
7359  )
7360 {
7361  SCIP_CONSDATA* consdata;
7362  SCIP_Bool initialized;
7363  SCIP_Bool redundant;
7364  int oldnchgbds;
7365 
7366  assert(scip != NULL);
7367  assert(cons != NULL);
7368 
7369  consdata = SCIPconsGetData(cons);
7370  assert(consdata != NULL);
7371 
7372  oldnchgbds = *nchgbds;
7373  initialized = FALSE;
7374  redundant = FALSE;
7375 
7376  if( SCIPconsIsDeleted(cons) )
7377  {
7378  assert(SCIPinProbing(scip));
7379  return SCIP_OKAY;
7380  }
7381 
7382  /* if the constraint marked to be propagated, do nothing */
7383  if( consdata->propagated && SCIPgetStage(scip) != SCIP_STAGE_PRESOLVING )
7384  return SCIP_OKAY;
7385 
7386  SCIP_CALL( propagateCumulativeCondition(scip, conshdlrdata,
7387  consdata->nvars, consdata->vars, consdata->durations, consdata->demands, consdata->capacity,
7388  consdata->hmin, consdata->hmax, cons,
7389  nchgbds, &redundant, &initialized, NULL, cutoff) );
7390 
7391  if( redundant )
7392  {
7393  SCIPdebugMessage("%s deletes cumulative constraint <%s> since it is redundant\n",
7394  SCIPgetDepth(scip) == 0 ? "globally" : "locally", SCIPconsGetName(cons));
7395 
7396  if( !SCIPinProbing(scip) )
7397  {
7398  SCIP_CALL( SCIPdelConsLocal(scip, cons) );
7399  (*ndelconss)++;
7400  }
7401  }
7402  else
7403  {
7404  if( initialized )
7405  {
7406  /* run conflict analysis since it was initialized */
7407  assert(*cutoff == TRUE);
7408  SCIPdebugMessage("start conflict analysis\n");
7409  SCIP_CALL( SCIPanalyzeConflictCons(scip, cons, NULL) );
7410  }
7411 
7412  /* if successful, reset age of constraint */
7413  if( *cutoff || *nchgbds > oldnchgbds )
7414  {
7415  SCIP_CALL( SCIPresetConsAge(scip, cons) );
7416  }
7417  else
7418  {
7419  /* mark the constraint to be propagated */
7420  consdata->propagated = TRUE;
7421  }
7422  }
7423 
7424  return SCIP_OKAY;
7425 }
7426 
7427 /** it is dual feasible to remove the values {leftub+1, ..., rightlb-1} since SCIP current does not feature domain holes
7428  * we use the probing mode to check if one of the two branches is infeasible. If this is the case the dual redundant can
7429  * be realize as domain reduction. Otherwise we do nothing
7430  */
7431 static
7433  SCIP* scip, /**< SCIP data structure */
7434  SCIP_VAR** vars, /**< problem variables */
7435  int nvars, /**< number of problem variables */
7436  int probingpos, /**< variable number to apply probing on */
7437  SCIP_Real leftub, /**< upper bound of probing variable in left branch */
7438  SCIP_Real rightlb, /**< lower bound of probing variable in right branch */
7439  SCIP_Real* leftimpllbs, /**< lower bounds after applying implications and cliques in left branch, or NULL */
7440  SCIP_Real* leftimplubs, /**< upper bounds after applying implications and cliques in left branch, or NULL */
7441  SCIP_Real* leftproplbs, /**< lower bounds after applying domain propagation in left branch */
7442  SCIP_Real* leftpropubs, /**< upper bounds after applying domain propagation in left branch */
7443  SCIP_Real* rightimpllbs, /**< lower bounds after applying implications and cliques in right branch, or NULL */
7444  SCIP_Real* rightimplubs, /**< upper bounds after applying implications and cliques in right branch, or NULL */
7445  SCIP_Real* rightproplbs, /**< lower bounds after applying domain propagation in right branch */
7446  SCIP_Real* rightpropubs, /**< upper bounds after applying domain propagation in right branch */
7447  int* nfixedvars, /**< pointer to counter which is increased by the number of deduced variable fixations */
7448  SCIP_Bool* success, /**< buffer to store whether a probing succeed to dual fix the variable */
7449  SCIP_Bool* cutoff /**< buffer to store whether a cutoff is detected */
7450  )
7451 {
7452  SCIP_VAR* var;
7453  SCIP_Bool tightened;
7454 
7455  assert(probingpos >= 0);
7456  assert(probingpos < nvars);
7457  assert(success != NULL);
7458  assert(cutoff != NULL);
7459 
7460  var = vars[probingpos];
7461  assert(var != NULL);
7462  assert(SCIPisGE(scip, leftub, SCIPvarGetLbLocal(var)));
7463  assert(SCIPisLE(scip, leftub, SCIPvarGetUbLocal(var)));
7464  assert(SCIPisGE(scip, rightlb, SCIPvarGetLbLocal(var)));
7465  assert(SCIPisLE(scip, rightlb, SCIPvarGetUbLocal(var)));
7466 
7467  (*success) = FALSE;
7468 
7469  if( SCIPinProbing(scip) || SCIPinRepropagation(scip) )
7470  return SCIP_OKAY;
7471 
7472  /* apply probing for the earliest start time (lower bound) of the variable (x <= est) */
7473  SCIP_CALL( SCIPapplyProbingVar(scip, vars, nvars, probingpos, SCIP_BOUNDTYPE_UPPER, leftub, -1,
7474  leftimpllbs, leftimplubs, leftproplbs, leftpropubs, cutoff) );
7475 
7476  if( (*cutoff) )
7477  {
7478  /* note that cutoff may occur if presolving has not been executed fully */
7479  SCIP_CALL( SCIPtightenVarLb(scip, var, rightlb, TRUE, cutoff, &tightened) );
7480 
7481  if( tightened )
7482  {
7483  (*success) =TRUE;
7484  (*nfixedvars)++;
7485  }
7486 
7487  return SCIP_OKAY;
7488  }
7489 
7490  /* note that probing can change the upper bound and thus the right branch may have been detected infeasible if
7491  * presolving has not been executed fully
7492  */
7493  if( SCIPisGT(scip, rightlb, SCIPvarGetUbLocal(var)) )
7494  {
7495  /* note that cutoff may occur if presolving has not been executed fully */
7496  SCIP_CALL( SCIPtightenVarUb(scip, var, leftub, TRUE, cutoff, &tightened) );
7497 
7498  if( tightened )
7499  {
7500  (*success) = TRUE;
7501  (*nfixedvars)++;
7502  }
7503 
7504  return SCIP_OKAY;
7505  }
7506 
7507  /* apply probing for the alternative lower bound of the variable (x <= alternativeubs[v]) */
7508  SCIP_CALL( SCIPapplyProbingVar(scip, vars, nvars, probingpos, SCIP_BOUNDTYPE_LOWER, rightlb, -1,
7509  rightimpllbs, rightimplubs, rightproplbs, rightpropubs, cutoff) );
7510 
7511  if( (*cutoff) )
7512  {
7513  /* note that cutoff may occur if presolving has not been executed fully */
7514  SCIP_CALL( SCIPtightenVarUb(scip, var, leftub, TRUE, cutoff, &tightened) );
7515 
7516  if( tightened )
7517  {
7518  (*success) =TRUE;
7519  (*nfixedvars)++;
7520  }
7521 
7522  return SCIP_OKAY;
7523  }
7524 
7525  return SCIP_OKAY;
7526 }
7527 
7528 /** is it possible, to round variable down w.r.t. objective function */
7529 static
7531  SCIP* scip, /**< SCIP data structure */
7532  SCIP_VAR* var, /**< problem variable */
7533  SCIP_Bool* roundable /**< pointer to store if the variable can be rounded down */
7534  )
7535 {
7536  SCIP_Real objval;
7537  int scalar;
7538 
7539  assert(roundable != NULL);
7540 
7541  *roundable = TRUE;
7542 
7543  /* a fixed variable can be definition always be safely rounded */
7545  return SCIP_OKAY;
7546 
7547  /* in case the variable is not active we need to check the object coefficient of the active variable */
7548  if( !SCIPvarIsActive(var) )
7549  {
7550  SCIP_VAR* actvar;
7551  int constant;
7552 
7553  actvar = var;
7554 
7555  SCIP_CALL( getActiveVar(scip, &actvar, &scalar, &constant) );
7556  assert(scalar != 0);
7557 
7558  objval = scalar * SCIPvarGetObj(actvar);
7559  }
7560  else
7561  {
7562  scalar = 1;
7563  objval = SCIPvarGetObj(var);
7564  }
7565 
7566  /* rounding the integer variable down is only a valid dual reduction if the object coefficient is zero or positive
7567  * (the transformed problem is always a minimization problem)
7568  *
7569  * @note that we need to check this condition w.r.t. active variable space
7570  */
7571  if( (scalar > 0 && SCIPisNegative(scip, objval)) || (scalar < 0 && SCIPisPositive(scip, objval)) )
7572  *roundable = FALSE;
7573 
7574  return SCIP_OKAY;
7575 }
7576 
7577 /** is it possible, to round variable up w.r.t. objective function */
7578 static
7580  SCIP* scip, /**< SCIP data structure */
7581  SCIP_VAR* var, /**< problem variable */
7582  SCIP_Bool* roundable /**< pointer to store if the variable can be rounded down */
7583  )
7584 {
7585  SCIP_Real objval;
7586  int scalar;
7587 
7588  assert(roundable != NULL);
7589 
7590  *roundable = TRUE;
7591 
7592  /* a fixed variable can be definition always be safely rounded */
7594  return SCIP_OKAY;
7595 
7596  /* in case the variable is not active we need to check the object coefficient of the active variable */
7597  if( !SCIPvarIsActive(var) )
7598  {
7599  SCIP_VAR* actvar;
7600  int constant;
7601 
7602  actvar = var;
7603 
7604  SCIP_CALL( getActiveVar(scip, &actvar, &scalar, &constant) );
7605  assert(scalar != 0);
7606 
7607  objval = scalar * SCIPvarGetObj(actvar);
7608  }
7609  else
7610  {
7611  scalar = 1;
7612  objval = SCIPvarGetObj(var);
7613  }
7614 
7615  /* rounding the integer variable up is only a valid dual reduction if the object coefficient is zero or negative
7616  * (the transformed problem is always a minimization problem)
7617  *
7618  * @note that we need to check this condition w.r.t. active variable space
7619  */
7620  if( (scalar > 0 && SCIPisPositive(scip, objval)) || (scalar < 0 && SCIPisNegative(scip, objval)) )
7621  *roundable = FALSE;
7622 
7623  return SCIP_OKAY;
7624 }
7625 
7626 /** For each variable we compute an alternative lower and upper bounds. That is, if the variable is not fixed to its
7627  * lower or upper bound the next reasonable lower or upper bound would be this alternative bound (implying that certain
7628  * values are not of interest). An alternative bound for a particular is only valied if the cumulative constarints are
7629  * the only one locking this variable in the corresponding direction.
7630  */
7631 static
7633  SCIP* scip, /**< SCIP data structure */
7634  SCIP_CONS** conss, /**< array of cumulative constraint constraints */
7635  int nconss, /**< number of cumulative constraints */
7636  SCIP_Bool local, /**< use local bounds effective horizon? */
7637  int* alternativelbs, /**< alternative lower bounds */
7638  int* alternativeubs, /**< alternative lower bounds */
7639  int* downlocks, /**< number of constraints with down lock participating by the computation */
7640  int* uplocks /**< number of constraints with up lock participating by the computation */
7641  )
7642 {
7643  int nvars;
7644  int c;
7645  int v;
7646 
7647  for( c = 0; c < nconss; ++c )
7648  {
7649  SCIP_CONSDATA* consdata;
7650  SCIP_CONS* cons;
7651  SCIP_VAR* var;
7652  int hmin;
7653  int hmax;
7654 
7655  cons = conss[c];
7656  assert(cons != NULL);
7657 
7658  /* ignore constraints which are already deletet and those which are not check constraints */
7659  if( SCIPconsIsDeleted(cons) || !SCIPconsIsChecked(cons) )
7660  continue;
7661 
7662  consdata = SCIPconsGetData(cons);
7663  assert(consdata != NULL);
7664  assert(consdata->nvars > 1);
7665 
7666  /* compute the hmin and hmax */
7667  if( local )
7668  {
7669  SCIP_PROFILE* profile;
7670 
7671  /* create empty resource profile with infinity resource capacity */
7672  SCIP_CALL( SCIPprofileCreate(&profile, INT_MAX) );
7673 
7674  /* create worst case resource profile */
7675  SCIP_CALL( SCIPcreateWorstCaseProfile(scip, profile, consdata->nvars, consdata->vars, consdata->durations, consdata->demands) );
7676 
7677  hmin = SCIPcomputeHmin(scip, profile, consdata->capacity);
7678  hmax = SCIPcomputeHmax(scip, profile, consdata->capacity);
7679 
7680  /* free worst case profile */
7681  SCIPprofileFree(&profile);
7682  }
7683  else
7684  {
7685  hmin = consdata->hmin;
7686  hmax = consdata->hmax;
7687  }
7688 
7689  consdata = SCIPconsGetData(cons);
7690  assert(consdata != NULL);
7691 
7692  nvars = consdata->nvars;
7693 
7694  for( v = 0; v < nvars; ++v )
7695  {
7696  int scalar;
7697  int constant;
7698  int idx;
7699 
7700  var = consdata->vars[v];
7701  assert(var != NULL);
7702 
7703  /* multi-aggregated variables should appear here since we mark the variables to be not mutlt-aggregated */
7704  assert(SCIPvarGetStatus(var) != SCIP_VARSTATUS_MULTAGGR);
7705 
7706  /* ignore variable locally fixed variables */
7707  if( SCIPvarGetUbLocal(var) - SCIPvarGetLbLocal(var) < 0.5 )
7708  continue;
7709 
7710 
7711  SCIP_CALL( getActiveVar(scip, &var, &scalar, &constant) );
7712  idx = SCIPvarGetProbindex(var);
7713  assert(idx >= 0);
7714 
7715  /* first check lower bound fixing */
7716  if( consdata->downlocks[v] )
7717  {
7718  int ect;
7719  int est;
7720 
7721  /* the variable has a down locked */
7722  est = scalar * convertBoundToInt(scip, SCIPvarGetLbLocal(var)) + constant;
7723  ect = est + consdata->durations[v];
7724 
7725  if( ect <= hmin || hmin >= hmax )
7726  downlocks[idx]++;
7727  else if( est < hmin && alternativelbs[idx] >= (hmin + 1 - constant) / scalar )
7728  {
7729  alternativelbs[idx] = (hmin + 1 - constant) / scalar;
7730  downlocks[idx]++;
7731  }
7732  }
7733 
7734  /* second check upper bound fixing */
7735  if( consdata->uplocks[v] )
7736  {
7737  int duration;
7738  int lct;
7739  int lst;
7740 
7741  duration = consdata->durations[v];
7742 
7743  /* the variable has a up lock locked */
7744  lst = scalar * convertBoundToInt(scip, SCIPvarGetUbLocal(var)) + constant;
7745  lct = lst + duration;
7746 
7747  if( lst >= hmax || hmin >= hmax )
7748  uplocks[idx]++;
7749  else if( lct > hmax && alternativeubs[idx] <= ((hmax - 1 - constant) / scalar) - duration )
7750  {
7751  alternativeubs[idx] = ((hmax - 1 - constant) / scalar) - duration;
7752  uplocks[idx]++;
7753  }
7754  }
7755  }
7756  }
7757 
7758  return SCIP_OKAY;
7759 }
7760 
7761 /** apply all fixings which are given by the alternative bounds */
7762 static
7764  SCIP* scip, /**< SCIP data structure */
7765  SCIP_VAR** vars, /**< array of active variables */
7766  int nvars, /**< number of active variables */
7767  int* alternativelbs, /**< alternative lower bounds */
7768  int* alternativeubs, /**< alternative lower bounds */
7769  int* downlocks, /**< number of constraints with down lock participating by the computation */
7770  int* uplocks, /**< number of constraints with up lock participating by the computation */
7771  int* nfixedvars, /**< pointer to counter which is increased by the number of deduced variable fixations */
7772  SCIP_Bool* cutoff /**< buffer to store whether a cutoff is detected */
7773  )
7774 {
7775  SCIP_Real* downimpllbs;
7776  SCIP_Real* downimplubs;
7777  SCIP_Real* downproplbs;
7778  SCIP_Real* downpropubs;
7779  SCIP_Real* upimpllbs;
7780  SCIP_Real* upimplubs;
7781  SCIP_Real* upproplbs;
7782  SCIP_Real* uppropubs;
7783  int v;
7784 
7785  /* get temporary memory for storing probing results */
7786  SCIP_CALL( SCIPallocBufferArray(scip, &downimpllbs, nvars) );
7787  SCIP_CALL( SCIPallocBufferArray(scip, &downimplubs, nvars) );
7788  SCIP_CALL( SCIPallocBufferArray(scip, &downproplbs, nvars) );
7789  SCIP_CALL( SCIPallocBufferArray(scip, &downpropubs, nvars) );
7790  SCIP_CALL( SCIPallocBufferArray(scip, &upimpllbs, nvars) );
7791  SCIP_CALL( SCIPallocBufferArray(scip, &upimplubs, nvars) );
7792  SCIP_CALL( SCIPallocBufferArray(scip, &upproplbs, nvars) );
7793  SCIP_CALL( SCIPallocBufferArray(scip, &uppropubs, nvars) );
7794 
7795  for( v = 0; v < nvars; ++v )
7796  {
7797  SCIP_VAR* var;
7798  SCIP_Bool infeasible;
7799  SCIP_Bool fixed;
7800  SCIP_Bool roundable;
7801  int ub;
7802  int lb;
7803 
7804  var = vars[v];
7805  assert(var != NULL);
7806 
7807  lb = convertBoundToInt(scip, SCIPvarGetLbLocal(var));
7808  ub = convertBoundToInt(scip, SCIPvarGetUbLocal(var));
7809 
7810  /* ignore fixed variables */
7811  if( ub - lb <= 0 )
7812  continue;
7813 
7814 
7815  if( SCIPvarGetNLocksDown(var) == downlocks[v] )
7816  {
7817  SCIP_CALL( varMayRoundDown(scip, var, &roundable) );
7818 
7819  if( roundable )
7820  {
7821  if( alternativelbs[v] > ub )
7822  {
7823  SCIP_CALL( SCIPfixVar(scip, var, SCIPvarGetLbLocal(var), &infeasible, &fixed) );
7824  assert(!infeasible);
7825  assert(fixed);
7826 
7827  (*nfixedvars)++;
7828 
7829  /* for the statistic we count the number of jobs which are dual fixed due the information of all cumulative
7830  * constraints
7831  */
7832  SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->nallconsdualfixs++ );
7833  }
7834  else
7835  {
7836  SCIP_Bool success;
7837 
7838  /* In the current version SCIP, variable domains are single intervals. Meaning that domain holes or not
7839  * representable. To retrieve a potential dual reduction we using probing to check both branches. If one in
7840  * infeasible we can apply the dual reduction; otherwise we do nothing
7841  */
7842  SCIP_CALL( applyProbingVar(scip, vars, nvars, v, (SCIP_Real) lb, (SCIP_Real) alternativelbs[v],
7843  downimpllbs, downimplubs, downproplbs, downpropubs, upimpllbs, upimplubs, upproplbs, uppropubs,
7844  nfixedvars, &success, cutoff) );
7845 
7846  if( success )
7847  {
7848  SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->nallconsdualfixs++ );
7849  }
7850 
7851  }
7852  }
7853  }
7854 
7855  lb = convertBoundToInt(scip, SCIPvarGetLbLocal(var));
7856  ub = convertBoundToInt(scip, SCIPvarGetUbLocal(var));
7857 
7858  /* ignore fixed variables */
7859  if( ub - lb <= 0 )
7860  continue;
7861 
7862  if( SCIPvarGetNLocksUp(var) == uplocks[v] )
7863  {
7864  SCIP_CALL( varMayRoundUp(scip, var, &roundable) );
7865 
7866  if( roundable )
7867  {
7868  if( alternativeubs[v] < lb )
7869  {
7870  SCIP_CALL( SCIPfixVar(scip, var, SCIPvarGetUbLocal(var), &infeasible, &fixed) );
7871  assert(!infeasible);
7872  assert(fixed);
7873 
7874  (*nfixedvars)++;
7875 
7876  /* for the statistic we count the number of jobs which are dual fixed due the information of all cumulative
7877  * constraints
7878  */
7879  SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->nallconsdualfixs++ );
7880  }
7881  else
7882  {
7883  SCIP_Bool success;
7884 
7885  /* In the current version SCIP, variable domains are single intervals. Meaning that domain holes or not
7886  * representable. To retrieve a potential dual reduction we using probing to check both branches. If one in
7887  * infeasible we can apply the dual reduction; otherwise we do nothing
7888  */
7889  SCIP_CALL( applyProbingVar(scip, vars, nvars, v, (SCIP_Real) alternativeubs[v], (SCIP_Real) ub,
7890  downimpllbs, downimplubs, downproplbs, downpropubs, upimpllbs, upimplubs, upproplbs, uppropubs,
7891  nfixedvars, &success, cutoff) );
7892 
7893  if( success )
7894  {
7895  SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->nallconsdualfixs++ );
7896  }
7897  }
7898  }
7899  }
7900  }
7901 
7902  /* free temporary memory */
7903  SCIPfreeBufferArray(scip, &uppropubs);
7904  SCIPfreeBufferArray(scip, &upproplbs);
7905  SCIPfreeBufferArray(scip, &upimplubs);
7906  SCIPfreeBufferArray(scip, &upimpllbs);
7907  SCIPfreeBufferArray(scip, &downpropubs);
7908  SCIPfreeBufferArray(scip, &downproplbs);
7909  SCIPfreeBufferArray(scip, &downimplubs);
7910  SCIPfreeBufferArray(scip, &downimpllbs);
7911 
7912  return SCIP_OKAY;
7913 }
7914 
7915 /** propagate all constraints together */
7916 static
7918  SCIP* scip, /**< SCIP data structure */
7919  SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
7920  SCIP_CONS** conss, /**< all cumulative constraint */
7921  int nconss, /**< number of cumulative constraints */
7922  SCIP_Bool local, /**< use local bounds effective horizon? */
7923  int* nfixedvars, /**< pointer to counter which is increased by the number of deduced variable fixations */
7924  SCIP_Bool* cutoff, /**< buffer to store whether a cutoff is detected */
7925  SCIP_Bool* branched /**< pointer to store if a branching was applied, or NULL to avoid branching */
7926  )
7927 { /*lint --e{715}*/
7928  SCIP_VAR** vars;
7929  int* downlocks;
7930  int* uplocks;
7931  int* alternativelbs;
7932  int* alternativeubs;
7933  int oldnfixedvars;
7934  int nvars;
7935  int v;
7936 
7937  if( SCIPinProbing(scip) || SCIPinRepropagation(scip) )
7938  return SCIP_OKAY;
7939 
7940  nvars = SCIPgetNVars(scip);
7941  oldnfixedvars = *nfixedvars;
7942 
7943  SCIP_CALL( SCIPduplicateBufferArray(scip, &vars, SCIPgetVars(scip), nvars) );
7944  SCIP_CALL( SCIPallocBufferArray(scip, &downlocks, nvars) );
7945  SCIP_CALL( SCIPallocBufferArray(scip, &uplocks, nvars) );
7946  SCIP_CALL( SCIPallocBufferArray(scip, &alternativelbs, nvars) );
7947  SCIP_CALL( SCIPallocBufferArray(scip, &alternativeubs, nvars) );
7948 
7949  /* initialize arrays */
7950  for( v = 0; v < nvars; ++v )
7951  {
7952  downlocks[v] = 0;
7953  uplocks[v] = 0;
7954  alternativelbs[v] = INT_MAX;
7955  alternativeubs[v] = INT_MIN;
7956  }
7957 
7958  /* compute alternative bounds */
7959  SCIP_CALL( computeAlternativeBounds(scip, conss, nconss, local, alternativelbs, alternativeubs, downlocks, uplocks) );
7960 
7961  /* apply fixing which result of the alternative bounds directly */
7962  SCIP_CALL( applyAlternativeBoundsFixing(scip, vars, nvars, alternativelbs, alternativeubs, downlocks, uplocks,
7963  nfixedvars, cutoff) );
7964 
7965  if( !(*cutoff) && oldnfixedvars == *nfixedvars && branched != NULL )
7966  {
7967  SCIP_CALL( applyAlternativeBoundsBranching(scip, vars, nvars, alternativelbs, alternativeubs, downlocks, uplocks, branched) );
7968  }
7969 
7970  /* free all buffers */
7971  SCIPfreeBufferArray(scip, &alternativeubs);
7972  SCIPfreeBufferArray(scip, &alternativelbs);
7973  SCIPfreeBufferArray(scip, &uplocks);
7974  SCIPfreeBufferArray(scip, &downlocks);
7975  SCIPfreeBufferArray(scip, &vars);
7976 
7977  return SCIP_OKAY;
7978 }
7979 
7980 /**@} */
7981 
7982 /**@name Linear relaxations
7983  *
7984  * @{
7985  */
7986 
7987 /** creates covering cuts for jobs violating resource constraints */
7988 static
7990  SCIP* scip, /**< SCIP data structure */
7991  SCIP_CONS* cons, /**< constraint to be checked */
7992  int* startvalues, /**< upper bounds on finishing time per job for activities from 0,..., nactivities -1 */
7993  int time /**< at this point in time covering constraints are valid */
7994  )
7995 {
7996  SCIP_CONSDATA* consdata;
7997  SCIP_ROW* row;
7998  int* flexibleids;
7999  int* demands;
8000 
8001  char rowname[SCIP_MAXSTRLEN];
8002 
8003  int remainingcap;
8004  int smallcoversize; /* size of a small cover */
8005  int bigcoversize; /* size of a big cover */
8006  int nvars;
8007 
8008  int nflexible;
8009  int sumdemand; /* demand of all jobs up to a certain index */
8010  int j;
8011 
8012  assert(cons != NULL);
8013 
8014  /* get constraint data structure */
8015  consdata = SCIPconsGetData(cons);
8016  assert(consdata != NULL );
8017 
8018  nvars = consdata->nvars;
8019 
8020  /* sort jobs according to demands */
8021  SCIP_CALL( SCIPallocBufferArray(scip, &demands, nvars) );
8022  SCIP_CALL( SCIPallocBufferArray(scip, &flexibleids, nvars) );
8023 
8024  nflexible = 0;
8025  remainingcap = consdata->capacity;
8026 
8027  /* get all jobs intersecting point 'time' with their bounds */
8028  for( j = 0; j < nvars; ++j )
8029  {
8030  int ub;
8031 
8032  ub = convertBoundToInt(scip, SCIPvarGetUbLocal(consdata->vars[j]));
8033 
8034  /* only add jobs to array if they intersect with point 'time' */
8035  if( startvalues[j] <= time && ub + consdata->durations[j] > time )
8036  {
8037  /* if job is fixed, capacity has to be decreased */
8038  if( startvalues[j] == ub )
8039  {
8040  remainingcap -= consdata->demands[j];
8041  }
8042  else
8043  {
8044  demands[nflexible] = consdata->demands[j];
8045  flexibleids[nflexible] = j;
8046  ++nflexible;
8047  }
8048  }
8049  }
8050  assert(remainingcap >= 0);
8051 
8052  /* sort demands and job ids */
8053  SCIPsortIntInt(demands, flexibleids, nflexible);
8054 
8055  /*
8056  * version 1:
8057  * D_j := sum_i=0,...,j d_i, finde j maximal, so dass D_j <= remainingcap
8058  * erzeuge cover constraint
8059  *
8060  */
8061 
8062  /* find maximum number of jobs that can run in parallel (-->coversize = j) */
8063  sumdemand = 0;
8064  j = 0;
8065 
8066  while( j < nflexible && sumdemand <= remainingcap )
8067  {
8068  sumdemand += demands[j];
8069  j++;
8070  }
8071 
8072  /* j jobs form a conflict, set coversize to 'j - 1' */
8073  bigcoversize = j-1;
8074  assert(sumdemand > remainingcap);
8075  assert(bigcoversize < nflexible);
8076 
8077  /* - create a row for all jobs and their binary variables.
8078  * - at most coversize many binary variables of jobs can be set to one
8079  */
8080 
8081  /* construct row name */
8082  (void)SCIPsnprintf(rowname, SCIP_MAXSTRLEN, "capacity_coverbig_%d", time);
8083  SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, SCIPconsGetHdlr(cons), rowname, -SCIPinfinity(scip), (SCIP_Real)bigcoversize,
8084  SCIPconsIsLocal(cons), SCIPconsIsModifiable(cons), TRUE) );
8085  SCIP_CALL( SCIPcacheRowExtensions(scip, row) );
8086 
8087  for( j = 0; j < nflexible; ++j )
8088  {
8089  SCIP_VAR** binvars;
8090  int* vals;
8091  int nbinvars;
8092  int idx;
8093  int start;
8094  int end;
8095  int lb;
8096  int ub;
8097  int b;
8098 
8099  idx = flexibleids[j];
8100 
8101  /* get and add binvars into var array */
8102  SCIP_CALL( SCIPgetBinvarsLinking(scip, consdata->linkingconss[idx], &binvars, &nbinvars) );
8103  assert(nbinvars != 0);
8104 
8105  vals = SCIPgetValsLinking(scip, consdata->linkingconss[idx]);
8106  assert(vals != NULL);
8107 
8108  lb = convertBoundToInt(scip, SCIPvarGetLbLocal(consdata->vars[idx]));
8109  ub = convertBoundToInt(scip, SCIPvarGetUbLocal(consdata->vars[idx]));
8110 
8111  /* compute start and finishing time */
8112  start = time - consdata->durations[idx] + 1;
8113  end = MIN(time, ub);
8114 
8115  /* add all neccessary binary variables */
8116  for( b = 0; b < nbinvars; ++b )
8117  {
8118  if( vals[b] < start || vals[b] < lb )
8119  continue;
8120 
8121  if( vals[b] > end )
8122  break;
8123 
8124  assert(binvars[b] != NULL);
8125  SCIP_CALL( SCIPaddVarToRow(scip, row, binvars[b], 1.0) );
8126  }
8127  }
8128 
8129  /* insert and release row */
8130  SCIP_CALL( SCIPflushRowExtensions(scip, row) );
8131 
8132  if( consdata->bcoverrowssize == 0 )
8133  {
8134  consdata->bcoverrowssize = 10;
8135  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->bcoverrows, consdata->bcoverrowssize) );
8136  }
8137  if( consdata->nbcoverrows == consdata->bcoverrowssize )
8138  {
8139  consdata->bcoverrowssize *= 2;
8140  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->bcoverrows, consdata->nbcoverrows, consdata->bcoverrowssize) );
8141  }
8142 
8143  consdata->bcoverrows[consdata->nbcoverrows] = row;
8144  consdata->nbcoverrows++;
8145 
8146  /*
8147  * version 2:
8148  * D_j := sum_i=j,...,0 d_i, finde j minimal, so dass D_j <= remainingcap
8149  * erzeuge cover constraint und fuege alle jobs i hinzu, mit d_i = d_largest
8150  */
8151  /* find maximum number of jobs that can run in parallel (= coversize -1) */
8152  sumdemand = 0;
8153  j = nflexible -1;
8154  while( sumdemand <= remainingcap )
8155  {
8156  assert(j >= 0);
8157  sumdemand += demands[j];
8158  j--;
8159  }
8160 
8161  smallcoversize = nflexible - (j + 1) - 1;
8162  while( j > 0 && demands[j] == demands[nflexible-1] )
8163  --j;
8164 
8165  assert(smallcoversize < nflexible);
8166 
8167  if( smallcoversize != 1 || smallcoversize != nflexible - (j + 1) - 1 )
8168  {
8169  /* construct row name */
8170  (void)SCIPsnprintf(rowname, SCIP_MAXSTRLEN, "capacity_coversmall_%d", time);
8171  SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, SCIPconsGetHdlr(cons), rowname, -SCIPinfinity(scip), (SCIP_Real)smallcoversize,
8172  SCIPconsIsLocal(cons), SCIPconsIsModifiable(cons), TRUE) );
8173  SCIP_CALL( SCIPcacheRowExtensions(scip, row) );
8174 
8175  /* filter binary variables for each unfixed job */
8176  for( j = j + 1; j < nflexible; ++j )
8177  {
8178  SCIP_VAR** binvars;
8179  int* vals;
8180  int nbinvars;
8181  int idx;
8182  int start;
8183  int end;
8184  int lb;
8185  int ub;
8186  int b;
8187 
8188  idx = flexibleids[j];
8189 
8190  /* get and add binvars into var array */
8191  SCIP_CALL( SCIPgetBinvarsLinking(scip, consdata->linkingconss[idx], &binvars, &nbinvars) );
8192  assert(nbinvars != 0);
8193 
8194  vals = SCIPgetValsLinking(scip, consdata->linkingconss[idx]);
8195  assert(vals != NULL);
8196 
8197  lb = convertBoundToInt(scip, SCIPvarGetLbLocal(consdata->vars[idx]));
8198  ub = convertBoundToInt(scip, SCIPvarGetUbLocal(consdata->vars[idx]));
8199 
8200  /* compute start and finishing time */
8201  start = time - consdata->durations[idx] + 1;
8202  end = MIN(time, ub);
8203 
8204  /* add all neccessary binary variables */
8205  for( b = 0; b < nbinvars; ++b )
8206  {
8207  if( vals[b] < start || vals[b] < lb )
8208  continue;
8209 
8210  if( vals[b] > end )
8211  break;
8212 
8213  assert(binvars[b] != NULL);
8214  SCIP_CALL( SCIPaddVarToRow(scip, row, binvars[b], 1.0) );
8215  }
8216  }
8217 
8218  /* insert and release row */
8219  SCIP_CALL( SCIPflushRowExtensions(scip, row) );
8220  if( consdata->scoverrowssize == 0 )
8221  {
8222  consdata->scoverrowssize = 10;
8223  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->scoverrows, consdata->scoverrowssize) );
8224  }
8225  if( consdata->nscoverrows == consdata->scoverrowssize )
8226  {
8227  consdata->scoverrowssize *= 2;
8228  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->scoverrows, consdata->nscoverrows, consdata->scoverrowssize) );
8229  }
8230 
8231  consdata->scoverrows[consdata->nscoverrows] = row;
8232  consdata->nscoverrows++;
8233  }
8234 
8235  /* free buffer arrays */
8236  SCIPfreeBufferArray(scip, &flexibleids);
8237  SCIPfreeBufferArray(scip, &demands);
8238 
8239  return SCIP_OKAY;
8240 }
8241 
8242 /** method to construct cover cuts for all points in time */
8243 static
8245  SCIP* scip, /**< SCIP data structure */
8246  SCIP_CONS* cons /**< constraint to be separated */
8247  )
8248 {
8249  SCIP_CONSDATA* consdata;
8250 
8251  int* startvalues; /* stores when each job is starting */
8252  int* endvalues; /* stores when each job ends */
8253  int* startvaluessorted; /* stores when each job is starting */
8254  int* endvaluessorted; /* stores when each job ends */
8255  int* startindices; /* we sort the startvalues, so we need to know wich index of a job it corresponds to */
8256  int* endindices; /* we sort the endvalues, so we need to know wich index of a job it corresponds to */
8257 
8258  int nvars; /* number of jobs for this constraint */
8259  int freecapacity; /* remaining capacity */
8260  int curtime; /* point in time which we are just checking */
8261  int endidx; /* index of endsolvalues with: endsolvalues[endindex] > curtime */
8262 
8263  int hmin;
8264  int hmax;
8265 
8266  int j;
8267  int t;
8268 
8269  assert(scip != NULL);
8270  assert(cons != NULL);
8271 
8272  consdata = SCIPconsGetData(cons);
8273  assert(consdata != NULL);
8274 
8275  /* if no activities are associated with this resource then this constraint is redundant */
8276  if( consdata->vars == NULL )
8277  return SCIP_OKAY;
8278 
8279  nvars = consdata->nvars;
8280  hmin = consdata->hmin;
8281  hmax = consdata->hmax;
8282 
8283  SCIP_CALL( SCIPallocBufferArray(scip, &startvalues, nvars) );
8284  SCIP_CALL( SCIPallocBufferArray(scip, &endvalues, nvars) );
8285  SCIP_CALL( SCIPallocBufferArray(scip, &startvaluessorted, nvars) );
8286  SCIP_CALL( SCIPallocBufferArray(scip, &endvaluessorted, nvars) );
8287  SCIP_CALL( SCIPallocBufferArray(scip, &startindices, nvars) );
8288  SCIP_CALL( SCIPallocBufferArray(scip, &endindices, nvars) );
8289 
8290  /* assign start and endpoints to arrays */
8291  for ( j = 0; j < nvars; ++j )
8292  {
8293  startvalues[j] = convertBoundToInt(scip, SCIPvarGetLbLocal(consdata->vars[j]));
8294  startvaluessorted[j] = startvalues[j];
8295 
8296  endvalues[j] = convertBoundToInt(scip, SCIPvarGetUbLocal(consdata->vars[j])) + consdata->durations[j];
8297  endvaluessorted[j] = endvalues[j];
8298 
8299  startindices[j] = j;
8300  endindices[j] = j;
8301  }
8302 
8303  /* sort the arrays not-decreasing according to startsolvalues and endsolvalues
8304  * (and sort the indices in the same way) */
8305  SCIPsortIntInt(startvaluessorted, startindices, nvars);
8306  SCIPsortIntInt(endvaluessorted, endindices, nvars);
8307 
8308  endidx = 0;
8309  freecapacity = consdata->capacity;
8310 
8311  /* check each startpoint of a job whether the capacity is kept or not */
8312  for( j = 0; j < nvars; ++j )
8313  {
8314  curtime = startvaluessorted[j];
8315  if( curtime >= hmax )
8316  break;
8317 
8318  /* subtract all capacity needed up to this point */
8319  freecapacity -= consdata->demands[startindices[j]];
8320 
8321  while( j+1 < nvars && startvaluessorted[j+1] == curtime )
8322  {
8323  ++j;
8324  freecapacity -= consdata->demands[startindices[j]];
8325  }
8326 
8327  /* free all capacity usages of jobs the are no longer running */
8328  while( endidx < nvars && curtime >= endvaluessorted[endidx] )
8329  {
8330  freecapacity += consdata->demands[endindices[endidx]];
8331  ++endidx;
8332  }
8333 
8334  assert(freecapacity <= consdata->capacity);
8335  assert(endidx <= nvars);
8336 
8337  /* --> endindex - points to the next job which will finish
8338  * j - points to the last job that has been released
8339  */
8340 
8341 
8342  /* check freecapacity to be smaller than zero
8343  * then we will add cover constraints to the MIP
8344  */
8345  if( freecapacity < 0 && curtime >= hmin )
8346  {
8347  int nextprofilechange;
8348 
8349  /* we can create covering constraints for each pint in time in interval [curtime; nextprofilechange[ */
8350  if( j < nvars-1 )
8351  nextprofilechange = MIN( startvaluessorted[j+1], endvaluessorted[endidx] );
8352  else
8353  nextprofilechange = endvaluessorted[endidx];
8354 
8355  nextprofilechange = MIN(nextprofilechange, hmax);
8356 
8357  for( t = curtime; t < nextprofilechange; ++t )
8358  {
8359  SCIPdebugMessage("add cover constraint for time %d\n", curtime);
8360 
8361  /* create covering constraint */
8362  SCIP_CALL( createCoverCutsTimepoint(scip, cons, startvalues, t) );
8363 
8364  }
8365  } /* end if freecapacity > 0 */
8366 
8367  } /*lint --e{850}*/
8368 
8369  consdata->covercuts = TRUE;
8370 
8371  /* free all buffer arrays */
8372  SCIPfreeBufferArray(scip, &endindices);
8373  SCIPfreeBufferArray(scip, &startindices);
8374  SCIPfreeBufferArray(scip, &endvaluessorted);
8375  SCIPfreeBufferArray(scip, &startvaluessorted);
8376  SCIPfreeBufferArray(scip, &endvalues);
8377  SCIPfreeBufferArray(scip, &startvalues);
8378 
8379  return SCIP_OKAY;
8380 }
8381 
8382 /** this method creates a row for time point curtime which insures the capacity restriction of the cumulative
8383  * constraint
8384  */
8385 static
8387  SCIP* scip, /**< SCIP data structure */
8388  SCIP_CONS* cons, /**< constraint to be checked */
8389  int* startindices, /**< permutation with rspect to the start times */
8390  int curtime, /**< current point in time */
8391  int nstarted, /**< number of jobs that start before the curtime or at curtime */
8392  int nfinished, /**< number of jobs that finished before curtime or at curtime */
8393  SCIP_Bool cutsasconss /**< should the cumulative constraint create the cuts as constraints? */
8394  )
8395 {
8396  SCIP_CONSDATA* consdata;
8397  SCIP_VAR** binvars;
8398  int* coefs;
8399  int nbinvars;
8400  char name[SCIP_MAXSTRLEN];
8401  int capacity;
8402  int b;
8403 
8404  assert(nstarted > nfinished);
8405 
8406  consdata = SCIPconsGetData(cons);
8407  assert(consdata != NULL);
8408  assert(consdata->nvars > 0);
8409 
8410  capacity = consdata->capacity;
8411  assert(capacity > 0);
8412 
8413  nbinvars = 0;
8414  SCIP_CALL( collectBinaryVars(scip, consdata, &binvars, &coefs, &nbinvars, startindices, curtime, nstarted, nfinished) );
8415 
8416  /* construct row name */
8417  (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_%d[%d]", SCIPconsGetName(cons), nstarted-1, curtime);
8418 
8419  if( cutsasconss )
8420  {
8421  SCIP_CONS* lincons;
8422 
8423  /* create knapsack constraint for the given time point */
8424  SCIP_CALL( SCIPcreateConsKnapsack(scip, &lincons, name, 0, NULL, NULL, (SCIP_Longint)(capacity),
8425  TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE) );
8426 
8427  for( b = 0; b < nbinvars; ++b )
8428  {
8429  SCIP_CALL( SCIPaddCoefKnapsack(scip, lincons, binvars[b], (SCIP_Longint)coefs[b]) );
8430  }
8431 
8432  /* add and release the new constraint */
8433  SCIP_CALL( SCIPaddCons(scip, lincons) );
8434  SCIP_CALL( SCIPreleaseCons(scip, &lincons) );
8435  }
8436  else
8437  {
8438  SCIP_ROW* row;
8439 
8440  SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, SCIPconsGetHdlr(cons), name, -SCIPinfinity(scip), (SCIP_Real)capacity, FALSE, FALSE, SCIPconsIsRemovable(cons)) );
8441  SCIP_CALL( SCIPcacheRowExtensions(scip, row) );
8442 
8443  for( b = 0; b < nbinvars; ++b )
8444  {
8445  SCIP_CALL( SCIPaddVarToRow(scip, row, binvars[b], (SCIP_Real)coefs[b]) );
8446  }
8447 
8448  SCIP_CALL( SCIPflushRowExtensions(scip, row) );
8449  SCIPdebug( SCIP_CALL(SCIPprintRow(scip, row, NULL)) );
8450 
8451  if( consdata->demandrowssize == 0 )
8452  {
8453  consdata->demandrowssize = 10;
8454  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->demandrows, consdata->demandrowssize) );
8455  }
8456  if( consdata->ndemandrows == consdata->demandrowssize )
8457  {
8458  consdata->demandrowssize *= 2;
8459  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->demandrows, consdata->ndemandrows, consdata->demandrowssize) );
8460  }
8461 
8462  consdata->demandrows[consdata->ndemandrows] = row;
8463  consdata->ndemandrows++;
8464  }
8465 
8466  SCIPfreeBufferArrayNull(scip, &binvars);
8467  SCIPfreeBufferArrayNull(scip, &coefs);
8468 
8469  return SCIP_OKAY;
8470 }
8471 
8472 /** this method checks how many cumulatives can run at most at one time if this is greater than the capacity it creates
8473  * row
8474  */
8475 static
8477  SCIP* scip, /**< SCIP data structure */
8478  SCIP_CONS* cons, /**< constraint to be checked */
8479  SCIP_Bool cutsasconss /**< should the cumulative constraint create the cuts as constraints? */
8480  )
8481 {
8482  SCIP_CONSDATA* consdata;
8483 
8484  int* starttimes; /* stores when each job is starting */
8485  int* endtimes; /* stores when each job ends */
8486  int* startindices; /* we will sort the startsolvalues, thus we need to know wich index of a job it corresponds to */
8487  int* endindices; /* we will sort the endsolvalues, thus we need to know wich index of a job it corresponds to */
8488 
8489  int nvars; /* number of activities for this constraint */
8490  int freecapacity; /* remaining capacity */
8491  int curtime; /* point in time which we are just checking */
8492  int endindex; /* index of endsolvalues with: endsolvalues[endindex] > curtime */
8493 
8494  int hmin;
8495  int hmax;
8496 
8497  int j;
8498 
8499  assert(scip != NULL);
8500  assert(cons != NULL);
8501 
8502  consdata = SCIPconsGetData(cons);
8503  assert(consdata != NULL);
8504 
8505  nvars = consdata->nvars;
8506 
8507  /* if no activities are associated with this cumulative then this constraint is redundant */
8508  if( nvars == 0 )
8509  return SCIP_OKAY;
8510 
8511  assert(consdata->vars != NULL);
8512 
8513  SCIP_CALL( SCIPallocBufferArray(scip, &starttimes, nvars) );
8514  SCIP_CALL( SCIPallocBufferArray(scip, &endtimes, nvars) );
8515  SCIP_CALL( SCIPallocBufferArray(scip, &startindices, nvars) );
8516  SCIP_CALL( SCIPallocBufferArray(scip, &endindices, nvars) );
8517 
8518  SCIPdebugMessage("create sorted event points for cumulative constraint <%s> with %d jobs\n",
8519  SCIPconsGetName(cons), nvars);
8520 
8521  /* create event point arrays */
8522  createSortedEventpoints(scip, nvars, consdata->vars, consdata->durations,
8523  starttimes, endtimes, startindices, endindices, FALSE);
8524 
8525  endindex = 0;
8526  freecapacity = consdata->capacity;
8527  hmin = consdata->hmin;
8528  hmax = consdata->hmax;
8529 
8530  /* check each startpoint of a job whether the capacity is kept or not */
8531  for( j = 0; j < nvars; ++j )
8532  {
8533  curtime = starttimes[j];
8534  SCIPdebugMessage("look at %d-th job with start %d\n", j, curtime);
8535 
8536  if( curtime >= hmax )
8537  break;
8538 
8539  /* remove the capacity requirments for all job which start at the curtime */
8540  subtractStartingJobDemands(consdata, curtime, starttimes, startindices, &freecapacity, &j, nvars);
8541 
8542  /* add the capacity requirments for all job which end at the curtime */
8543  addEndingJobDemands(consdata, curtime, endtimes, endindices, &freecapacity, &endindex, nvars);
8544 
8545  assert(freecapacity <= consdata->capacity);
8546  assert(endindex <= nvars);
8547 
8548  /* endindex - points to the next job which will finish */
8549  /* j - points to the last job that has been released */
8550 
8551  /* if free capacity is smaller than zero, then add rows to the LP */
8552  if( freecapacity < 0 && curtime >= hmin )
8553  {
8554  int nextstarttime;
8555  int t;
8556 
8557  /* step forward until next job is released and see whether capacity constraint is met or not */
8558  if( j < nvars-1 )
8559  nextstarttime = starttimes[j+1];
8560  else
8561  nextstarttime = endtimes[nvars-1];
8562 
8563  nextstarttime = MIN(nextstarttime, hmax);
8564 
8565  /* create capacity restriction row for current event point */
8566  SCIP_CALL( createCapacityRestriction(scip, cons, startindices, curtime, j+1, endindex, cutsasconss) );
8567 
8568  /* create for all points in time between the current event point and next start event point a row if the free
8569  * capacity is still smaller than zero */
8570  for( t = curtime+1 ; t < nextstarttime; ++t )
8571  {
8572  /* add the capacity requirments for all job which end at the curtime */
8573  addEndingJobDemands(consdata, t, endtimes, endindices, &freecapacity, &endindex, nvars);
8574 
8575  if( freecapacity < 0 )
8576  {
8577  /* add constraint */
8578  SCIPdebugMessage("add capacity constraint at time %d\n", t);
8579 
8580  /* create capacity restriction row */
8581  SCIP_CALL( createCapacityRestriction(scip, cons, startindices, t, j+1, endindex, cutsasconss) );
8582  }
8583  else
8584  break;
8585  }
8586  }
8587  } /*lint --e{850}*/
8588 
8589  /* free all buffer arrays */
8590  SCIPfreeBufferArray(scip, &endindices);
8591  SCIPfreeBufferArray(scip, &startindices);
8592  SCIPfreeBufferArray(scip, &endtimes);
8593  SCIPfreeBufferArray(scip, &starttimes);
8594 
8595  return SCIP_OKAY;
8596 }
8597 
8598 /** creates LP rows corresponding to cumulative constraint; therefore, check each point in time if the maximal needed
8599  * capacity is larger than the capacity of the cumulative constraint
8600  * - for each necessary point in time:
8601  *
8602  * sum_j sum_t demand_j * x_{j,t} <= capacity
8603  *
8604  * where x(j,t) is the binary variables of job j at time t
8605  */
8606 static
8608  SCIP* scip, /**< SCIP data structure */
8609  SCIP_CONS* cons, /**< cumulative constraint */
8610  SCIP_Bool cutsasconss /**< should the cumulative constraint create the cuts as constraints? */
8611  )
8612 {
8613  SCIP_CONSDATA* consdata;
8614 
8615  consdata = SCIPconsGetData(cons);
8616  assert(consdata != NULL);
8617  assert(consdata->demandrows == NULL);
8618  assert(consdata->ndemandrows == 0);
8619 
8620  /* collect the linking constraints */
8621  if( consdata->linkingconss == NULL )
8622  {
8623  SCIP_CALL( consdataCollectLinkingCons(scip, consdata) );
8624  }
8625 
8626  SCIP_CALL( consCapacityConstraintsFinder(scip, cons, cutsasconss) );
8627 
8628  /* switch of separation for the cumulative constraint if linear constraints are add as cuts */
8629  if( cutsasconss )
8630  {
8631  if( SCIPconsIsInitial(cons) )
8632  {
8633  SCIP_CALL( SCIPsetConsInitial(scip, cons, FALSE) );
8634  }
8635  if( SCIPconsIsSeparated(cons) )
8636  {
8637  SCIP_CALL( SCIPsetConsSeparated(scip, cons, FALSE) );
8638  }
8639  if( SCIPconsIsEnforced(cons) )
8640  {
8641  SCIP_CALL( SCIPsetConsEnforced(scip, cons, FALSE) );
8642  }
8643  }
8644 
8645  return SCIP_OKAY;
8646 }
8647 
8648 /** adds linear relaxation of cumulative constraint to the LP */
8649 static
8651  SCIP* scip, /**< SCIP data structure */
8652  SCIP_CONS* cons, /**< cumulative constraint */
8653  SCIP_Bool cutsasconss /**< should the cumulative constraint create the cuts as constraints? */
8654  )
8655 {
8656  SCIP_CONSDATA* consdata;
8657  int r;
8658 
8659  consdata = SCIPconsGetData(cons);
8660  assert(consdata != NULL);
8661 
8662  if( consdata->demandrows == NULL )
8663  {
8664  SCIP_CALL( createRelaxation(scip, cons, cutsasconss) );
8665  }
8666 
8667  for( r = 0; r < consdata->ndemandrows; ++r )
8668  {
8669  if( !SCIProwIsInLP(consdata->demandrows[r]) )
8670  {
8671  SCIP_Bool infeasible;
8672 
8673  assert(consdata->demandrows[r] != NULL);
8674  SCIP_CALL( SCIPaddCut(scip, NULL, consdata->demandrows[r], FALSE, &infeasible) );
8675  assert( ! infeasible ); /* this function is only called by initlp -> the cut should be feasible */
8676  }
8677  }
8678 
8679  return SCIP_OKAY;
8680 }
8681 
8682 /** checks constraint for violation, and adds it as a cut if possible */
8683 static
8685  SCIP* scip, /**< SCIP data structure */
8686  SCIP_CONS* cons, /**< cumulative constraint to be separated */
8687  SCIP_SOL* sol, /**< primal CIP solution, NULL for current LP solution */
8688  SCIP_Bool* separated, /**< pointer to store TRUE, if a cut was found */
8689  SCIP_Bool* cutoff /**< whether a cutoff has been detected */
8690  )
8691 { /*lint --e{715}*/
8692  SCIP_CONSDATA* consdata;
8693  int ncuts;
8694  int r;
8695 
8696  assert(scip != NULL);
8697  assert(cons != NULL);
8698  assert(separated != NULL);
8699  assert(cutoff != NULL);
8700 
8701  *separated = FALSE;
8702  *cutoff = FALSE;
8703 
8704  consdata = SCIPconsGetData(cons);
8705  assert(consdata != NULL);
8706 
8707  SCIPdebugMessage("separate cumulative constraint <%s>\n", SCIPconsGetName(cons));
8708 
8709  if( consdata->demandrows == NULL )
8710  {
8711  SCIP_CALL( createRelaxation(scip, cons, FALSE) );
8712  }
8713 
8714  ncuts = 0;
8715 
8716  /* check each row that is not contained in LP */
8717  for( r = 0; r < consdata->ndemandrows; ++r )
8718  {
8719  if( !SCIProwIsInLP(consdata->demandrows[r]) )
8720  {
8721  SCIP_Real feasibility;
8722 
8723  if( sol != NULL )
8724  feasibility = SCIPgetRowSolFeasibility(scip, consdata->demandrows[r], sol);
8725  else
8726  feasibility = SCIPgetRowLPFeasibility(scip, consdata->demandrows[r]);
8727 
8728  if( SCIPisFeasNegative(scip, feasibility) )
8729  {
8730  SCIP_CALL( SCIPaddCut(scip, sol, consdata->demandrows[r], FALSE, cutoff) );
8731  if ( *cutoff )
8732  {
8733  SCIP_CALL( SCIPresetConsAge(scip, cons) );
8734  return SCIP_OKAY;
8735  }
8736  *separated = TRUE;
8737  ncuts++;
8738  }
8739  }
8740  }
8741 
8742  if( ncuts > 0 )
8743  {
8744  SCIPdebugMessage("cumulative constraint <%s> separated %d cuts\n", SCIPconsGetName(cons), ncuts);
8745 
8746  /* if successful, reset age of constraint */
8747  SCIP_CALL( SCIPresetConsAge(scip, cons) );
8748  (*separated) = TRUE;
8749  }
8750 
8751  return SCIP_OKAY;
8752 }
8753 
8754 /** checks constraint for violation, and adds it as a cut if possible */
8755 static
8757  SCIP* scip, /**< SCIP data structure */
8758  SCIP_CONS* cons, /**< logic or constraint to be separated */
8759  SCIP_SOL* sol, /**< primal CIP solution, NULL for current LP solution */
8760  SCIP_Bool* separated, /**< pointer to store TRUE, if a cut was found */
8761  SCIP_Bool* cutoff /**< whether a cutoff has been detected */
8762  )
8763 {
8764  SCIP_CONSDATA* consdata;
8765  SCIP_ROW* row;
8766  SCIP_Real minfeasibility;
8767  int r;
8768 
8769  assert(scip != NULL);
8770  assert(cons != NULL);
8771  assert(separated != NULL);
8772  assert(cutoff != NULL);
8773 
8774  *separated = FALSE;
8775  *cutoff = FALSE;
8776 
8777  consdata = SCIPconsGetData(cons);
8778  assert(consdata != NULL);
8779 
8780  SCIPdebugMessage("separate cumulative constraint <%s>\n", SCIPconsGetName(cons));
8781 
8782  /* collect the linking constraints */
8783  if( consdata->linkingconss == NULL )
8784  {
8785  SCIP_CALL( consdataCollectLinkingCons(scip, consdata) );
8786  }
8787 
8788  if( !consdata->covercuts )
8789  {
8790  SCIP_CALL( createCoverCuts(scip, cons) );
8791  }
8792 
8793  row = NULL;
8794  minfeasibility = SCIPinfinity(scip);
8795 
8796  /* check each row of small covers that is not contained in LP */
8797  for( r = 0; r < consdata->nscoverrows; ++r )
8798  {
8799  if( !SCIProwIsInLP(consdata->scoverrows[r]) )
8800  {
8801  SCIP_Real feasibility;
8802 
8803  assert(consdata->scoverrows[r] != NULL);
8804  if( sol != NULL )
8805  feasibility = SCIPgetRowSolFeasibility(scip, consdata->scoverrows[r], sol);
8806  else
8807  feasibility = SCIPgetRowLPFeasibility(scip, consdata->scoverrows[r]);
8808 
8809  if( minfeasibility > feasibility )
8810  {
8811  minfeasibility = feasibility;
8812  row = consdata->scoverrows[r];
8813  }
8814  }
8815  }
8816 
8817  if( SCIPisFeasNegative(scip, minfeasibility) )
8818  {
8819  SCIPdebugMessage("cumulative constraint <%s> separated 1 cover cut with feasibility %g\n",
8820  SCIPconsGetName(cons), minfeasibility);
8821 
8822  assert(row != NULL);
8823  SCIP_CALL( SCIPaddCut(scip, sol, row, FALSE, cutoff) );
8824  SCIP_CALL( SCIPresetConsAge(scip, cons) );
8825  if ( *cutoff )
8826  return SCIP_OKAY;
8827  (*separated) = TRUE;
8828  }
8829 
8830  minfeasibility = SCIPinfinity(scip);
8831  row = NULL;
8832 
8833  /* check each row of small covers that is not contained in LP */
8834  for( r = 0; r < consdata->nbcoverrows; ++r )
8835  {
8836  if( !SCIProwIsInLP(consdata->bcoverrows[r]) )
8837  {
8838  SCIP_Real feasibility;
8839 
8840  assert(consdata->bcoverrows[r] != NULL);
8841  if( sol != NULL )
8842  feasibility = SCIPgetRowSolFeasibility(scip, consdata->bcoverrows[r], sol);
8843  else
8844  feasibility = SCIPgetRowLPFeasibility(scip, consdata->bcoverrows[r]);
8845 
8846  if( minfeasibility > feasibility )
8847  {
8848  minfeasibility = feasibility;
8849  row = consdata->bcoverrows[r];
8850  }
8851  }
8852  }
8853 
8854  if( SCIPisFeasNegative(scip, minfeasibility) )
8855  {
8856  SCIPdebugMessage("cumulative constraint <%s> separated 1 cover cut with feasibility %g\n",
8857  SCIPconsGetName(cons), minfeasibility);
8858 
8859  assert(row != NULL);
8860  SCIP_CALL( SCIPaddCut(scip, sol, row, FALSE, cutoff) );
8861  SCIP_CALL( SCIPresetConsAge(scip, cons) );
8862  if ( *cutoff )
8863  return SCIP_OKAY;
8864  (*separated) = TRUE;
8865  }
8866 
8867  return SCIP_OKAY;
8868 }
8869 
8870 /** this method creates a row for time point @p curtime which ensures the capacity restriction of the cumulative constraint */
8871 static
8873  SCIP* scip, /**< SCIP data structure */
8874  SCIP_CONS* cons, /**< constraint to be checked */
8875  SCIP_SOL* sol, /**< primal CIP solution, NULL for current LP solution */
8876  int* startindices, /**< permutation with rspect to the start times */
8877  int curtime, /**< current point in time */
8878  int nstarted, /**< number of jobs that start before the curtime or at curtime */
8879  int nfinished, /**< number of jobs that finished before curtime or at curtime */
8880  SCIP_Bool lower /**< shall cuts be created due to lower or upper bounds? */
8881  )
8882 {
8883  SCIP_CONSDATA* consdata;
8884  char name[SCIP_MAXSTRLEN];
8885  SCIP_Bool infeasible;
8886  int lhs; /* left hand side of constraint */
8887 
8888  SCIP_VAR** activevars;
8889  SCIP_ROW* row;
8890 
8891  int v;
8892 
8893  assert(nstarted > nfinished);
8894 
8895  consdata = SCIPconsGetData(cons);
8896  assert(consdata != NULL);
8897  assert(consdata->nvars > 0);
8898 
8899 
8900  SCIP_CALL( SCIPallocBufferArray(scip, &activevars, nstarted-nfinished) );
8901 
8902  SCIP_CALL( collectIntVars(scip, consdata, &activevars, startindices, curtime, nstarted, nfinished, lower, &lhs ) );
8903 
8904  if( lower )
8905  {
8906  (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "lower(%d)", curtime);
8907 
8908  SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, SCIPconsGetHdlr(cons), name, (SCIP_Real) lhs, SCIPinfinity(scip),
8909  TRUE, FALSE, SCIPconsIsRemovable(cons)) );
8910  }
8911  else
8912  {
8913  (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "upper(%d)", curtime);
8914  SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, SCIPconsGetHdlr(cons), name, -SCIPinfinity(scip), (SCIP_Real) lhs,
8915  TRUE, FALSE, SCIPconsIsRemovable(cons)) );
8916  }
8917 
8918  SCIP_CALL( SCIPcacheRowExtensions(scip, row) );
8919 
8920  for( v = 0; v < nstarted - nfinished; ++v )
8921  {
8922  SCIP_CALL( SCIPaddVarToRow(scip, row, activevars[v], 1.0) );
8923  }
8924 
8925  SCIP_CALL( SCIPflushRowExtensions(scip, row) );
8926  SCIPdebug( SCIP_CALL(SCIPprintRow(scip, row, NULL)) );
8927 
8928  SCIP_CALL( SCIPaddCut(scip, sol, row, TRUE, &infeasible) );
8929  assert( ! infeasible );
8930 
8931  SCIP_CALL( SCIPreleaseRow(scip, &row) );
8932 
8933  /* free buffers */
8934  SCIPfreeBufferArrayNull(scip, &activevars);
8935 
8936  return SCIP_OKAY;
8937 }
8938 
8939 /** checks constraint for violation, and adds it as a cut if possible */
8940 static
8942  SCIP* scip, /**< SCIP data structure */
8943  SCIP_CONS* cons, /**< cumulative constraint to be separated */
8944  SCIP_SOL* sol, /**< primal CIP solution, NULL for current LP solution */
8945  SCIP_Bool lower, /**< shall cuts be created according to lower bounds? */
8946  SCIP_Bool* separated /**< pointer to store TRUE, if a cut was found */
8947  )
8948 {
8949 
8950  SCIP_CONSDATA* consdata;
8951 
8952  int* starttimes; /* stores when each job is starting */
8953  int* endtimes; /* stores when each job ends */
8954  int* startindices; /* we will sort the startsolvalues, thus we need to know wich index of a job it corresponds to */
8955  int* endindices; /* we will sort the endsolvalues, thus we need to know wich index of a job it corresponds to */
8956 
8957  int nvars; /* number of activities for this constraint */
8958  int freecapacity; /* remaining capacity */
8959  int curtime; /* point in time which we are just checking */
8960  int endindex; /* index of endsolvalues with: endsolvalues[endindex] > curtime */
8961 
8962  int hmin;
8963  int hmax;
8964  int j;
8965 
8966  assert(scip != NULL);
8967  assert(cons != NULL);
8968 
8969  consdata = SCIPconsGetData(cons);
8970  assert(consdata != NULL);
8971 
8972  nvars = consdata->nvars;
8973 
8974  /* if no activities are associated with this cumulative then this constraint is redundant */
8975  if( nvars <= 1 )
8976  return SCIP_OKAY;
8977 
8978  assert(consdata->vars != NULL);
8979 
8980  SCIP_CALL( SCIPallocBufferArray(scip, &starttimes, nvars) );
8981  SCIP_CALL( SCIPallocBufferArray(scip, &endtimes, nvars) );
8982  SCIP_CALL( SCIPallocBufferArray(scip, &startindices, nvars) );
8983  SCIP_CALL( SCIPallocBufferArray(scip, &endindices, nvars) );
8984 
8985  SCIPdebugMessage("create sorted event points for cumulative constraint <%s> with %d jobs\n",
8986  SCIPconsGetName(cons), nvars);
8987 
8988  /* create event point arrays */
8989  createSelectedSortedEventpointsSol(scip, consdata, sol, starttimes, endtimes, startindices, endindices, &nvars, lower);
8990 
8991  /* now nvars might be smaller than before! */
8992 
8993  endindex = 0;
8994  freecapacity = consdata->capacity;
8995  hmin = consdata->hmin;
8996  hmax = consdata->hmax;
8997 
8998  /* check each startpoint of a job whether the capacity is kept or not */
8999  for( j = 0; j < nvars; ++j )
9000  {
9001  curtime = starttimes[j];
9002 
9003  if( curtime >= hmax )
9004  break;
9005 
9006  /* remove the capacity requirements for all job which start at the curtime */
9007  subtractStartingJobDemands(consdata, curtime, starttimes, startindices, &freecapacity, &j, nvars);
9008 
9009  /* add the capacity requirments for all job which end at the curtime */
9010  addEndingJobDemands(consdata, curtime, endtimes, endindices, &freecapacity, &endindex, nvars);
9011 
9012  assert(freecapacity <= consdata->capacity);
9013  assert(endindex <= nvars);
9014 
9015  /* endindex - points to the next job which will finish */
9016  /* j - points to the last job that has been released */
9017 
9018  /* if free capacity is smaller than zero, then add rows to the LP */
9019  if( freecapacity < 0 && curtime >= hmin)
9020  {
9021  /* create capacity restriction row for current event point */
9022  SCIP_CALL( createCapacityRestrictionIntvars(scip, cons, sol, startindices, curtime, j+1, endindex, lower) );
9023  *separated = TRUE;
9024  }
9025  } /*lint --e{850}*/
9026 
9027  /* free all buffer arrays */
9028  SCIPfreeBufferArray(scip, &endindices);
9029  SCIPfreeBufferArray(scip, &startindices);
9030  SCIPfreeBufferArray(scip, &endtimes);
9031  SCIPfreeBufferArray(scip, &starttimes);
9032 
9033  return SCIP_OKAY;
9034 }
9035 
9036 /**@} */
9037 
9038 
9039 /**@name Presolving
9040  *
9041  * @{
9042  */
9043 
9044 #ifndef NDEBUG
9045 /** returns TRUE if all demands are smaller than the capacity of the cumulative constraint and if the total demand is
9046  * correct
9047  */
9048 static
9050  SCIP* scip, /**< SCIP data structure */
9051  SCIP_CONS* cons /**< constraint to be checked */
9052  )
9053 {
9054  SCIP_CONSDATA* consdata;
9055  int capacity;
9056  int nvars;
9057  int j;
9058 
9059  assert(scip != NULL);
9060  assert(cons != NULL);
9061 
9062  consdata = SCIPconsGetData(cons);
9063  assert(consdata != NULL);
9064 
9065  nvars = consdata->nvars;
9066 
9067  /* if no activities are associated with this cumulative then this constraint is not infeasible, return */
9068  if( nvars <= 1 )
9069  return TRUE;
9070 
9071  assert(consdata->vars != NULL);
9072  capacity = consdata->capacity;
9073 
9074  /* check each activity: if demand is larger than capacity the problem is infeasible */
9075  for ( j = 0; j < nvars; ++j )
9076  {
9077  if( consdata->demands[j] > capacity )
9078  return FALSE;
9079  }
9080 
9081  return TRUE;
9082 }
9083 #endif
9084 
9085 /** delete constraint if it consists of at most one job
9086  *
9087  * @todo this method needs to be adjusted w.r.t. effective horizon
9088  */
9089 static
9091  SCIP* scip, /**< SCIP data structure */
9092  SCIP_CONS* cons, /**< constraint to propagate */
9093  int* ndelconss, /**< pointer to store the number of deleted constraints */
9094  SCIP_Bool* cutoff /**< pointer to store if the constraint is infeasible */
9095  )
9096 {
9097  SCIP_CONSDATA* consdata;
9098 
9099  assert(scip != NULL);
9100  assert(cons != NULL);
9101 
9102  consdata = SCIPconsGetData(cons);
9103  assert(consdata != NULL);
9104 
9105  if( consdata->nvars == 0 )
9106  {
9107  SCIPdebugMessage("delete cumulative constraints <%s>\n", SCIPconsGetName(cons));
9108 
9109  SCIP_CALL( SCIPdelCons(scip, cons) );
9110  (*ndelconss)++;
9111  }
9112  else if( consdata->nvars == 1 )
9113  {
9114  if( consdata->demands[0] > consdata->capacity )
9115  (*cutoff) = TRUE;
9116  else
9117  {
9118  SCIPdebugMessage("delete cumulative constraints <%s>\n", SCIPconsGetName(cons));
9119 
9120  SCIP_CALL( SCIPdelCons(scip, cons) );
9121  (*ndelconss)++;
9122  }
9123  }
9124 
9125  return SCIP_OKAY;
9126 }
9127 
9128 /** remove jobs which have a duration or demand of zero (zero energy) or lay outside the efficient horizon [hmin, hmax);
9129  * this is done in the SCIP_DECL_CONSINITPRE() callback
9130  */
9131 static
9133  SCIP* scip, /**< SCIP data structure */
9134  SCIP_CONS* cons /**< constraint to propagate */
9135  )
9136 {
9137  SCIP_CONSDATA* consdata;
9138  SCIP_VAR* var;
9139  int demand;
9140  int duration;
9141  int hmin;
9142  int hmax;
9143  int est;
9144  int lct;
9145  int j;
9146 
9147  assert(scip != NULL);
9148  assert(cons != NULL);
9149 
9150  consdata = SCIPconsGetData(cons);
9151  assert(consdata != NULL);
9152 
9153  hmin = consdata->hmin;
9154  hmax = consdata->hmax;
9155 
9156  SCIPdebugMessage("check for irrelevant jobs within cumulative constraint <%s>[%d,%d)\n",
9157  SCIPconsGetName(cons), hmin, hmax);
9158 
9159  for( j = consdata->nvars-1; j >= 0; --j )
9160  {
9161  var = consdata->vars[j];
9162  demand = consdata->demands[j];
9163  duration = consdata->durations[j];
9164 
9165  /* earliest completion time (ect) and latest start time (lst) */
9166  est = convertBoundToInt(scip, SCIPvarGetLbGlobal(var));
9167  lct = convertBoundToInt(scip, SCIPvarGetUbGlobal(var)) + duration;
9168 
9169  if( demand == 0 || duration == 0 )
9170  {
9171  /* jobs with zero demand or zero duration can be removed */
9172  SCIPdebugMessage(" remove variable <%s> due to zero %s\n",
9173  SCIPvarGetName(var), demand == 0 ? "demand" : "duration");
9174 
9175  /* remove variable form constraint */
9176  SCIP_CALL( consdataDeletePos(scip, consdata, cons, j) );
9177  }
9178  else if( est >= hmax || lct <= hmin )
9179  {
9180  SCIPdebugMessage(" remove variable <%s>[%d,%d] with duration <%d>\n",
9181  SCIPvarGetName(var), est, lct - duration, duration);
9182 
9183  /* delete variable at the given position */
9184  SCIP_CALL( consdataDeletePos(scip, consdata, cons, j) );
9185 
9186  /* for the statistic we count the number of jobs which are irrelevant */
9187  SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->nirrelevantjobs++ );
9188  }
9189  }
9190 
9191  return SCIP_OKAY;
9192 }
9193 
9194 /** adjust bounds of over sizeed job (the demand is larger than the capacity) */
9195 static
9197  SCIP* scip, /**< SCIP data structure */
9198  SCIP_CONSDATA* consdata, /**< constraint data */
9199  int pos, /**< position of job in the consdata */
9200  int* nchgbds, /**< pointer to store the number of changed bounds */
9201  int* naddconss, /**< pointer to store the number of added constraints */
9202  SCIP_Bool* cutoff /**< pointer to store if a cutoff was detected */
9203  )
9204 {
9205  SCIP_VAR* var;
9206  SCIP_Bool tightened;
9207  int duration;
9208  int ect;
9209  int lst;
9210 
9211  assert(scip != NULL);
9212 
9213  /* zero energy jobs should be removed already */
9214  assert(consdata->durations[pos] > 0);
9215  assert(consdata->demands[pos] > 0);
9216 
9217  var = consdata->vars[pos];
9218  assert(var != NULL);
9219  duration = consdata->durations[pos];
9220 
9221  /* jobs with a demand greater than the the capacity have to moved outside the time interval [hmin,hmax) */
9222  SCIPdebugMessage(" variable <%s>: demand <%d> is larger than the capacity <%d>\n",
9223  SCIPvarGetName(var), consdata->demands[pos], consdata->capacity);
9224 
9225  /* earliest completion time (ect) and latest start time (lst) */
9226  ect = convertBoundToInt(scip, SCIPvarGetLbGlobal(var)) + duration;
9227  lst = convertBoundToInt(scip, SCIPvarGetUbGlobal(var));
9228 
9229  /* the jobs has to have an overlap with the efficient horizon otherwise it would be already removed */
9230  if( ect - duration >= consdata->hmax || lst + duration <= consdata->hmin)
9231  return SCIP_OKAY;
9232 
9233  if( ect > consdata->hmin && lst < consdata->hmax )
9234  {
9235  /* the job will at least run partly in the time interval [hmin,hmax) this means the problem is infeasible */
9236  *cutoff = TRUE;
9237  }
9238  else if( lst < consdata->hmax )
9239  {
9240  /* move the latest start time of this job in such a way that it finishes before or at hmin */
9241  SCIP_CALL( SCIPtightenVarUb(scip, var, (SCIP_Real)(consdata->hmin - duration), TRUE, cutoff, &tightened) );
9242  assert(tightened);
9243  assert(!(*cutoff));
9244  (*nchgbds)++;
9245  }
9246  else if( ect > consdata->hmin )
9247  {
9248  /* move the earliest start time of this job in such a way that it starts after or at hmax */
9249  SCIP_CALL( SCIPtightenVarLb(scip, var, (SCIP_Real)(consdata->hmax), TRUE, cutoff, &tightened) );
9250  assert(tightened);
9251  assert(!(*cutoff));
9252  (*nchgbds)++;
9253  }
9254  else
9255  {
9256  /* this job can run before or after the time interval [hmin,hmax) thus we create a bound disjunction
9257  * constraint to ensure that it does not overlap with the time interval [hmin,hmax); that is:
9258  *
9259  * (var <= hmin - duration) /\ (var >= hmax)
9260  */
9261  SCIP_CONS* cons;
9262 
9263  SCIP_VAR* vartuple[2];
9264  SCIP_BOUNDTYPE boundtypetuple[2];
9265  SCIP_Real boundtuple[2];
9266 
9267  char name[SCIP_MAXSTRLEN];
9268  int leftbound;
9269  int rightbound;
9270 
9271  leftbound = consdata->hmin - duration;
9272  rightbound = consdata->hmax;
9273 
9274  /* allocate temporary memory for arrays */
9275  vartuple[0] = var;
9276  vartuple[1] = var;
9277  boundtuple[0] = (SCIP_Real)leftbound;
9278  boundtuple[1] = (SCIP_Real)rightbound;
9279  boundtypetuple[0] = SCIP_BOUNDTYPE_UPPER;
9280  boundtypetuple[1] = SCIP_BOUNDTYPE_LOWER;
9281 
9282  (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s<=%d or %s >= %d",
9283  SCIPvarGetName(var), leftbound, SCIPvarGetName(var), rightbound);
9284 
9285  /* create and add bounddisjunction constraint */
9286  SCIP_CALL( SCIPcreateConsBounddisjunction(scip, &cons, name, 2, vartuple, boundtypetuple, boundtuple,
9287  TRUE, FALSE, TRUE, TRUE /*check*/, TRUE/*prop*/, FALSE, FALSE, FALSE, FALSE, FALSE) );
9288 
9289  SCIPdebugPrintCons(scip, cons, NULL);
9290 
9291  /* add and release the new constraint */
9292  SCIP_CALL( SCIPaddCons(scip, cons) );
9293  SCIP_CALL( SCIPreleaseCons(scip, &cons) );
9294  (*naddconss)++;
9295  }
9296 
9297  return SCIP_OKAY;
9298 }
9299 
9300 /** try to removed over sizeed jobs (the demand is larger than the capacity) */
9301 static
9303  SCIP* scip, /**< SCIP data structure */
9304  SCIP_CONS* cons, /**< constraint */
9305  int* nchgbds, /**< pointer to store the number of changed bounds */
9306  int* nchgcoefs, /**< pointer to store the number of changed coefficient */
9307  int* naddconss, /**< pointer to store the number of added constraints */
9308  SCIP_Bool* cutoff /**< pointer to store if a cutoff was detected */
9309  )
9310 {
9311  SCIP_CONSDATA* consdata;
9312  int capacity;
9313  int j;
9314 
9315  consdata = SCIPconsGetData(cons);
9316  assert(consdata != NULL);
9317 
9318  /* if a cutoff was already detected just return */
9319  if( *cutoff )
9320  return SCIP_OKAY;
9321 
9322  capacity = consdata->capacity;
9323 
9324  for( j = consdata->nvars-1; j >= 0 && !(*cutoff); --j )
9325  {
9326  if( consdata->demands[j] > capacity )
9327  {
9328  SCIP_CALL( adjustOversizedJobBounds(scip, consdata, j, nchgbds, naddconss, cutoff) );
9329 
9330  /* remove variable form constraint */
9331  SCIP_CALL( consdataDeletePos(scip, consdata, cons, j) );
9332  (*nchgcoefs)++;
9333  }
9334  }
9335 
9336  SCIPdebugMessage("cumulative constraint <%s> has %d jobs left, cutoff %u\n", SCIPconsGetName(cons), consdata->nvars, *cutoff);
9337 
9338  return SCIP_OKAY;
9339 }
9340 
9341 /** fix integer variable to upper bound if the rounding locks and the object coefficient are in favor of that */
9342 static
9344  SCIP* scip, /**< SCIP data structure */
9345  SCIP_VAR* var, /**< integer variable to fix */
9346  SCIP_Bool uplock, /**< has thet start time variable a up lock */
9347  int* nfixedvars /**< pointer to store the number fixed variables */
9348  )
9349 {
9350  SCIP_Bool infeasible;
9351  SCIP_Bool tightened;
9352  SCIP_Bool roundable;
9353 
9354  /* if SCIP is in probing mode or repropagation we cannot perform this dual reductions since this dual reduction
9355  * would/could end in an implication which can lead to cutoff of the/all optimal solution
9356  */
9357  if( SCIPinProbing(scip) || SCIPinRepropagation(scip) )
9358  return SCIP_OKAY;
9359 
9360  /* rounding the variable to the upper bound is only a feasible dual reduction if the cumulative constraint
9361  * handler is the only one locking that variable up
9362  */
9363  assert(uplock == TRUE || uplock == FALSE);
9364  assert((int)TRUE == 1);
9365  assert((int)FALSE == 0);
9366 
9367  if( SCIPvarGetNLocksUp(var) > (int)(uplock) )
9368  return SCIP_OKAY;
9369 
9370  SCIP_CALL( varMayRoundUp(scip, var, &roundable) );
9371 
9372  /* rounding the integer variable up is only a valid dual reduction if the object coefficient is zero or negative
9373  * (the transformed problem is always a minimization problem)
9374  */
9375  if( !roundable )
9376  return SCIP_OKAY;
9377 
9378  SCIPdebugMessage("try fixing variable <%s>[%g,%g] to upper bound %g\n", SCIPvarGetName(var),
9380 
9381  SCIP_CALL( SCIPfixVar(scip, var, SCIPvarGetUbLocal(var), &infeasible, &tightened) );
9382  assert(!infeasible);
9383 
9384  if( tightened )
9385  {
9386  SCIPdebugMessage("fix variable <%s> to upper bound %g\n", SCIPvarGetName(var), SCIPvarGetUbLocal(var));
9387  (*nfixedvars)++;
9388  }
9389 
9390  return SCIP_OKAY;
9391 }
9392 
9393 /** fix integer variable to lower bound if the rounding locks and the object coefficient are in favor of that */
9394 static
9396  SCIP* scip, /**< SCIP data structure */
9397  SCIP_VAR* var, /**< integer variable to fix */
9398  SCIP_Bool downlock, /**< has the variable a down lock */
9399  int* nfixedvars /**< pointer to store the number fixed variables */
9400  )
9401 {
9402  SCIP_Bool infeasible;
9403  SCIP_Bool tightened;
9404  SCIP_Bool roundable;
9405 
9406  /* if SCIP is in probing mode or repropagation we cannot perform this dual reductions since this dual reduction
9407  * would/could end in an implication which can lead to cutoff of the/all optimal solution
9408  */
9409  if( SCIPinProbing(scip) || SCIPinRepropagation(scip) )
9410  return SCIP_OKAY;
9411 
9412  /* rounding the variable to the lower bound is only a feasible dual reduction if the cumulative constraint
9413  * handler is the only one locking that variable down
9414  */
9415  assert(downlock == TRUE || downlock == FALSE);
9416  assert((int)TRUE == 1);
9417  assert((int)FALSE == 0);
9418 
9419  if( SCIPvarGetNLocksDown(var) > (int)(downlock) )
9420  return SCIP_OKAY;
9421 
9422  SCIP_CALL( varMayRoundDown(scip, var, &roundable) );
9423 
9424  /* is it possible, to round variable down w.r.t. objective function? */
9425  if( !roundable )
9426  return SCIP_OKAY;
9427 
9428  SCIP_CALL( SCIPfixVar(scip, var, SCIPvarGetLbLocal(var), &infeasible, &tightened) );
9429  assert(!infeasible);
9430 
9431  if( tightened )
9432  {
9433  SCIPdebugMessage("fix variable <%s> to lower bound %g\n", SCIPvarGetName(var), SCIPvarGetLbLocal(var));
9434  (*nfixedvars)++;
9435  }
9436 
9437  return SCIP_OKAY;
9438 }
9439 
9440 /** normalize cumulative condition */
9441 static
9443  SCIP* scip, /**< SCIP data structure */
9444  int nvars, /**< number of start time variables (activities) */
9445  SCIP_VAR** vars, /**< array of start time variables */
9446  int* durations, /**< array of durations */
9447  int* demands, /**< array of demands */
9448  int* capacity, /**< pointer to store the changed cumulative capacity */
9449  int* nchgcoefs, /**< pointer to count total number of changed coefficients */
9450  int* nchgsides /**< pointer to count number of side changes */
9451  )
9452 { /*lint --e{715}*/
9453  SCIP_Longint gcd;
9454  int mindemand1;
9455  int mindemand2;
9456  int v;
9457 
9458  if( *capacity == 1 || nvars <= 1 )
9459  return SCIP_OKAY;
9460 
9461  assert(demands[nvars-1] <= *capacity);
9462  assert(demands[nvars-2] <= *capacity);
9463 
9464  gcd = (SCIP_Longint)demands[nvars-1];
9465  mindemand1 = MIN(demands[nvars-1], demands[nvars-2]);
9466  mindemand2 = MAX(demands[nvars-1], demands[nvars-2]);
9467 
9468  for( v = nvars-2; v >= 0 && (gcd >= 2 || mindemand1 + mindemand2 > *capacity); --v )
9469  {
9470  assert(mindemand1 <= mindemand2);
9471  assert(demands[v] <= *capacity);
9472 
9473  gcd = SCIPcalcGreComDiv(gcd, (SCIP_Longint)demands[v]);
9474 
9475  if( mindemand1 > demands[v] )
9476  {
9477  mindemand2 = mindemand1;
9478  mindemand1 = demands[v];
9479  }
9480  else if( mindemand2 > demands[v] )
9481  mindemand2 = demands[v];
9482  }
9483 
9484  if( mindemand1 + mindemand2 > *capacity )
9485  {
9486  SCIPdebugMessage("update cumulative condition (%d + %d > %d) to unary cumulative condition\n", mindemand1, mindemand2, *capacity);
9487 
9488  for( v = 0; v < nvars; ++v )
9489  demands[v] = 1;
9490 
9491  (*capacity) = 1;
9492 
9493  (*nchgcoefs) += nvars;
9494  (*nchgsides)++;
9495  }
9496  else if( gcd >= 2 )
9497  {
9498  SCIPdebugMessage("cumulative condition: dividing demands by %"SCIP_LONGINT_FORMAT"\n", gcd);
9499 
9500  for( v = 0; v < nvars; ++v )
9501  demands[v] /= gcd;
9502 
9503  (*capacity) /= gcd;
9504 
9505  (*nchgcoefs) += nvars;
9506  (*nchgsides)++;
9507  }
9508 
9509  return SCIP_OKAY;
9510 }
9511 
9512 /** divides demands by their greatest common divisor and divides capacity by the same value, rounding down the result;
9513  * in case the the smallest demands add up to more than the capacity we reductions all demands to one as well as the
9514  * capacity since in that case none of the jobs can run in parallel
9515  */
9516 static
9518  SCIP* scip, /**< SCIP data structure */
9519  SCIP_CONS* cons, /**< cumulative constraint */
9520  int* nchgcoefs, /**< pointer to count total number of changed coefficients */
9521  int* nchgsides /**< pointer to count number of side changes */
9522  )
9523 {
9524  SCIP_CONSDATA* consdata;
9525  int capacity;
9526 
9527  assert(nchgcoefs != NULL);
9528  assert(nchgsides != NULL);
9529  assert(!SCIPconsIsModifiable(cons));
9530 
9531  consdata = SCIPconsGetData(cons);
9532  assert(consdata != NULL);
9533 
9534  if( consdata->normalized )
9535  return SCIP_OKAY;
9536 
9537  capacity = consdata->capacity;
9538 
9539  /**@todo sort items w.r.t. the demands, because we can stop earlier if the smaller weights are evaluated first */
9540 
9541  SCIP_CALL( normalizeCumulativeCondition(scip, consdata->nvars, consdata->vars, consdata->durations,
9542  consdata->demands, &consdata->capacity, nchgcoefs, nchgsides) );
9543 
9544  consdata->normalized = TRUE;
9545 
9546  if( capacity > consdata->capacity )
9547  consdata->varbounds = FALSE;
9548 
9549  return SCIP_OKAY;
9550 }
9551 
9552 /** computes for the given cumulative condition the effective horizon */
9553 static
9555  SCIP* scip, /**< SCIP data structure */
9556  int nvars, /**< number of variables (jobs) */
9557  SCIP_VAR** vars, /**< array of integer variable which corresponds to starting times for a job */
9558  int* durations, /**< array containing corresponding durations */
9559  int* demands, /**< array containing corresponding demands */
9560  int capacity, /**< available cumulative capacity */
9561  int* hmin, /**< pointer to store the left bound of the effective horizon */
9562  int* hmax, /**< pointer to store the right bound of the effective horizon */
9563  int* split /**< point were the cumulative condition can be split */
9564  )
9565 {
9566  SCIP_PROFILE* profile;
9567 
9568  /* create empty resource profile with infinity resource capacity */
9569  SCIP_CALL( SCIPprofileCreate(&profile, INT_MAX) );
9570 
9571  /* create worst case resource profile */
9572  SCIP_CALL( SCIPcreateWorstCaseProfile(scip, profile, nvars, vars, durations, demands) );
9573 
9574  /* print resource profile in if SCIP_DEBUG is defined */
9575  SCIPdebug( SCIPprofilePrint(profile, SCIPgetMessagehdlr(scip), NULL) );
9576 
9577  /* computes the first time point where the resource capacity can be violated */
9578  (*hmin) = SCIPcomputeHmin(scip, profile, capacity);
9579 
9580  /* computes the first time point where the resource capacity is satisfied for sure */
9581  (*hmax) = SCIPcomputeHmax(scip, profile, capacity);
9582 
9583  (*split) = (*hmax);
9584 
9585  if( *hmin < *hmax && !SCIPinRepropagation(scip) )
9586  {
9587  int* timepoints;
9588  int* loads;
9589  int ntimepoints;
9590  int t;
9591 
9592  /* If SCIP is repropagating the root node, it is not possible to decompose the constraints. This is the case since
9593  * the conflict analysis stores the constraint pointer for bound changes made by this constraint. These pointer
9594  * are used during the resolve propagation phase to explain bound changes. If we would decompose certain jobs into
9595  * a new cumulative constraint, the "old" pointer is not valid. More precise, the "old" constraint is not able to
9596  * explain the certain "old" bound changes
9597  */
9598 
9599  /* search for time points */
9600  ntimepoints = SCIPprofileGetNTimepoints(profile);
9601  timepoints = SCIPprofileGetTimepoints(profile);
9602  loads = SCIPprofileGetLoads(profile);
9603 
9604  /* 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 */
9605  for( t = 0; t < ntimepoints; ++t )
9606  {
9607  /* ignore all time points before the effective horizon */
9608  if( timepoints[t] <= *hmin )
9609  continue;
9610 
9611  /* ignore all time points after the effective horizon */
9612  if( timepoints[t] >= *hmax )
9613  break;
9614 
9615  /* check if the current time point does not exceed the capacity w.r.t. worst case resource profile; if so we
9616  * can split the cumulative constraint into two cumulative constraints
9617  */
9618  if( loads[t] <= capacity )
9619  {
9620  (*split) = timepoints[t];
9621  break;
9622  }
9623  }
9624  }
9625 
9626  /* free worst case profile */
9627  SCIPprofileFree(&profile);
9628 
9629  return SCIP_OKAY;
9630 }
9631 
9632 /** creates and adds a cumulative constraint */
9633 static
9635  SCIP* scip, /**< SCIP data structure */
9636  const char* name, /**< name of constraint */
9637  int nvars, /**< number of variables (jobs) */
9638  SCIP_VAR** vars, /**< array of integer variable which corresponds to starting times for a job */
9639  int* durations, /**< array containing corresponding durations */
9640  int* demands, /**< array containing corresponding demands */
9641  int capacity, /**< available cumulative capacity */
9642  int hmin, /**< left bound of time axis to be considered (including hmin) */
9643  int hmax, /**< right bound of time axis to be considered (not including hmax) */
9644  SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP?
9645  * Usually set to TRUE. Set to FALSE for 'lazy constraints'. */
9646  SCIP_Bool separate, /**< should the constraint be separated during LP processing?
9647  * Usually set to TRUE. */
9648  SCIP_Bool enforce, /**< should the constraint be enforced during node processing?
9649  * TRUE for model constraints, FALSE for additional, redundant constraints. */
9650  SCIP_Bool check, /**< should the constraint be checked for feasibility?
9651  * TRUE for model constraints, FALSE for additional, redundant constraints. */
9652  SCIP_Bool propagate, /**< should the constraint be propagated during node processing?
9653  * Usually set to TRUE. */
9654  SCIP_Bool local, /**< is constraint only valid locally?
9655  * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */
9656  SCIP_Bool modifiable, /**< is constraint modifiable (subject to column generation)?
9657  * Usually set to FALSE. In column generation applications, set to TRUE if pricing
9658  * adds coefficients to this constraint. */
9659  SCIP_Bool dynamic, /**< is constraint subject to aging?
9660  * Usually set to FALSE. Set to TRUE for own cuts which
9661  * are seperated as constraints. */
9662  SCIP_Bool removable, /**< should the relaxation be removed from the LP due to aging or cleanup?
9663  * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */
9664  SCIP_Bool stickingatnode /**< should the constraint always be kept at the node where it was added, even
9665  * if it may be moved to a more global node?
9666  * Usually set to FALSE. Set to TRUE to for constraints that represent node data. */
9667  )
9668 {
9669  SCIP_CONS* cons;
9670 
9671  /* creates cumulative constraint and adds it to problem */
9672  SCIP_CALL( SCIPcreateConsCumulative(scip, &cons, name, nvars, vars, durations, demands, capacity,
9673  initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode) );
9674 
9675  /* adjust the effective time horizon of the new constraint */
9676  SCIP_CALL( SCIPsetHminCumulative(scip, cons, hmin) );
9677  SCIP_CALL( SCIPsetHmaxCumulative(scip, cons, hmax) );
9678 
9679  /* add and release new cumulative constraint */
9680  SCIP_CALL( SCIPaddCons(scip, cons) );
9681  SCIP_CALL( SCIPreleaseCons(scip, &cons) );
9682 
9683  return SCIP_OKAY;
9684 }
9685 
9686 /** computes the effective horizon and checks if the constraint can be decompsed */
9687 static
9689  SCIP* scip, /**< SCIP data structure */
9690  SCIP_CONS* cons, /**< cumulative constraint */
9691  int* ndelconss, /**< pointer to store the number of deleted constraints */
9692  int* naddconss, /**< pointer to store the number of added constraints */
9693  int* nchgsides /**< pointer to store the number of changed sides */
9694  )
9695 {
9696  SCIP_CONSDATA* consdata;
9697  int hmin;
9698  int hmax;
9699  int split;
9700 
9701  consdata = SCIPconsGetData(cons);
9702  assert(consdata != NULL);
9703 
9704  if( consdata->nvars <= 1 )
9705  return SCIP_OKAY;
9706 
9707  SCIP_CALL( computeEffectiveHorizonCumulativeCondition(scip, consdata->nvars, consdata->vars,
9708  consdata->durations, consdata->demands, consdata->capacity, &hmin, &hmax, &split) );
9709 
9710  /* check if this time point improves the effective horizon */
9711  if( consdata->hmin < hmin )
9712  {
9713  SCIPdebugMessage("cumulative constraint <%s> adjust hmin <%d> -> <%d>\n", SCIPconsGetName(cons), consdata->hmin, hmin);
9714 
9715  consdata->hmin = hmin;
9716  (*nchgsides)++;
9717  }
9718 
9719  /* check if this time point improves the effective horizon */
9720  if( consdata->hmax > hmax )
9721  {
9722  SCIPdebugMessage("cumulative constraint <%s> adjust hmax <%d> -> <%d>\n", SCIPconsGetName(cons), consdata->hmax, hmax);
9723  consdata->hmax = hmax;
9724  (*nchgsides)++;
9725  }
9726 
9727  /* check if the constraint is redundant */
9728  if( consdata->hmax <= consdata->hmin )
9729  {
9730  SCIPdebugMessage("constraint <%s> is redundant since hmax(%d) <= hmin(%d)\n",
9731  SCIPconsGetName(cons), consdata->hmax, consdata->hmin);
9732 
9733  SCIP_CALL( SCIPdelCons(scip, cons) );
9734  (*ndelconss)++;
9735  }
9736  else if( consdata->hmin < split && split < consdata->hmax )
9737  {
9738  char name[SCIP_MAXSTRLEN];
9739  (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "(%s)'", SCIPconsGetName(cons));
9740 
9741  SCIPdebugMessage("split cumulative constraint <%s>[%d,%d) with %d jobs at time point %d\n",
9742  SCIPconsGetName(cons), consdata->hmin, consdata->hmax, consdata->nvars, split);
9743 
9744  assert(split < consdata->hmax);
9745 
9746  /* creates cumulative constraint and adds it to problem */
9747  SCIP_CALL( createConsCumulative(scip, name, consdata->nvars, consdata->vars,
9748  consdata->durations, consdata->demands, consdata->capacity, split, consdata->hmax,
9751 
9752  /* adjust the effective time horizon of the constraint */
9753  consdata->hmax = split;
9754 
9755  assert(consdata->hmin < consdata->hmax);
9756 
9757  /* for the statistic we count the number of time we decompose a cumulative constraint */
9759  (*naddconss)++;
9760  }
9761 
9762  return SCIP_OKAY;
9763 }
9764 
9765 
9766 /** presolve cumulative condition w.r.t. the earlier start times (est) and the hmin of the effective horizon
9767  *
9768  * (1) If the latest completion time (lct) of a job is smaller or equal than hmin, the corresponding job can be removed
9769  * form the constraint. This is the case since it cannot effect any assignment within the effective horizon
9770  *
9771  * (2) If the latest start time (lst) of a job is smaller or equal than hmin it follows that the this jobs can run
9772  * before the effective horizon or it overlaps with the effective horizon such that hmin in included. Hence, the
9773  * down-lock of the corresponding start time variable can be removed.
9774  *
9775  * (3) If the earlier completion time (ect) of a job is smaller or equal than hmin, the cumulative is the only one
9776  * locking the corresponding variable down, and the objective coefficient of the start time variable is not
9777  * negative, than the job can be dual fixed to its earlier start time (est).
9778  *
9779  * (4) If the earlier start time (est) of job is smaller than the hmin, the cumulative is the only one locking the
9780  * corresponding variable down, and the objective coefficient of the start time variable is not negative, than
9781  * removing the values {est+1,...,hmin} form variable domain is dual feasible.
9782  *
9783  * (5) If the earlier start time (est) of job is smaller than the smallest earlier completion times of all other jobs
9784  * (lets denote this with minect), the cumulative is the only one locking the corresponding variable down, and the
9785  * objective coefficient of the start time variable is not negative, than removing the values {est+1,...,minect-1}
9786  * form variable domain is dual feasible.
9787  *
9788  * @note That method does not remove any variable form the arrays. It only marks the variables which are irrelevant for
9789  * the cumulative condition; The deletion has to be done later.
9790  */
9791 static
9793  SCIP* scip, /**< SCIP data structure */
9794  int nvars, /**< number of start time variables (activities) */
9795  SCIP_VAR** vars, /**< array of start time variables */
9796  int* durations, /**< array of durations */
9797  int hmin, /**< left bound of time axis to be considered (including hmin) */
9798  int hmax, /**< right bound of time axis to be considered (not including hmax) */
9799  SCIP_Bool* downlocks, /**< array to store if the variable has a down lock, or NULL */
9800  SCIP_Bool* uplocks, /**< array to store if the variable has an up lock, or NULL */
9801  SCIP_CONS* cons, /**< underlying constraint, or NULL */
9802  SCIP_Bool* irrelevants, /**< array mark those variables which are irrelevant for the cumulative condition */
9803  int* nfixedvars, /**< pointer to store the number of fixed variables */
9804  int* nchgsides, /**< pointer to store the number of changed sides */
9805  SCIP_Bool* cutoff /**< buffer to store whether a cutoff is detected */
9806  )
9807 {
9808  SCIP_Real* downimpllbs;
9809  SCIP_Real* downimplubs;
9810  SCIP_Real* downproplbs;
9811  SCIP_Real* downpropubs;
9812  SCIP_Real* upimpllbs;
9813  SCIP_Real* upimplubs;
9814  SCIP_Real* upproplbs;
9815  SCIP_Real* uppropubs;
9816 
9817  int firstminect;
9818  int secondminect;
9819  int v;
9820 
9821  /* get temporary memory for storing probing results needed for step (4) and (5) */
9822  SCIP_CALL( SCIPallocBufferArray(scip, &downimpllbs, nvars) );
9823  SCIP_CALL( SCIPallocBufferArray(scip, &downimplubs, nvars) );
9824  SCIP_CALL( SCIPallocBufferArray(scip, &downproplbs, nvars) );
9825  SCIP_CALL( SCIPallocBufferArray(scip, &downpropubs, nvars) );
9826  SCIP_CALL( SCIPallocBufferArray(scip, &upimpllbs, nvars) );
9827  SCIP_CALL( SCIPallocBufferArray(scip, &upimplubs, nvars) );
9828  SCIP_CALL( SCIPallocBufferArray(scip, &upproplbs, nvars) );
9829  SCIP_CALL( SCIPallocBufferArray(scip, &uppropubs, nvars) );
9830 
9831  assert(scip != NULL);
9832  assert(nvars > 1);
9833  assert(cons != NULL);
9834 
9835  SCIPdebugMessage("check for irrelevant variable for cumulative condition (hmin %d) w.r.t. earlier start time\n", hmin);
9836 
9837  firstminect = INT_MAX;
9838  secondminect = INT_MAX;
9839 
9840  /* compute the two smallest earlier completion times; which are needed for step (5) */
9841  for( v = 0; v < nvars; ++v )
9842  {
9843  int ect;
9844 
9845  ect = convertBoundToInt(scip, SCIPvarGetLbGlobal(vars[v])) + durations[v];
9846 
9847  if( ect < firstminect )
9848  {
9849  secondminect = firstminect;
9850  firstminect = ect;
9851  }
9852  else if( ect < secondminect )
9853  secondminect = ect;
9854  }
9855 
9856  /* loop over all jobs and check if one of the 5 reductions can be applied */
9857  for( v = 0; v < nvars; ++v )
9858  {
9859  SCIP_VAR* var;
9860  int duration;
9861 
9862  int alternativelb;
9863  int minect;
9864  int est;
9865  int ect;
9866  int lst;
9867  int lct;
9868 
9869  var = vars[v];
9870  assert(var != NULL);
9871 
9872  duration = durations[v];
9873  assert(duration > 0);
9874 
9875  /* collect earlier start time (est), earlier completion time (ect), latest start time (lst), and latest completion
9876  * time (lct)
9877  */
9878  est = convertBoundToInt(scip, SCIPvarGetLbGlobal(var));
9879  ect = est + duration;
9880  lst = convertBoundToInt(scip, SCIPvarGetUbGlobal(var));
9881  lct = lst + duration;
9882 
9883  /* compute the earliest completion time of all remaining jobs */
9884  if( ect == firstminect )
9885  minect = secondminect;
9886  else
9887  minect = firstminect;
9888 
9889  /* compute potential alternative lower bound (step (4) and (5)) */
9890  alternativelb = MAX(hmin+1, minect);
9891  alternativelb = MIN(alternativelb, hmax);
9892 
9893  if( lct <= hmin )
9894  {
9895  /* (1) check if the job runs completely before the effective horizon; if so the job can be removed form the
9896  * cumulative condition
9897  */
9898  SCIPdebugMessage(" variable <%s>[%g,%g] with duration <%d> is irrelevant\n",
9899  SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var), duration);
9900 
9901  /* mark variable to be irrelevant */
9902  irrelevants[v] = TRUE;
9903 
9904  /* for the statistic we count the number of jobs which are irrelevant */
9905  SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->nirrelevantjobs++ );
9906  }
9907  else if( lst <= hmin && SCIPconsIsChecked(cons) )
9908  {
9909  /* (2) check if the jobs overlaps with the time point hmin if it overlaps at all with the effective horizon; if
9910  * so the down lock can be omitted
9911  */
9912 
9913  assert(downlocks != NULL);
9914  assert(uplocks != NULL);
9915 
9916  if( !uplocks[v] )
9917  {
9918  /* the variables has no up lock and we can also remove the down lock;
9919  * => lst <= hmin and ect >= hmax
9920  * => remove job and reduce capacity by the demand of that job
9921  *
9922  * We mark the job to be deletable. The removement together with the capacity reducion is done later
9923  */
9924 
9925  SCIPdebugMessage(" variables <%s>[%d,%d] (duration <%d>) is irrelevant due to no up lock\n",
9926  SCIPvarGetName(var), ect - duration, lst, duration);
9927 
9928  /* mark variable to be irrelevant */
9929  irrelevants[v] = TRUE;
9930 
9931  /* for the statistic we count the number of jobs which always run during the effective horizon */
9933  }
9934 
9935  if( downlocks[v] )
9936  {
9937  SCIPdebugMessage(" remove down lock of variable <%s>[%g,%g] with duration <%d>\n",
9938  SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var), duration);
9939 
9940  SCIP_CALL( SCIPunlockVarCons(scip, var, cons, TRUE, FALSE) );
9941  downlocks[v] = FALSE;
9942  (*nchgsides)++;
9943 
9944  /* for the statistic we count the number of removed locks */
9946  }
9947  }
9948  else if( ect <= hmin )
9949  {
9950  /* (3) check if the job can finish before the effective horizon starts; if so and the job can be fixed to its
9951  * earliest start time (which implies that it finishes before the effective horizon starts), the job can be
9952  * removed form the cumulative condition after it was fixed to its earliest start time
9953  */
9954 
9955  /* job can be removed from the constraint only if the integer start time variable can be fixed to its lower
9956  * bound;
9957  */
9958  if( downlocks != NULL && SCIPconsIsChecked(cons) )
9959  {
9960  /* fix integer start time variable if possible to it lower bound */
9961  SCIP_CALL( fixIntegerVariableLb(scip, var, downlocks[v], nfixedvars) );
9962  }
9963 
9964  if( SCIPvarGetLbGlobal(var) + 0.5 > SCIPvarGetUbGlobal(var) )
9965  {
9966  SCIPdebugMessage(" variable <%s>[%d,%d] with duration <%d> is irrelevant due to dual fixing wrt EST\n",
9967  SCIPvarGetName(var), ect - duration, lst, duration);
9968 
9969  /* after fixing the start time variable to its lower bound, the (new) earliest completion time should be smaller or equal ti hmin */
9970  assert(convertBoundToInt(scip, SCIPvarGetLbGlobal(var)) + duration <= hmin);
9971 
9972  /* mark variable to be irrelevant */
9973  irrelevants[v] = TRUE;
9974 
9975  /* for the statistic we count the number of jobs which are dual fixed */
9977  }
9978  }
9979  else if( est < lst && est < alternativelb && SCIPconsIsChecked(cons) )
9980  {
9981  assert(downlocks != NULL);
9982 
9983  /* check step (4) and (5) */
9984 
9985  /* check if the cumulative constraint is the only one looking this variable down and if the objective function
9986  * is in favor of rounding the variable down
9987  */
9988  if( SCIPvarGetNLocksDown(var) == (int)(downlocks[v]) )
9989  {
9990  SCIP_Bool roundable;
9991 
9992  SCIP_CALL( varMayRoundDown(scip, var, &roundable) );
9993 
9994  if( roundable )
9995  {
9996  if( alternativelb > lst )
9997  {
9998  SCIP_Bool infeasible;
9999  SCIP_Bool fixed;
10000 
10001  SCIP_CALL( SCIPfixVar(scip, var, SCIPvarGetLbLocal(var), &infeasible, &fixed) );
10002  assert(!infeasible);
10003  assert(fixed);
10004 
10005  (*nfixedvars)++;
10006 
10007  /* for the statistic we count the number of jobs which are dual fixed due the information of all cumulative
10008  * constraints
10009  */
10011  }
10012  else
10013  {
10014  SCIP_Bool success;
10015 
10016  /* In the current version SCIP, variable domains are single intervals. Meaning that domain holes or not
10017  * representable. To retrieve a potential dual reduction we using probing to check both branches. If one in
10018  * infeasible we can apply the dual reduction; otherwise we do nothing
10019  */
10020  SCIP_CALL( applyProbingVar(scip, vars, nvars, v, (SCIP_Real) est, (SCIP_Real) alternativelb,
10021  downimpllbs, downimplubs, downproplbs, downpropubs, upimpllbs, upimplubs, upproplbs, uppropubs,
10022  nfixedvars, &success, cutoff) );
10023 
10024  if( success )
10025  {
10027  }
10028  }
10029  }
10030  }
10031  }
10032 
10033  SCIPdebugMessage("********* check variable <%s>[%g,%g] with duration <%d> (hmin %d)\n",
10034  SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var), duration, hmin);
10035  }
10036 
10037  /* free temporary memory */
10038  SCIPfreeBufferArray(scip, &uppropubs);
10039  SCIPfreeBufferArray(scip, &upproplbs);
10040  SCIPfreeBufferArray(scip, &upimplubs);
10041  SCIPfreeBufferArray(scip, &upimpllbs);
10042  SCIPfreeBufferArray(scip, &downpropubs);
10043  SCIPfreeBufferArray(scip, &downproplbs);
10044  SCIPfreeBufferArray(scip, &downimplubs);
10045  SCIPfreeBufferArray(scip, &downimpllbs);
10046 
10047  return SCIP_OKAY;
10048 }
10049 
10050 /** presolve cumulative condition w.r.t. the latest completion times (lct) and the hmax of the effective horizon
10051  *
10052  * (1) If the earliest start time (est) of a job is larger or equal than hmax, the corresponding job can be removed
10053  * form the constraint. This is the case since it cannot effect any assignment within the effective horizon
10054  *
10055  * (2) If the earliest completion time (ect) of a job is larger or equal than hmax it follows that the this jobs can run
10056  * before the effective horizon or it overlaps with the effective horizon such that hmax in included. Hence, the
10057  * up-lock of the corresponding start time variable can be removed.
10058  *
10059  * (3) If the latest start time (lst) of a job is larger or equal than hmax, the cumulative is the only one
10060  * locking the corresponding variable up, and the objective coefficient of the start time variable is not
10061  * positive, than the job can be dual fixed to its latest start time (lst).
10062  *
10063  * (4) If the latest completion time (lct) of job is larger than the hmax, the cumulative is the only one locking the
10064  * corresponding variable up, and the objective coefficient of the start time variable is not positive, than
10065  * removing the values {hmax - p_j, ..., lst-1} form variable domain is dual feasible (p_j is the processing time
10066  * of the corresponding job).
10067 
10068  * (5) If the latest completion time (lct) of job is smaller than the largerst latest start time of all other jobs
10069  * (lets denote this with maxlst), the cumulative is the only one locking the corresponding variable up, and the
10070  * objective coefficient of the start time variable is not positive, than removing the values {maxlst - p_j + 1,
10071  * ..., lst-1} form variable domain is dual feasible (p_j is the processing time of the corresponding job).
10072  *
10073  * @note That method does not remove any variable form the arrays. It only marks the variables which are irrelevant for
10074  * the cumulative condition; The deletion has to be done later.
10075  */
10076 static
10078  SCIP* scip, /**< SCIP data structure */
10079  int nvars, /**< number of start time variables (activities) */
10080  SCIP_VAR** vars, /**< array of start time variables */
10081  int* durations, /**< array of durations */
10082  int hmin, /**< left bound of time axis to be considered (including hmin) */
10083  int hmax, /**< right bound of time axis to be considered (not including hmax) */
10084  SCIP_Bool* downlocks, /**< array to store if the variable has a down lock, or NULL */
10085  SCIP_Bool* uplocks, /**< array to store if the variable has an up lock, or NULL */
10086  SCIP_CONS* cons, /**< underlying constraint, or NULL */
10087  SCIP_Bool* irrelevants, /**< array mark those variables which are irrelevant for the cumulative condition */
10088  int* nfixedvars, /**< pointer to counter which is increased by the number of deduced variable fixations */
10089  int* nchgsides, /**< pointer to store the number of changed sides */
10090  SCIP_Bool* cutoff /**< buffer to store whether a cutoff is detected */
10091  )
10092 {
10093  SCIP_Real* downimpllbs;
10094  SCIP_Real* downimplubs;
10095  SCIP_Real* downproplbs;
10096  SCIP_Real* downpropubs;
10097  SCIP_Real* upimpllbs;
10098  SCIP_Real* upimplubs;
10099  SCIP_Real* upproplbs;
10100  SCIP_Real* uppropubs;
10101 
10102  int firstmaxlst;
10103  int secondmaxlst;
10104  int v;
10105 
10106  /* get temporary memory for storing probing results needed for step (4) and (5) */
10107  SCIP_CALL( SCIPallocBufferArray(scip, &downimpllbs, nvars) );
10108  SCIP_CALL( SCIPallocBufferArray(scip, &downimplubs, nvars) );
10109  SCIP_CALL( SCIPallocBufferArray(scip, &downproplbs, nvars) );
10110  SCIP_CALL( SCIPallocBufferArray(scip, &downpropubs, nvars) );
10111  SCIP_CALL( SCIPallocBufferArray(scip, &upimpllbs, nvars) );
10112  SCIP_CALL( SCIPallocBufferArray(scip, &upimplubs, nvars) );
10113  SCIP_CALL( SCIPallocBufferArray(scip, &upproplbs, nvars) );
10114  SCIP_CALL( SCIPallocBufferArray(scip, &uppropubs, nvars) );
10115 
10116  assert(scip != NULL);
10117  assert(nvars > 1);
10118  assert(cons != NULL);
10119 
10120  SCIPdebugMessage("check for irrelevant variable for cumulative condition (hmax %d) w.r.t. latest completion time\n", hmax);
10121 
10122  firstmaxlst = INT_MIN;
10123  secondmaxlst = INT_MIN;
10124 
10125  /* compute the two largest latest start times; which are needed for step (5) */
10126  for( v = 0; v < nvars; ++v )
10127  {
10128  int lst;
10129 
10130  lst = convertBoundToInt(scip, SCIPvarGetUbGlobal(vars[v]));
10131 
10132  if( lst > firstmaxlst )
10133  {
10134  secondmaxlst = firstmaxlst;
10135  firstmaxlst = lst;
10136  }
10137  else if( lst > secondmaxlst )
10138  secondmaxlst = lst;
10139  }
10140 
10141  /* loop over all jobs and check if one of the 5 reductions can be applied */
10142  for( v = 0; v < nvars; ++v )
10143  {
10144  SCIP_VAR* var;
10145  int duration;
10146 
10147  int alternativeub;
10148  int maxlst;
10149  int est;
10150  int ect;
10151  int lst;
10152 
10153  var = vars[v];
10154  assert(var != NULL);
10155 
10156  duration = durations[v];
10157  assert(duration > 0);
10158 
10159  /* collect earlier start time (est), earlier completion time (ect), latest start time (lst), and latest completion
10160  * time (lct)
10161  */
10162  est = convertBoundToInt(scip, SCIPvarGetLbGlobal(var));
10163  ect = est + duration;
10164  lst = convertBoundToInt(scip, SCIPvarGetUbGlobal(var));
10165 
10166  /* compute the latest start time of all remaining jobs */
10167  if( lst == firstmaxlst )
10168  maxlst = secondmaxlst;
10169  else
10170  maxlst = firstmaxlst;
10171 
10172  /* compute potential alternative upper bound (step (4) and (5)) */
10173  alternativeub = MIN(hmax - 1, maxlst) - duration;
10174  alternativeub = MAX(alternativeub, hmin);
10175 
10176  if( est >= hmax )
10177  {
10178  /* (1) check if the job runs completely after the effective horizon; if so the job can be removed form the
10179  * cumulative condition
10180  */
10181  SCIPdebugMessage(" variable <%s>[%g,%g] with duration <%d> is irrelevant\n",
10182  SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var), duration);
10183 
10184  /* mark variable to be irrelevant */
10185  irrelevants[v] = TRUE;
10186 
10187  /* for the statistic we count the number of jobs which are irrelevant */
10188  SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->nirrelevantjobs++ );
10189  }
10190  else if( ect >= hmax && SCIPconsIsChecked(cons) )
10191  {
10192  assert(downlocks != NULL);
10193  assert(uplocks != NULL);
10194 
10195  /* (2) check if the jobs overlaps with the time point hmax if it overlaps at all with the effective horizon; if
10196  * so the up lock can be omitted
10197  */
10198 
10199  if( !downlocks[v] )
10200  {
10201  /* the variables has no down lock and we can also remove the up lock;
10202  * => lst <= hmin and ect >= hmax
10203  * => remove job and reduce capacity by the demand of that job
10204  */
10205  SCIPdebugMessage(" variables <%s>[%d,%d] with duration <%d> is irrelevant due to no down lock\n",
10206  SCIPvarGetName(var), est, lst, duration);
10207 
10208  /* mark variable to be irrelevant */
10209  irrelevants[v] = TRUE;
10210 
10211  /* for the statistic we count the number of jobs which always run during the effective horizon */
10213  }
10214 
10215  if( uplocks[v] )
10216  {
10217  SCIPdebugMessage(" remove up lock of variable <%s>[%g,%g] with duration <%d>\n",
10218  SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var), duration);
10219 
10220  SCIP_CALL( SCIPunlockVarCons(scip, var, cons, FALSE, TRUE) );
10221  uplocks[v] = FALSE;
10222  (*nchgsides)++;
10223 
10224  /* for the statistic we count the number of removed locks */
10226  }
10227  }
10228  else if( lst >= hmax )
10229  {
10230  /* (3) check if the job can start after the effective horizon finishes; if so and the job can be fixed to its
10231  * latest start time (which implies that it starts after the effective horizon finishes), the job can be
10232  * removed form the cumulative condition after it was fixed to its latest start time
10233  */
10234 
10235  /* job can be removed from the constraint only if the integer start time variable can be fixed to its upper
10236  * bound
10237  */
10238  if( uplocks != NULL && SCIPconsIsChecked(cons) )
10239  {
10240  /* fix integer start time variable if possible to its upper bound */
10241  SCIP_CALL( fixIntegerVariableUb(scip, var, uplocks[v], nfixedvars) );
10242  }
10243 
10244  if( SCIPvarGetLbGlobal(var) + 0.5 > SCIPvarGetUbGlobal(var) )
10245  {
10246  SCIPdebugMessage(" variable <%s>[%d,%d] with duration <%d> is irrelevant due to dual fixing wrt LCT\n",
10247  SCIPvarGetName(var), est, lst, duration);
10248 
10249  /* after fixing the start time variable to its upper bound, the (new) latest start time should be greather or equal ti hmax */
10250  assert(convertBoundToInt(scip, SCIPvarGetUbGlobal(var)) >= hmax);
10251 
10252  /* mark variable to be irrelevant */
10253  irrelevants[v] = TRUE;
10254 
10255  /* for the statistic we count the number of jobs which are dual fixed */
10257  }
10258  }
10259  else if( est < lst && lst > alternativeub && SCIPconsIsChecked(cons) )
10260  {
10261  assert(uplocks != NULL);
10262 
10263  /* check step (4) and (5) */
10264 
10265  /* check if the cumulative constraint is the only one looking this variable down and if the objective function
10266  * is in favor of rounding the variable down
10267  */
10268  if( SCIPvarGetNLocksUp(var) == (int)(uplocks[v]) )
10269  {
10270  SCIP_Bool roundable;
10271 
10272  SCIP_CALL( varMayRoundUp(scip, var, &roundable) );
10273 
10274  if( roundable )
10275  {
10276  if( alternativeub < est )
10277  {
10278  SCIP_Bool infeasible;
10279  SCIP_Bool fixed;
10280 
10281  SCIP_CALL( SCIPfixVar(scip, var, SCIPvarGetUbLocal(var), &infeasible, &fixed) );
10282  assert(!infeasible);
10283  assert(fixed);
10284 
10285  (*nfixedvars)++;
10286 
10287  /* for the statistic we count the number of jobs which are dual fixed due the information of all cumulative
10288  * constraints
10289  */
10291  }
10292  else
10293  {
10294  SCIP_Bool success;
10295 
10296  /* In the current version SCIP, variable domains are single intervals. Meaning that domain holes or not
10297  * representable. To retrieve a potential dual reduction we using probing to check both branches. If one
10298  * in infeasible we can apply the dual reduction; otherwise we do nothing
10299  */
10300  SCIP_CALL( applyProbingVar(scip, vars, nvars, v, (SCIP_Real) alternativeub, (SCIP_Real) lst,
10301  downimpllbs, downimplubs, downproplbs, downpropubs, upimpllbs, upimplubs, upproplbs, uppropubs,
10302  nfixedvars, &success, cutoff) );
10303 
10304  if( success )
10305  {
10307  }
10308  }
10309  }
10310  }
10311  }
10312  }
10313 
10314  /* free temporary memory */
10315  SCIPfreeBufferArray(scip, &uppropubs);
10316  SCIPfreeBufferArray(scip, &upproplbs);
10317  SCIPfreeBufferArray(scip, &upimplubs);
10318  SCIPfreeBufferArray(scip, &upimpllbs);
10319  SCIPfreeBufferArray(scip, &downpropubs);
10320  SCIPfreeBufferArray(scip, &downproplbs);
10321  SCIPfreeBufferArray(scip, &downimplubs);
10322  SCIPfreeBufferArray(scip, &downimpllbs);
10323 
10324  return SCIP_OKAY;
10325 }
10326 
10327 /** presolve cumulative constraint w.r.t. the boundary of the effective horizon */
10328 static
10330  SCIP* scip, /**< SCIP data structure */
10331  SCIP_CONS* cons, /**< cumulative constraint */
10332  int* nfixedvars, /**< pointer to store the number of fixed variables */
10333  int* nchgcoefs, /**< pointer to store the number of changed coefficients */
10334  int* nchgsides, /**< pointer to store the number of changed sides */
10335  SCIP_Bool* cutoff /**< pointer to store if a cutoff was detected */
10336  )
10337 {
10338  SCIP_CONSDATA* consdata;
10339  SCIP_Bool* irrelevants;
10340  int nvars;
10341  int v;
10342 
10343  assert(scip != NULL);
10344  assert(cons != NULL);
10345  assert(!(*cutoff));
10346 
10347  consdata = SCIPconsGetData(cons);
10348  assert(consdata != NULL);
10349 
10350  nvars = consdata->nvars;
10351 
10352  if( nvars <= 1 )
10353  return SCIP_OKAY;
10354 
10355  SCIP_CALL( SCIPallocBufferArray(scip, &irrelevants, nvars) );
10356  BMSclearMemoryArray(irrelevants, nvars);
10357 
10358  /* presolve constraint form the earlier start time point of view */
10359  SCIP_CALL( presolveConsEst(scip, nvars, consdata->vars, consdata->durations,
10360  consdata->hmin, consdata->hmax, consdata->downlocks, consdata->uplocks, cons,
10361  irrelevants, nfixedvars, nchgsides, cutoff) );
10362 
10363  /* presolve constraint form the latest completion time point of view */
10364  SCIP_CALL( presolveConsLct(scip, nvars, consdata->vars, consdata->durations,
10365  consdata->hmin, consdata->hmax, consdata->downlocks, consdata->uplocks, cons,
10366  irrelevants, nfixedvars, nchgsides, cutoff) );
10367 
10368  /* remove variables from the cumulative constraint which are marked to be deleted; we need to that in the reverse
10369  * order to ensure a correct behaviour
10370  */
10371  for( v = nvars-1; v >= 0; --v )
10372  {
10373  if( irrelevants[v] )
10374  {
10375  SCIP_VAR* var;
10376  int ect;
10377  int lst;
10378 
10379  var = consdata->vars[v];
10380  assert(var != NULL);
10381 
10382  ect = convertBoundToInt(scip, SCIPvarGetLbGlobal(var)) + consdata->durations[v];
10383  lst = convertBoundToInt(scip, SCIPvarGetUbGlobal(var));
10384 
10385  /* check if the jobs runs completely during the effective horizon */
10386  if( lst <= consdata->hmin && ect >= consdata->hmax )
10387  {
10388  if( consdata->capacity < consdata->demands[v] )
10389  {
10390  *cutoff = TRUE;
10391  break;
10392  }
10393 
10394  consdata->capacity -= consdata->demands[v];
10395  consdata->varbounds = FALSE;
10396  }
10397 
10398  SCIP_CALL( consdataDeletePos(scip, consdata, cons, v) );
10399  (*nchgcoefs)++;
10400  }
10401  }
10402 
10403  SCIPfreeBufferArray(scip, &irrelevants);
10404 
10405  return SCIP_OKAY;
10406 }
10407 
10408 /** stores all demands which are smaller than the capacity of those jobs that are running at 'curtime' */
10409 static
10411  SCIP* scip, /**< SCIP data structure */
10412  SCIP_CONSDATA* consdata, /**< constraint data */
10413  int* startindices, /**< permutation with rspect to the start times */
10414  int curtime, /**< current point in time */
10415  int nstarted, /**< number of jobs that start before the curtime or at curtime */
10416  int nfinished, /**< number of jobs that finished before curtime or at curtime */
10417  SCIP_Longint** demands, /**< pointer to array storing the demands */
10418  int* ndemands /**< pointer to store the number of different demands */
10419  )
10420 {
10421  int startindex;
10422  int ncountedvars;
10423 
10424  assert(demands != NULL);
10425  assert(ndemands != NULL);
10426 
10427  ncountedvars = 0;
10428  startindex = nstarted - 1;
10429 
10430  *ndemands = 0;
10431 
10432  /* search for the (nstarted - nfinished) jobs which are active at curtime */
10433  while( nstarted - nfinished > ncountedvars )
10434  {
10435  SCIP_VAR* var;
10436  int endtime;
10437  int varidx;
10438 
10439  /* collect job information */
10440  varidx = startindices[startindex];
10441  assert(varidx >= 0 && varidx < consdata->nvars);
10442 
10443  var = consdata->vars[varidx];
10444  assert(var != NULL);
10445 
10446  endtime = convertBoundToInt(scip, SCIPvarGetUbGlobal(var)) + consdata->durations[varidx];
10447 
10448  /* check the end time of this job is larger than the curtime; in this case the job is still running */
10449  if( endtime > curtime )
10450  {
10451  if( consdata->demands[varidx] < consdata->capacity )
10452  {
10453  (*demands)[*ndemands] = consdata->demands[varidx];
10454  (*ndemands)++;
10455  }
10456  ncountedvars++;
10457  }
10458 
10459  startindex--;
10460  }
10461 
10462  return SCIP_OKAY;
10463 }
10464 
10465 /** this method creates a row for time point curtime which insures the capacity restriction of the cumulative
10466  * constraint
10467  */
10468 static
10470  SCIP* scip, /**< SCIP data structure */
10471  SCIP_CONS* cons, /**< constraint to be checked */
10472  int* startindices, /**< permutation with rspect to the start times */
10473  int curtime, /**< current point in time */
10474  int nstarted, /**< number of jobs that start before the curtime or at curtime */
10475  int nfinished, /**< number of jobs that finished before curtime or at curtime */
10476  int* bestcapacity /**< pointer to store the maximum possible capacity usage */
10477  )
10478 {
10479  SCIP_CONSDATA* consdata;
10480  SCIP_Longint* demands;
10481  SCIP_Real* profits;
10482  int* items;
10483  int ndemands;
10484  SCIP_Bool success;
10485  SCIP_Real solval;
10486  int j;
10487  assert(nstarted > nfinished);
10488 
10489  consdata = SCIPconsGetData(cons);
10490  assert(consdata != NULL);
10491  assert(consdata->nvars > 0);
10492  assert(consdata->capacity > 0);
10493 
10494  SCIP_CALL( SCIPallocBufferArray(scip, &demands, consdata->nvars) );
10495  ndemands = 0;
10496 
10497  /* get demand array to initialize knapsack problem */
10498  SCIP_CALL( collectDemands(scip, consdata, startindices, curtime, nstarted, nfinished, &demands, &ndemands) );
10499 
10500  /* create array for profits */
10501  SCIP_CALL( SCIPallocBufferArray(scip, &profits, ndemands) );
10502  SCIP_CALL( SCIPallocBufferArray(scip, &items, ndemands) );
10503  for( j = 0; j < ndemands; ++j )
10504  {
10505  profits[j] = (SCIP_Real) demands[j];
10506  items[j] = j;/* this is only a dummy value*/
10507  }
10508 
10509  /* solve knapsack problem and get maximum capacity usage <= capacity */
10510  SCIP_CALL( SCIPsolveKnapsackExactly(scip, ndemands, demands, profits, (SCIP_Longint)consdata->capacity,
10511  items, NULL, NULL, NULL, NULL, &solval, &success) );
10512 
10513  assert(SCIPisFeasIntegral(scip, solval));
10514 
10515  /* store result */
10516  *bestcapacity = convertBoundToInt(scip, solval);
10517 
10518  SCIPfreeBufferArray(scip, &items);
10519  SCIPfreeBufferArray(scip, &profits);
10520  SCIPfreeBufferArray(scip, &demands);
10521 
10522  return SCIP_OKAY;
10523 }
10524 
10525 /** try to tighten the capacity
10526  * -- using DP for knapsack, we find the maximum possible capacity usage
10527  * -- neglects hmin and hmax, such that it is also able to check solutions globally
10528  */
10529 static
10531  SCIP* scip, /**< SCIP data structure */
10532  SCIP_CONS* cons, /**< cumulative constraint */
10533  int* nchgcoefs, /**< pointer to count total number of changed coefficients */
10534  int* nchgsides /**< pointer to store the number of changed sides */
10535  )
10536 {
10537  SCIP_CONSDATA* consdata;
10538  int* starttimes; /* stores when each job is starting */
10539  int* endtimes; /* stores when each job ends */
10540  int* startindices; /* we will sort the startsolvalues, thus we need to know wich index of a job it corresponds to */
10541  int* endindices; /* we will sort the endsolvalues, thus we need to know wich index of a job it corresponds to */
10542 
10543  int nvars; /* number of activities for this constraint */
10544  int freecapacity; /* remaining capacity */
10545  int curtime; /* point in time which we are just checking */
10546  int endindex; /* index of endsolvalues with: endsolvalues[endindex] > curtime */
10547 
10548  int bestcapacity;
10549 
10550  int j;
10551 
10552  assert(scip != NULL);
10553  assert(cons != NULL);
10554  assert(nchgsides != NULL);
10555 
10556  consdata = SCIPconsGetData(cons);
10557  assert(consdata != NULL);
10558 
10559  nvars = consdata->nvars;
10560 
10561  /* if no activities are associated with this cumulative or the capacity is 1, then this constraint is redundant */
10562  if( nvars <= 1 || consdata->capacity <= 1 )
10563  return SCIP_OKAY;
10564 
10565  assert(consdata->vars != NULL);
10566 
10567  SCIPdebugMessage("try to tighten capacity for cumulative constraint <%s> with capacity %d\n",
10568  SCIPconsGetName(cons), consdata->capacity);
10569 
10570  SCIP_CALL( SCIPallocBufferArray(scip, &starttimes, nvars) );
10571  SCIP_CALL( SCIPallocBufferArray(scip, &endtimes, nvars) );
10572  SCIP_CALL( SCIPallocBufferArray(scip, &startindices, nvars) );
10573  SCIP_CALL( SCIPallocBufferArray(scip, &endindices, nvars) );
10574 
10575  /* create event point arrays */
10576  createSortedEventpoints(scip, nvars, consdata->vars, consdata->durations,
10577  starttimes, endtimes, startindices, endindices, FALSE);
10578 
10579  bestcapacity = 1;
10580  endindex = 0;
10581  freecapacity = consdata->capacity;
10582 
10583  /* check each startpoint of a job whether the capacity is kept or not */
10584  for( j = 0; j < nvars && bestcapacity < consdata->capacity; ++j )
10585  {
10586  curtime = starttimes[j];
10587  SCIPdebugMessage("look at %d-th job with start %d\n", j, curtime);
10588 
10589  /* remove the capacity requirments for all job which start at the curtime */
10590  subtractStartingJobDemands(consdata, curtime, starttimes, startindices, &freecapacity, &j, nvars);
10591 
10592  /* add the capacity requirments for all job which end at the curtime */
10593  addEndingJobDemands(consdata, curtime, endtimes, endindices, &freecapacity, &endindex, nvars);
10594 
10595  assert(freecapacity <= consdata->capacity);
10596  assert(endindex <= nvars);
10597 
10598  /* endindex - points to the next job which will finish */
10599  /* j - points to the last job that has been released */
10600 
10601  /* check point in time when capacity is exceeded (here, a knapsack problem must be solved) */
10602  if( freecapacity < 0 )
10603  {
10604  int newcapacity;
10605 
10606  newcapacity = 1;
10607 
10608  /* get best possible upper bound on capacity usage */
10609  SCIP_CALL( getHighestCapacityUsage(scip, cons, startindices, curtime, j+1, endindex, &newcapacity) );
10610 
10611  /* update bestcapacity */
10612  bestcapacity = MAX(bestcapacity, newcapacity);
10613  SCIPdebugMessage("after highest cap usage: bestcapacity = %d\n", bestcapacity);
10614  }
10615 
10616  /* also those points in time, where the capacity limit is not exceeded, must be taken into account */
10617  if( freecapacity > 0 && freecapacity != consdata->capacity )
10618  {
10619  bestcapacity = MAX(bestcapacity, consdata->capacity - freecapacity);
10620  SCIPdebugMessage("after peak < cap: bestcapacity = %d\n", bestcapacity);
10621  }
10622 
10623  /* capacity cannot be decreased if the demand sum over more than one job equals the capacity */
10624  if( freecapacity == 0 && consdata->demands[startindices[j]] < consdata->capacity)
10625  {
10626  /* if demands[startindices[j]] == cap then exactly that job is running */
10627  SCIPdebugMessage("--> cannot decrease capacity since sum equals capacity\n");
10628  bestcapacity = consdata->capacity;
10629  break;
10630  }
10631  } /*lint --e{850}*/
10632 
10633  /* free all buffer arrays */
10634  SCIPfreeBufferArray(scip, &endindices);
10635  SCIPfreeBufferArray(scip, &startindices);
10636  SCIPfreeBufferArray(scip, &endtimes);
10637  SCIPfreeBufferArray(scip, &starttimes);
10638 
10639  /* check whether capacity can be tightened and whether demands need to be adjusted */
10640  if( bestcapacity < consdata->capacity )
10641  {
10642  int oldnchgcoefs;
10643 
10644  oldnchgcoefs = *nchgcoefs;
10645 
10646  SCIPdebugMessage("+-+-+-+-+-+ --> CHANGE capacity of cons<%s> from %d to %d\n",
10647  SCIPconsGetName(cons), consdata->capacity, bestcapacity);
10648 
10649  for( j = 0; j < nvars; ++j )
10650  {
10651  if( consdata->demands[j] == consdata->capacity )
10652  {
10653  consdata->demands[j] = bestcapacity;
10654  (*nchgcoefs)++;
10655  }
10656  }
10657 
10658  consdata->capacity = bestcapacity;
10659  (*nchgsides)++;
10660 
10661  SCIPdebugPrintf("; changed additionally %d coefficients\n", (*nchgcoefs)-oldnchgcoefs);
10662 
10663  consdata->varbounds = FALSE;
10664  }
10665 
10666  return SCIP_OKAY;
10667 }
10668 
10669 /** tries to change coefficients:
10670  * demand_j < cap && all other parallel jobs in conflict
10671  * ==> set demand_j := cap
10672  */
10673 static
10675  SCIP* scip, /**< SCIP data structure */
10676  SCIP_CONS* cons, /**< cumulative constraint */
10677  int* nchgcoefs /**< pointer to count total number of changed coefficients */
10678  )
10679 {
10680  SCIP_CONSDATA* consdata;
10681  int nvars;
10682  int j;
10683  int oldnchgcoefs;
10684  int mindemand;
10685 
10686  assert(scip != NULL);
10687  assert(cons != NULL);
10688  assert(nchgcoefs != NULL);
10689 
10690  /* get constraint data for some parameter testings only! */
10691  consdata = SCIPconsGetData(cons);
10692  assert(consdata != NULL);
10693 
10694  nvars = consdata->nvars;
10695  oldnchgcoefs = *nchgcoefs;
10696 
10697  if( nvars <= 0 )
10698  return SCIP_OKAY;
10699 
10700  /* PRE1:
10701  * check all jobs j whether: r_j + r_min > capacity holds
10702  * if so: adjust r_j to capacity
10703  */
10704  mindemand = consdata->demands[0];
10705  for( j = 0; j < nvars; ++j )
10706  {
10707  mindemand = MIN(mindemand, consdata->demands[j]);
10708  }
10709 
10710  /*check each job */
10711  for( j = 0; j < nvars; ++j )
10712  {
10713  if( mindemand + consdata->demands[j] > consdata->capacity && consdata->demands[j] < consdata->capacity )
10714  {
10715  SCIPdebugMessage("+-+-+-+-+-+change demand of var<%s> from %d to capacity %d\n", SCIPvarGetName(consdata->vars[j]),
10716  consdata->demands[j], consdata->capacity);
10717  consdata->demands[j] = consdata->capacity;
10718  (*nchgcoefs)++;
10719  }
10720  }
10721 
10722  /* PRE2:
10723  * check for each job (with d_j < cap)
10724  * whether it is disjunctive to all others over the time horizon
10725  */
10726  for( j = 0; j < nvars; ++j )
10727  {
10728  SCIP_Bool chgcoef;
10729  int est_j;
10730  int lct_j;
10731  int i;
10732 
10733  assert(consdata->demands[j] <= consdata->capacity);
10734 
10735  if( consdata->demands[j] == consdata->capacity )
10736  continue;
10737 
10738  chgcoef = TRUE;
10739 
10740  est_j = convertBoundToInt(scip, SCIPvarGetLbLocal(consdata->vars[j]));
10741  lct_j = convertBoundToInt(scip, SCIPvarGetUbLocal(consdata->vars[j])) + consdata->durations[j];
10742 
10743  for( i = 0; i < nvars; ++i )
10744  {
10745  int est_i;
10746  int lct_i;
10747 
10748  if( i == j )
10749  continue;
10750 
10751  est_i = convertBoundToInt(scip, SCIPvarGetLbLocal(consdata->vars[i]));
10752  lct_i = convertBoundToInt(scip, SCIPvarGetUbLocal(consdata->vars[i])) + consdata->durations[i];
10753 
10754  if( est_i >= lct_j || est_j >= lct_i )
10755  continue;
10756 
10757  if( consdata->demands[j] + consdata->demands[i] <= consdata->capacity )
10758  {
10759  chgcoef = FALSE;
10760  break;
10761  }
10762  }
10763 
10764  if( chgcoef )
10765  {
10766  SCIPdebugMessage("+-+-+-+-+-+change demand of var<%s> from %d to capacity %d\n", SCIPvarGetName(consdata->vars[j]),
10767  consdata->demands[j], consdata->capacity);
10768  consdata->demands[j] = consdata->capacity;
10769  (*nchgcoefs)++;
10770  }
10771 
10772  }
10773 
10774  if( (*nchgcoefs) > oldnchgcoefs )
10775  {
10776  SCIPdebugMessage("+-+-+-+-+-+changed %d coefficients of variables of cumulative constraint<%s>\n",
10777  (*nchgcoefs) - oldnchgcoefs, SCIPconsGetName(cons));
10778  }
10779 
10780  return SCIP_OKAY;
10781 }
10782 
10783 #if 0
10784 /** try to reformulate constraint by replacing certain jobs */
10785 static
10786 SCIP_RETCODE reformulateCons(
10787  SCIP* scip, /**< SCIP data structure */
10788  SCIP_CONS* cons, /**< cumulative constraint */
10789  int* naggrvars /**< pointer to store the number of aggregated variables */
10790  )
10791 {
10792  SCIP_CONSDATA* consdata;
10793  int hmin;
10794  int hmax;
10795  int nvars;
10796  int v;
10797 
10798  consdata = SCIPconsGetData(cons);
10799  assert(cons != NULL);
10800 
10801  nvars = consdata->nvars;
10802  assert(nvars > 1);
10803 
10804  hmin = consdata->hmin;
10805  hmax = consdata->hmax;
10806  assert(hmin < hmax);
10807 
10808  for( v = 0; v < nvars; ++v )
10809  {
10810  SCIP_VAR* var;
10811  int duration;
10812  int est;
10813  int ect;
10814  int lst;
10815  int lct;
10816 
10817  var = consdata->vars[v];
10818  assert(var != NULL);
10819 
10820  duration = consdata->durations[v];
10821 
10822  est = convertBoundToInt(scip, SCIPvarGetLbGlobal(var));
10823  ect = est + duration;
10824  lst = convertBoundToInt(scip, SCIPvarGetUbGlobal(var));
10825  lct = lst + duration;
10826 
10827  /* jobs for which the core [lst,ect) contains [hmin,hmax) should be removed already */
10828  assert(lst > hmin || ect < hmax);
10829 
10830  if( lst <= hmin && est < hmin - lct + MIN(hmin, ect) )
10831  {
10832  SCIP_VAR* aggrvar;
10833  char name[SCIP_MAXSTRLEN];
10834  SCIP_Bool infeasible;
10835  SCIP_Bool redundant;
10836  SCIP_Bool aggregated;
10837  int shift;
10838 
10839  shift = est - (hmin - lct + MIN(hmin, ect));
10840  assert(shift > 0);
10841  lst = hmin;
10842  duration = hmin - lct;
10843 
10844  SCIPdebugMessage("replace variable <%s>[%g,%g] by [%d,%d]\n",
10845  SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var), est + shift, lst);
10846 
10847  (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_aggr", SCIPvarGetName(var));
10848  SCIP_CALL( SCIPcreateVar(scip, &aggrvar, name, (SCIP_Real)(est+shift), (SCIP_Real)lst, 0.0, SCIPvarGetType(var),
10850  SCIP_CALL( SCIPaddVar(scip, var) );
10851  SCIP_CALL( SCIPaggregateVars(scip, var, aggrvar, 1.0, -1.0, (SCIP_Real)shift, &infeasible, &redundant, &aggregated) );
10852 
10853  assert(!infeasible);
10854  assert(!redundant);
10855  assert(aggregated);
10856 
10857  /* replace variable */
10858  consdata->durations[v] = duration;
10859  consdata->vars[v] = aggrvar;
10860 
10861  /* remove and add locks */
10862  SCIP_CALL( SCIPunlockVarCons(scip, var, cons, consdata->downlocks[v], consdata->uplocks[v]) );
10863  SCIP_CALL( SCIPlockVarCons(scip, var, cons, consdata->downlocks[v], consdata->uplocks[v]) );
10864 
10865  SCIP_CALL( SCIPreleaseVar(scip, &aggrvar) );
10866 
10867  (*naggrvars)++;
10868  }
10869  }
10870 
10871  return SCIP_OKAY;
10872 }
10873 #endif
10874 
10875 /** creare a disjunctive constraint which contains all jobs which cannot run in parallel */
10876 static
10878  SCIP* scip, /**< SCIP data structure */
10879  SCIP_CONS* cons, /**< cumulative constraint */
10880  int* naddconss /**< pointer to store the number of added constraints */
10881  )
10882 {
10883  SCIP_CONSDATA* consdata;
10884  SCIP_VAR** vars;
10885  int* durations;
10886  int* demands;
10887  int capacity;
10888  int halfcapacity;
10889  int mindemand;
10890  int nvars;
10891  int v;
10892 
10893  consdata = SCIPconsGetData(cons);
10894  assert(consdata != NULL);
10895 
10896  capacity = consdata->capacity;
10897 
10898  if( capacity == 1 )
10899  return SCIP_OKAY;
10900 
10901  SCIP_CALL( SCIPallocBufferArray(scip, &vars, consdata->nvars) );
10902  SCIP_CALL( SCIPallocBufferArray(scip, &durations, consdata->nvars) );
10903  SCIP_CALL( SCIPallocBufferArray(scip, &demands, consdata->nvars) );
10904 
10905  halfcapacity = capacity / 2;
10906  mindemand = consdata->capacity;
10907  nvars = 0;
10908 
10909  /* collect all jobs with demand larger than half of the capacity */
10910  for( v = 0; v < consdata->nvars; ++v )
10911  {
10912  if( consdata->demands[v] > halfcapacity )
10913  {
10914  vars[nvars] = consdata->vars[v];
10915  demands[nvars] = 1;
10916  durations[nvars] = consdata->durations[v];
10917  nvars++;
10918 
10919  mindemand = MIN(mindemand, consdata->demands[v]);
10920  }
10921  }
10922 
10923  if( nvars > 0 )
10924  {
10925  /* add all jobs which has a demand smaller than one half of the capacity but together with the smallest collected
10926  * job is still to large to be scheduled in parallel
10927  */
10928  for( v = 0; v < consdata->nvars; ++v )
10929  {
10930  if( consdata->demands[v] > halfcapacity )
10931  continue;
10932 
10933  if( mindemand + consdata->demands[v] > capacity )
10934  {
10935  demands[nvars] = 1;
10936  durations[nvars] = consdata->durations[v];
10937  vars[nvars] = consdata->vars[v];
10938  nvars++;
10939 
10940  /* adjust minimum demand of collected jobs */
10941  mindemand = MIN(mindemand, consdata->demands[v]);
10942  }
10943  }
10944 
10945  /* creates cumulative constraint and adds it to problem */
10946  SCIP_CALL( createConsCumulative(scip, SCIPconsGetName(cons), nvars, vars, durations, demands, 1, consdata->hmin, consdata->hmax,
10948  (*naddconss)++;
10949  }
10950 
10951  SCIPfreeBufferArray(scip, &demands);
10952  SCIPfreeBufferArray(scip, &durations);
10953  SCIPfreeBufferArray(scip, &vars);
10954 
10955  return SCIP_OKAY;
10956 }
10957 
10958 /** presolve given constraint */
10959 static
10961  SCIP* scip, /**< SCIP data structure */
10962  SCIP_CONS* cons, /**< cumulative constraint */
10963  SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
10964  int* nfixedvars, /**< pointer to store the number of fixed variables */
10965 #if 0
10966  int* naggrvars, /**< pointer to counter which is increased by the number of deduced variable aggregations */
10967 #endif
10968  int* nchgbds, /**< pointer to store the number of changed bounds */
10969  int* ndelconss, /**< pointer to store the number of deleted constraints */
10970  int* naddconss, /**< pointer to store the number of added constraints */
10971  int* nchgcoefs, /**< pointer to store the number of changed coefficients */
10972  int* nchgsides, /**< pointer to store the number of changed sides */
10973  SCIP_Bool* cutoff, /**< pointer to store if a cutoff was detected */
10974  SCIP_Bool* unbounded /**< pointer to store if the problem is unbounded */
10975  )
10976 {
10977  assert(!SCIPconsIsDeleted(cons));
10978 
10979  /* only perform dual reductions on model constraints */
10980  if( conshdlrdata->dualpresolve )
10981  {
10982  /* computes the effective horizon and checks if the constraint can be decomposed */
10983  SCIP_CALL( computeEffectiveHorizon(scip, cons, ndelconss, naddconss, nchgsides) );
10984 
10985  if( SCIPconsIsDeleted(cons) )
10986  return SCIP_OKAY;
10987 
10988  /* in case the cumulative constraint is independent of every else, solve the cumulative problem and apply the
10989  * fixings (dual reductions)
10990  */
10991  SCIP_CALL( solveIndependentCons(scip, cons, conshdlrdata->maxnodes, nchgbds, nfixedvars, ndelconss, cutoff, unbounded) );
10992 
10993  if( *cutoff || *unbounded )
10994  return SCIP_OKAY;
10995 
10996  SCIP_CALL( presolveConsEffectiveHorizon(scip, cons, nfixedvars, nchgcoefs, nchgsides, cutoff) );
10997 
10998  if( *cutoff || SCIPconsIsDeleted(cons) )
10999  return SCIP_OKAY;
11000  }
11001 
11002  /* remove jobs which have a demand larger than the capacity */
11003  SCIP_CALL( removeOversizedJobs(scip, cons, nchgbds, nchgcoefs, naddconss, cutoff) );
11004  assert((*cutoff) || checkDemands(scip, cons));
11005 
11006  if( *cutoff )
11007  return SCIP_OKAY;
11008 
11009  if( conshdlrdata->normalize )
11010  {
11011  /* divide demands by their greatest common divisor */
11012  SCIP_CALL( normalizeDemands(scip, cons, nchgcoefs, nchgsides) );
11013  }
11014 
11015  /* delete constraint with one job */
11016  SCIP_CALL( deleteTrivilCons(scip, cons, ndelconss, cutoff) );
11017 
11018  if( *cutoff || SCIPconsIsDeleted(cons) )
11019  return SCIP_OKAY;
11020 
11021  if( conshdlrdata->coeftightening )
11022  {
11023  /* try to tighten the capacity */
11024  SCIP_CALL( tightenCapacity(scip, cons, nchgcoefs, nchgsides) );
11025 
11026  /* try to tighten the coefficients */
11027  SCIP_CALL( tightenCoefs(scip, cons, nchgcoefs) );
11028  }
11029 
11030  assert(checkDemands(scip, cons) || *cutoff);
11031 
11032 #if 0
11033  SCIP_CALL( reformulateCons(scip, cons, naggrvars) );
11034 #endif
11035 
11036  return SCIP_OKAY;
11037 }
11038 
11039 /**@name TClique Graph callbacks
11040  *
11041  * @{
11042  */
11043 
11044 /** tclique graph data */
11045 struct TCLIQUE_Graph
11046 {
11047  SCIP_VAR** vars; /**< start time variables each of them is a node */
11048  SCIP_HASHMAP* varmap; /**< variable map, mapping variable to indux in vars array */
11049  SCIP_Bool** precedencematrix; /**< precedence adjacent matrix */
11050  SCIP_Bool** demandmatrix; /**< demand adjacent matrix */
11051  TCLIQUE_WEIGHT* weights; /**< weight of nodes */
11052  int* ninarcs; /**< number if in arcs for the precedence graph */
11053  int* noutarcs; /**< number if out arcs for the precedence graph */
11054  int* durations; /**< for each node the duration of the corresponding job */
11055  int nnodes; /**< number of nodes */
11056  int size; /**< size of the array */
11057 };
11058 
11059 /** gets number of nodes in the graph */
11060 static
11061 TCLIQUE_GETNNODES(tcliqueGetnnodesClique)
11062 {
11063  assert(tcliquegraph != NULL);
11064 
11065  return tcliquegraph->nnodes;
11066 }
11067 
11068 /** gets weight of nodes in the graph */
11069 static
11070 TCLIQUE_GETWEIGHTS(tcliqueGetweightsClique)
11071 {
11072  assert(tcliquegraph != NULL);
11073 
11074  return tcliquegraph->weights;
11075 }
11076 
11077 /** returns, whether the edge (node1, node2) is in the graph */
11078 static
11079 TCLIQUE_ISEDGE(tcliqueIsedgeClique)
11080 {
11081  assert(tcliquegraph != NULL);
11082  assert(0 <= node1 && node1 < tcliquegraph->nnodes);
11083  assert(0 <= node2 && node2 < tcliquegraph->nnodes);
11084 
11085  /* check if an arc exits in the precedence graph */
11086  if( tcliquegraph->precedencematrix[node1][node2] || tcliquegraph->precedencematrix[node2][node1] )
11087  return TRUE;
11088 
11089  /* check if an edge exits in the non-overlapping graph */
11090  if( tcliquegraph->demandmatrix[node1][node2] )
11091  return TRUE;
11092 
11093  return FALSE;
11094 }
11095 
11096 /** selects all nodes from a given set of nodes which are adjacent to a given node
11097  * and returns the number of selected nodes
11098  */
11099 static
11100 TCLIQUE_SELECTADJNODES(tcliqueSelectadjnodesClique)
11101 {
11102  int nadjnodes;
11103  int i;
11104 
11105  assert(tcliquegraph != NULL);
11106  assert(0 <= node && node < tcliquegraph->nnodes);
11107  assert(nnodes == 0 || nodes != NULL);
11108  assert(adjnodes != NULL);
11109 
11110  nadjnodes = 0;
11111 
11112  for( i = 0; i < nnodes; i++ )
11113  {
11114  /* check if the node is adjacent to the given node (nodes and adjacent nodes are ordered by node index) */
11115  assert(0 <= nodes[i] && nodes[i] < tcliquegraph->nnodes);
11116  assert(i == 0 || nodes[i-1] < nodes[i]);
11117 
11118  /* check if an edge exists */
11119  if( tcliqueIsedgeClique(tcliquegraph, node, nodes[i]) )
11120  {
11121  /* current node is adjacent to given node */
11122  adjnodes[nadjnodes] = nodes[i];
11123  nadjnodes++;
11124  }
11125  }
11126 
11127  return nadjnodes;
11128 }
11129 
11130 /** generates cuts using a clique found by algorithm for maximum weight clique
11131  * and decides whether to stop generating cliques with the algorithm for maximum weight clique
11132  */
11133 static
11134 TCLIQUE_NEWSOL(tcliqueNewsolClique)
11135 { /*lint --e{715}*/
11136  SCIPdebugMessage("####### max clique %d\n", cliqueweight);
11137 }
11138 
11139 /** print the tclique graph */
11140 #if 0
11141 static
11142 void tcliquePrint(
11143  SCIP* scip, /**< SCIP data structure */
11144  TCLIQUE_GRAPH* tcliquegraph /**< tclique graph */
11145  )
11146 {
11147  int nnodes;
11148  int i;
11149  int j;
11150 
11151  nnodes = tcliquegraph->nnodes;
11152 
11153  for( i = 0; i < nnodes; ++i )
11154  {
11155  for( j = 0; j < nnodes; ++j )
11156  {
11157  SCIPinfoMessage(scip, NULL, "(%d/%d) ", tcliquegraph->precedencematrix[i][j], tcliquegraph->demandmatrix[i][j]);
11158  }
11159  SCIPinfoMessage(scip, NULL, "\n");
11160  }
11161 }
11162 #endif
11163 
11164 /** @} */
11165 
11166 /** analyzes if the given variable lower bound condition implies a precedence condition w.r.t. given duration for the
11167  * job corresponding to variable bound variable (vlbvar)
11168  *
11169  * variable lower bound is given as: var >= vlbcoef * vlbvar + vlbconst
11170  */
11171 static
11173  SCIP* scip, /**< SCIP data structure */
11174  SCIP_VAR* vlbvar, /**< variable which bounds the variable from below */
11175  SCIP_Real vlbcoef, /**< variable bound coefficient */
11176  SCIP_Real vlbconst, /**< variable bound constant */
11177  int duration /**< duration of the variable bound variable */
11178  )
11179 {
11180  if( SCIPisEQ(scip, vlbcoef, 1.0) )
11181  {
11182  if( SCIPisGE(scip, vlbconst, (SCIP_Real) duration) )
11183  {
11184  /* if vlbcoef = 1 and vlbcoef >= duration -> precedence condition */
11185  return TRUE;
11186  }
11187  }
11188  else
11189  {
11190  SCIP_Real bound;
11191 
11192  bound = (duration - vlbcoef) / (vlbcoef - 1.0);
11193 
11194  if( SCIPisLT(scip, vlbcoef, 1.0) )
11195  {
11196  SCIP_Real ub;
11197 
11198  ub = SCIPvarGetUbLocal(vlbvar);
11199 
11200  /* if vlbcoef < 1 and ub(vlbvar) <= (duration - vlbconst)/(vlbcoef - 1) -> precedence condition */
11201  if( SCIPisLE(scip, ub, bound) )
11202  return TRUE;
11203  }
11204  else
11205  {
11206  SCIP_Real lb;
11207 
11208  assert(SCIPisGT(scip, vlbcoef, 1.0));
11209 
11210  lb = SCIPvarGetLbLocal(vlbvar);
11211 
11212  /* if vlbcoef > 1 and lb(vlbvar) >= (duration - vlbconst)/(vlbcoef - 1) -> precedence condition */
11213  if( SCIPisGE(scip, lb, bound) )
11214  return TRUE;
11215  }
11216  }
11217 
11218  return FALSE;
11219 }
11220 
11221 /** analyzes if the given variable upper bound condition implies a precedence condition w.r.t. given duration for the
11222  * job corresponding to variable which is bounded (var)
11223  *
11224  * variable upper bound is given as: var <= vubcoef * vubvar + vubconst
11225  */
11226 static
11228  SCIP* scip, /**< SCIP data structure */
11229  SCIP_VAR* var, /**< variable which is bound from above */
11230  SCIP_Real vubcoef, /**< variable bound coefficient */
11231  SCIP_Real vubconst, /**< variable bound constant */
11232  int duration /**< duration of the variable which is bounded from above */
11233  )
11234 {
11235  SCIP_Real vlbcoef;
11236  SCIP_Real vlbconst;
11237 
11238  /* convert the variable upper bound into an variable lower bound */
11239  vlbcoef = 1.0 / vubcoef;
11240  vlbconst = -vubconst / vubcoef;
11241 
11242  return impliesVlbPrecedenceCondition(scip, var, vlbcoef, vlbconst, duration);
11243 }
11244 
11245 /** get the corresponding index of the given variables; this in case of an active variable the problem index and for
11246  * others an index larger than the number if active variables
11247  */
11248 static
11250  SCIP* scip, /**< SCIP data structure */
11251  TCLIQUE_GRAPH* tcliquegraph, /**< incompatibility graph */
11252  SCIP_VAR* var, /**< variable for which we want the index */
11253  int* idx /**< pointer to store the index */
11254  )
11255 {
11256  (*idx) = SCIPvarGetProbindex(var);
11257 
11258  if( (*idx) == -1 )
11259  {
11260  if( SCIPhashmapExists(tcliquegraph->varmap, (void*)var) )
11261  {
11262  (*idx) = (int)(size_t) SCIPhashmapGetImage(tcliquegraph->varmap, (void*)var);
11263  }
11264  else
11265  {
11266  int pos;
11267  int v;
11268 
11269  /**@todo we might want to add the aggregation path to graph */
11270 
11271  /* check if we have to realloc memory */
11272  if( tcliquegraph->size == tcliquegraph->nnodes )
11273  {
11274  int size;
11275 
11276  size = SCIPcalcMemGrowSize(scip, tcliquegraph->nnodes+1);
11277  tcliquegraph->size = size;
11278 
11279  SCIP_CALL( SCIPreallocBufferArray(scip, &tcliquegraph->vars, size) );
11280  SCIP_CALL( SCIPreallocBufferArray(scip, &tcliquegraph->precedencematrix, size) );
11281  SCIP_CALL( SCIPreallocBufferArray(scip, &tcliquegraph->demandmatrix, size) );
11282  SCIP_CALL( SCIPreallocBufferArray(scip, &tcliquegraph->durations, size) );
11283  SCIP_CALL( SCIPreallocBufferArray(scip, &tcliquegraph->weights, size) );
11284 
11285  for( v = 0; v < tcliquegraph->nnodes; ++v )
11286  {
11287  SCIP_CALL( SCIPreallocBufferArray(scip, &tcliquegraph->precedencematrix[v], size) ); /*lint !e866*/
11288  SCIP_CALL( SCIPreallocBufferArray(scip, &tcliquegraph->demandmatrix[v], size) ); /*lint !e866*/
11289  }
11290  }
11291  assert(tcliquegraph->nnodes < tcliquegraph->size);
11292 
11293  pos = tcliquegraph->nnodes;
11294 
11295  tcliquegraph->durations[pos] = 0;
11296  tcliquegraph->weights[pos] = 0;
11297  tcliquegraph->vars[pos] = var;
11298 
11299  SCIP_CALL( SCIPallocBufferArray(scip, &tcliquegraph->precedencematrix[pos], tcliquegraph->size) ); /*lint !e866*/
11300  BMSclearMemoryArray(tcliquegraph->precedencematrix[pos], tcliquegraph->nnodes); /*lint !e866*/
11301 
11302  SCIP_CALL( SCIPallocBufferArray(scip, &tcliquegraph->demandmatrix[pos], tcliquegraph->size) ); /*lint !e866*/
11303  BMSclearMemoryArray(tcliquegraph->demandmatrix[pos], tcliquegraph->nnodes); /*lint !e866*/
11304 
11305  SCIP_CALL( SCIPhashmapInsert(tcliquegraph->varmap, (void*)var, (void*)(size_t)(pos)) );
11306 
11307  tcliquegraph->nnodes++;
11308 
11309  for( v = 0; v < tcliquegraph->nnodes; ++v )
11310  {
11311  tcliquegraph->precedencematrix[v][pos] = 0;
11312  tcliquegraph->demandmatrix[v][pos] = 0;
11313  }
11314 
11315  (*idx) = tcliquegraph->nnodes;
11316  }
11317  }
11318  else
11319  {
11320  assert(*idx == (int)(size_t)SCIPhashmapGetImage(tcliquegraph->varmap, (void*)var));
11321  }
11322 
11323  assert(SCIPhashmapExists(tcliquegraph->varmap, (void*)var));
11324 
11325  return SCIP_OKAY;
11326 }
11327 
11328 /** use the variables bounds of SCIP to projected variables bound graph into a precedence garph
11329  *
11330  * Let d be the (assumed) duration of variable x and consider a variable bound of the form b * x + c <= y. This
11331  * variable bounds implies a precedence condition x -> y (meaning job y starts after job x is finished) if:
11332  *
11333  * (i) b = 1 and c >= d
11334  * (ii) b > 1 and lb(x) >= (d - c)/(b - 1)
11335  * (iii) b < 1 and ub(x) >= (d - c)/(b - 1)
11336  *
11337  */
11338 static
11340  SCIP* scip, /**< SCIP data structure */
11341  TCLIQUE_GRAPH* tcliquegraph /**< incompatibility graph */
11342  )
11343 {
11344  SCIP_VAR** vars;
11345  int nvars;
11346  int v;
11347 
11348  vars = SCIPgetVars(scip);
11349  nvars = SCIPgetNVars(scip);
11350 
11351  /* try to project each arc of the variable bound graph to precedence condition */
11352  for( v = 0; v < nvars; ++v )
11353  {
11354  SCIP_VAR** vbdvars;
11355  SCIP_VAR* var;
11356  SCIP_Real* vbdcoefs;
11357  SCIP_Real* vbdconsts;
11358  int nvbdvars;
11359  int idx1;
11360  int b;
11361 
11362  var = vars[v];
11363  assert(var != NULL);
11364 
11365  SCIP_CALL( getNodeIdx(scip, tcliquegraph, var, &idx1) );
11366  assert(idx1 >= 0);
11367 
11368  if( tcliquegraph->durations[idx1] == 0 )
11369  continue;
11370 
11371  vbdvars = SCIPvarGetVlbVars(var);
11372  vbdcoefs = SCIPvarGetVlbCoefs(var);
11373  vbdconsts = SCIPvarGetVlbConstants(var);
11374  nvbdvars = SCIPvarGetNVlbs(var);
11375 
11376  for( b = 0; b < nvbdvars; ++b )
11377  {
11378  int idx2;
11379 
11380  SCIP_CALL( getNodeIdx(scip, tcliquegraph, vbdvars[b], &idx2) );
11381  assert(idx2 >= 0);
11382 
11383  if( tcliquegraph->durations[idx2] == 0 )
11384  continue;
11385 
11386  if( impliesVlbPrecedenceCondition(scip, vbdvars[b], vbdcoefs[b], vbdconsts[b], tcliquegraph->durations[idx2]) )
11387  tcliquegraph->precedencematrix[idx2][idx1] = TRUE;
11388  }
11389 
11390  vbdvars = SCIPvarGetVubVars(var);
11391  vbdcoefs = SCIPvarGetVubCoefs(var);
11392  vbdconsts = SCIPvarGetVubConstants(var);
11393  nvbdvars = SCIPvarGetNVubs(var);
11394 
11395  for( b = 0; b < nvbdvars; ++b )
11396  {
11397  int idx2;
11398 
11399  SCIP_CALL( getNodeIdx(scip, tcliquegraph, vbdvars[b], &idx2) );
11400  assert(idx2 >= 0);
11401 
11402  if( tcliquegraph->durations[idx2] == 0 )
11403  continue;
11404 
11405  if( impliesVubPrecedenceCondition(scip, var, vbdcoefs[b], vbdconsts[b], tcliquegraph->durations[idx1]) )
11406  tcliquegraph->precedencematrix[idx1][idx2] = TRUE;
11407  }
11408 
11409  for( b = v+1; b < nvars; ++b )
11410  {
11411  int idx2;
11412 
11413  SCIP_CALL( getNodeIdx(scip, tcliquegraph, vars[b], &idx2) );
11414  assert(idx2 >= 0);
11415 
11416  if( tcliquegraph->durations[idx2] == 0 )
11417  continue;
11418 
11419  /* check if the latest completion time of job1 is smaller than the earliest start time of job2 */
11420  if( SCIPisLE(scip, SCIPvarGetUbLocal(var) + tcliquegraph->durations[idx1], SCIPvarGetLbLocal(vars[b])) )
11421  tcliquegraph->precedencematrix[idx1][idx2] = TRUE;
11422 
11423  /* check if the latest completion time of job2 is smaller than the earliest start time of job1 */
11424  if( SCIPisLE(scip, SCIPvarGetUbLocal(vars[b]) + tcliquegraph->durations[idx2], SCIPvarGetLbLocal(var)) )
11425  tcliquegraph->precedencematrix[idx2][idx1] = TRUE;
11426  }
11427  }
11428 
11429  return SCIP_OKAY;
11430 }
11431 
11432 /** compute the transitive closer of the given graph and the number of in and out arcs */
11433 static
11435  SCIP_Bool** adjmatrix, /**< adjacent matrix */
11436  int* ninarcs, /**< array to store the number of in arcs */
11437  int* noutarcs, /**< array to store the number of out arcs */
11438  int nnodes /**< number if nodes */
11439  )
11440 {
11441  int i;
11442  int j;
11443  int k;
11444 
11445  for( i = 0; i < nnodes; ++i )
11446  {
11447  for( j = 0; j < nnodes; ++j )
11448  {
11449  if( adjmatrix[i][j] )
11450  {
11451  ninarcs[j]++;
11452  noutarcs[i]++;
11453 
11454  for( k = 0; k < nnodes; ++k )
11455  {
11456  if( adjmatrix[j][k] )
11457  adjmatrix[i][k] = TRUE;
11458  }
11459  }
11460  }
11461  }
11462 }
11463 
11464 /** constructs a non-overlapping graph w.r.t. given durations and available cumulative constraints */
11465 static
11467  SCIP* scip, /**< SCIP data structure */
11468  TCLIQUE_GRAPH* tcliquegraph, /**< incompatibility graph */
11469  SCIP_CONS** conss, /**< array of cumulative constraints */
11470  int nconss /**< number of cumulative constraints */
11471  )
11472 {
11473  int c;
11474 
11475  /* use the cumulative constraints to initialize the none overlapping graph */
11476  for( c = 0; c < nconss; ++c )
11477  {
11478  SCIP_CONSDATA* consdata;
11479  SCIP_VAR** vars;
11480  int* demands;
11481  int capacity;
11482  int nvars;
11483  int i;
11484 
11485  consdata = SCIPconsGetData(conss[c]);
11486  assert(consdata != NULL);
11487 
11488  vars = consdata->vars;
11489  demands = consdata->demands;
11490 
11491  nvars = consdata->nvars;
11492  capacity = consdata->capacity;
11493 
11494  SCIPdebugMessage("constraint <%s>\n", SCIPconsGetName(conss[c]));
11495 
11496  /* check pairwise if two jobs have a cumulative demand larger than the capacity */
11497  for( i = 0; i < nvars; ++i )
11498  {
11499  int idx1;
11500  int j;
11501 
11502  SCIP_CALL( getNodeIdx(scip, tcliquegraph, vars[i], &idx1) );
11503  assert(idx1 >= 0);
11504 
11505  if( tcliquegraph->durations[idx1] == 0 || tcliquegraph->durations[idx1] > consdata->durations[i] )
11506  continue;
11507 
11508  for( j = i+1; j < nvars; ++j )
11509  {
11510  assert(consdata->durations[j] > 0);
11511 
11512  if( demands[i] + demands[j] > capacity )
11513  {
11514  int idx2;
11515  int est1;
11516  int est2;
11517  int lct1;
11518  int lct2;
11519 
11520  /* check if the effective horizon is large enough */
11521  est1 = convertBoundToInt(scip, SCIPvarGetLbLocal(vars[i]));
11522  est2 = convertBoundToInt(scip, SCIPvarGetLbLocal(vars[j]));
11523 
11524  /* at least one of the jobs needs to start at hmin or later */
11525  if( est1 < consdata->hmin && est2 < consdata->hmin )
11526  continue;
11527 
11528  lct1 = convertBoundToInt(scip, SCIPvarGetUbLocal(vars[i])) + consdata->durations[i];
11529  lct2 = convertBoundToInt(scip, SCIPvarGetUbLocal(vars[j])) + consdata->durations[j];
11530 
11531  /* at least one of the jobs needs to finish not later then hmin */
11532  if( lct1 > consdata->hmax && lct2 > consdata->hmax )
11533  continue;
11534 
11535  SCIP_CALL( getNodeIdx(scip, tcliquegraph, vars[j], &idx2) );
11536  assert(idx2 >= 0);
11537  assert(idx1 != idx2);
11538 
11539  if( tcliquegraph->durations[idx2] == 0 || tcliquegraph->durations[idx2] > consdata->durations[j] )
11540  continue;
11541 
11542  SCIPdebugMessage(" *** variable <%s> and variable <%s>\n", SCIPvarGetName(vars[i]), SCIPvarGetName(vars[j]));
11543 
11544  assert(tcliquegraph->durations[idx1] > 0);
11545  assert(tcliquegraph->durations[idx2] > 0);
11546 
11547  tcliquegraph->demandmatrix[idx1][idx2] = TRUE;
11548  tcliquegraph->demandmatrix[idx2][idx1] = TRUE;
11549 
11550  }
11551  }
11552  }
11553  }
11554 
11555  return SCIP_OKAY;
11556 }
11557 
11558 /** constructs a conflict set graph (undirected) which contains for each job a node and edge if the corresponding pair
11559  * of jobs cannot run in parallel
11560  */
11561 static
11563  SCIP* scip, /**< SCIP data structure */
11564  TCLIQUE_GRAPH* tcliquegraph, /**< incompatibility graph */
11565  SCIP_CONS** conss, /**< array of cumulative constraints */
11566  int nconss /**< number of cumulative constraints */
11567  )
11568 {
11569  assert(scip != NULL);
11570  assert(tcliquegraph != NULL);
11571 
11572  /* use the variables bounds of SCIP to project the variables bound graph inot a precedence graph */
11573  SCIP_CALL( projectVbd(scip, tcliquegraph) );
11574 
11575  /* compute the transitive closure of the precedence graph and the number of in and out arcs */
11576  transitiveClosure(tcliquegraph->precedencematrix, tcliquegraph->ninarcs, tcliquegraph->noutarcs, tcliquegraph->nnodes);
11577 
11578  /* constraints non-overlapping graph */
11579  SCIP_CALL( constraintNonOverlappingGraph(scip, tcliquegraph, conss, nconss) );
11580 
11581  return SCIP_OKAY;
11582 }
11583 
11584 /** create cumulative constraint from conflict set */
11585 static
11587  SCIP* scip, /**< SCIP data structure */
11588  const char* name, /**< constraint name */
11589  TCLIQUE_GRAPH* tcliquegraph, /**< conflict set graph */
11590  int* cliquenodes, /**< array storing the indecies of the nodes belonging to the clique */
11591  int ncliquenodes /**< number of nodes in the clique */
11592  )
11593 {
11594  SCIP_CONS* cons;
11595  SCIP_VAR** vars;
11596  int* durations;
11597  int* demands;
11598  int v;
11599 
11600  SCIP_CALL( SCIPallocBufferArray(scip, &vars, ncliquenodes) );
11601  SCIP_CALL( SCIPallocBufferArray(scip, &durations, ncliquenodes) );
11602  SCIP_CALL( SCIPallocBufferArray(scip, &demands, ncliquenodes) );
11603 
11604  SCIPsortInt(cliquenodes, ncliquenodes);
11605 
11606  /* collect variables, durations, and demands */
11607  for( v = 0; v < ncliquenodes; ++v )
11608  {
11609  durations[v] = tcliquegraph->durations[cliquenodes[v]];
11610  assert(durations[v] > 0);
11611  demands[v] = 1;
11612  vars[v] = tcliquegraph->vars[cliquenodes[v]];
11613  }
11614 
11615  /* create (unary) cumulative constraint */
11616  SCIP_CALL( SCIPcreateConsCumulative(scip, &cons, name, ncliquenodes, vars, durations, demands, 1,
11618 
11619  SCIP_CALL( SCIPaddCons(scip, cons) );
11620  SCIP_CALL( SCIPreleaseCons(scip, &cons) );
11621 
11622  /* free buffers */
11623  SCIPfreeBufferArray(scip, &demands);
11624  SCIPfreeBufferArray(scip, &durations);
11625  SCIPfreeBufferArray(scip, &vars);
11626 
11627  return SCIP_OKAY;
11628 }
11629 
11630 /** search for cumulative constrainst */
11631 static
11633  SCIP* scip, /**< SCIP data structure */
11634  TCLIQUE_GRAPH* tcliquegraph, /**< conflict set graph */
11635  int* naddconss /**< pointer to store the number of added constraints */
11636  )
11637 {
11638  TCLIQUE_STATUS tcliquestatus;
11639  SCIP_Bool* precedencerow;
11640  SCIP_Bool* precedencecol;
11641  SCIP_Bool* demandrow;
11642  SCIP_Bool* demandcol;
11643  SCIP_HASHTABLE* covered;
11644  int* cliquenodes;
11645  int ncliquenodes;
11646  int cliqueweight;
11647  int ntreenodes;
11648  int nnodes;
11649  int nconss;
11650  int v;
11651 
11652  nnodes = tcliquegraph->nnodes;
11653  nconss = 0;
11654 
11655  /* initialize the weight of each job with its duration */
11656  for( v = 0; v < nnodes; ++v )
11657  {
11658  tcliquegraph->weights[v] = tcliquegraph->durations[v];
11659  }
11660 
11661  SCIP_CALL( SCIPallocBufferArray(scip, &cliquenodes, nnodes) );
11662  SCIP_CALL( SCIPallocBufferArray(scip, &precedencerow, nnodes) );
11663  SCIP_CALL( SCIPallocBufferArray(scip, &precedencecol, nnodes) );
11664  SCIP_CALL( SCIPallocBufferArray(scip, &demandrow, nnodes) );
11665  SCIP_CALL( SCIPallocBufferArray(scip, &demandcol, nnodes) );
11666 
11667  /* create a hash table to store all start time variables which are already covered by at least one clique */
11669  SCIPvarGetHashkey, SCIPvarIsHashkeyEq, SCIPvarGetHashkeyVal, NULL) );
11670 
11671  /* for each variables/job we are ... */
11672  for( v = 0; v < nnodes && !SCIPisStopped(scip); ++v )
11673  {
11674  char name[SCIP_MAXSTRLEN];
11675  int c;
11676 
11677  /* jobs with zero durations are skipped */
11678  if( tcliquegraph->durations[v] == 0 )
11679  continue;
11680 
11681  /* check if the start time variable is already covered by at least one clique */
11682  if( SCIPhashtableExists(covered, tcliquegraph->vars[v]) )
11683  continue;
11684 
11685  SCIPdebugMessage("********** variable <%s>\n", SCIPvarGetName(tcliquegraph->vars[v]));
11686 
11687  /* temporarily remove the connection via the precedence graph */
11688  for( c = 0; c < nnodes; ++c )
11689  {
11690  precedencerow[c] = tcliquegraph->precedencematrix[v][c];
11691  precedencecol[c] = tcliquegraph->precedencematrix[c][v];
11692 
11693  demandrow[c] = tcliquegraph->demandmatrix[v][c];
11694  demandcol[c] = tcliquegraph->demandmatrix[c][v];
11695 
11696 #if 0
11697  if( precedencerow[c] || precedencecol[c] )
11698  {
11699  tcliquegraph->demandmatrix[v][c] = FALSE;
11700  tcliquegraph->demandmatrix[c][v] = FALSE;
11701  }
11702 #endif
11703 
11704  tcliquegraph->precedencematrix[c][v] = FALSE;
11705  tcliquegraph->precedencematrix[v][c] = FALSE;
11706  }
11707 
11708  /* find (heuristically) maximum cliques which includes node v */
11709  tcliqueMaxClique(tcliqueGetnnodesClique, tcliqueGetweightsClique, tcliqueIsedgeClique, tcliqueSelectadjnodesClique,
11710  tcliquegraph, tcliqueNewsolClique, NULL,
11711  cliquenodes, &ncliquenodes, &cliqueweight, 1, 1,
11712  10000, 1000, 1000, v, &ntreenodes, &tcliquestatus);
11713 
11714  SCIPdebugMessage("tree nodes %d clique size %d (weight %d, status %d)\n", ntreenodes, ncliquenodes, cliqueweight, tcliquestatus);
11715 
11716  if( ncliquenodes == 1 )
11717  continue;
11718 
11719  /* construct constraint name */
11720  (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "nooverlap_%d_%d", SCIPgetNRuns(scip), nconss);
11721 
11722  SCIP_CALL( createCumulativeCons(scip, name, tcliquegraph, cliquenodes, ncliquenodes) );
11723  nconss++;
11724 
11725  /* all start time variable to covered hash table */
11726  for( c = 0; c < ncliquenodes; ++c )
11727  {
11728  SCIP_CALL( SCIPhashtableInsert(covered, tcliquegraph->vars[cliquenodes[c]]) );
11729  }
11730 
11731  /* copy the precedence relations back */
11732  for( c = 0; c < nnodes; ++c )
11733  {
11734  tcliquegraph->precedencematrix[v][c] = precedencerow[c];
11735  tcliquegraph->precedencematrix[c][v] = precedencecol[c];
11736 
11737  tcliquegraph->demandmatrix[v][c] = demandrow[c];
11738  tcliquegraph->demandmatrix[c][v] = demandcol[c];
11739  }
11740  }
11741 
11742  SCIPhashtableFree(&covered);
11743 
11744  SCIPfreeBufferArray(scip, &demandcol);
11745  SCIPfreeBufferArray(scip, &demandrow);
11746  SCIPfreeBufferArray(scip, &precedencecol);
11747  SCIPfreeBufferArray(scip, &precedencerow);
11748  SCIPfreeBufferArray(scip, &cliquenodes);
11749 
11750  (*naddconss) += nconss;
11751 
11752  /* for the statistic we count the number added disjunctive constraints */
11753  SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->naddeddisjunctives += nconss );
11754 
11755  return SCIP_OKAY;
11756 }
11757 
11758 /** create precedence constraint (as variable bound constraint */
11759 static
11761  SCIP* scip, /**< SCIP data structure */
11762  const char* name, /**< constraint name */
11763  SCIP_VAR* var, /**< variable x that has variable bound */
11764  SCIP_VAR* vbdvar, /**< binary, integer or implicit integer bounding variable y */
11765  int distance /**< minimum distance between the start time of the job corresponding to var and the job corresponding to vbdvar */
11766  )
11767 {
11768  SCIP_CONS* cons;
11769 
11770  /* create variable bound constraint */
11771  SCIP_CALL( SCIPcreateConsVarbound(scip, &cons, name, var, vbdvar, -1.0, -SCIPinfinity(scip), -(SCIP_Real)distance,
11773 
11774  SCIPdebugPrintCons(scip, cons, NULL);
11775 
11776  /* add constraint to problem and release it */
11777  SCIP_CALL( SCIPaddCons(scip, cons) );
11778  SCIP_CALL( SCIPreleaseCons(scip, &cons) );
11779 
11780  return SCIP_OKAY;
11781 }
11782 
11783 /** compute a minimum distance between the start times of the two given jobs and post it as variable bound constraint */
11784 static
11786  SCIP* scip, /**< SCIP data structure */
11787  TCLIQUE_GRAPH* tcliquegraph, /**< conflict set graph */
11788  int source, /**< index of the source node */
11789  int sink, /**< index of the sink node */
11790  int* naddconss /**< pointer to store the number of added constraints */
11791  )
11792 {
11793  TCLIQUE_WEIGHT cliqueweight;
11794  TCLIQUE_STATUS tcliquestatus;
11795  SCIP_VAR** vars;
11796  int* cliquenodes;
11797  int nnodes;
11798  int lct;
11799  int est;
11800  int i;
11801 
11802  int ntreenodes;
11803  int ncliquenodes;
11804 
11805  /* check if source and sink are connencted */
11806  if( !tcliquegraph->precedencematrix[source][sink] )
11807  return SCIP_OKAY;
11808 
11809  nnodes = tcliquegraph->nnodes;
11810  vars = tcliquegraph->vars;
11811 
11812  /* reset the weights to zero */
11813  BMSclearMemoryArray(tcliquegraph->weights, nnodes);
11814 
11815  /* get latest completion time (lct) of the source and the earliest start time (est) of sink */
11816  lct = convertBoundToInt(scip, SCIPvarGetUbLocal(vars[source])) + tcliquegraph->durations[source];
11817  est = convertBoundToInt(scip, SCIPvarGetLbLocal(vars[sink]));
11818 
11819  /* weight all jobs which run for sure between source and sink with their duration */
11820  for( i = 0; i < nnodes; ++i )
11821  {
11822  SCIP_VAR* var;
11823  int duration;
11824 
11825  var = vars[i];
11826  assert(var != NULL);
11827 
11828  duration = tcliquegraph->durations[i];
11829 
11830  if( i == source || i == sink )
11831  {
11832  /* source and sink are not weighted */
11833  tcliquegraph->weights[i] = 0;
11834  }
11835  else if( tcliquegraph->precedencematrix[source][i] && tcliquegraph->precedencematrix[i][sink] )
11836  {
11837  /* job i runs after source and before sink */
11838  tcliquegraph->weights[i] = duration;
11839  }
11840  else if( lct <= convertBoundToInt(scip, SCIPvarGetLbLocal(var))
11841  && est >= convertBoundToInt(scip, SCIPvarGetUbLocal(var)) + duration )
11842  {
11843  /* job i run in between due the bounds of the start time variables */
11844  tcliquegraph->weights[i] = duration;
11845  }
11846  else
11847  tcliquegraph->weights[i] = 0;
11848  }
11849 
11850  SCIP_CALL( SCIPallocBufferArray(scip, &cliquenodes, nnodes) );
11851 
11852  /* find (heuristically) maximum cliques */
11853  tcliqueMaxClique(tcliqueGetnnodesClique, tcliqueGetweightsClique, tcliqueIsedgeClique, tcliqueSelectadjnodesClique,
11854  tcliquegraph, tcliqueNewsolClique, NULL,
11855  cliquenodes, &ncliquenodes, &cliqueweight, 1, 1,
11856  10000, 1000, 1000, -1, &ntreenodes, &tcliquestatus);
11857 
11858  if( ncliquenodes > 1 )
11859  {
11860  char name[SCIP_MAXSTRLEN];
11861  int distance;
11862 
11863  /* construct constraint name */
11864  (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "varbound_%d_%d", SCIPgetNRuns(scip), *naddconss);
11865 
11866  /* the minimum distance between the start times of source job and the sink job is the clique weight plus the
11867  * duration of the source job
11868  */
11869  distance = cliqueweight + tcliquegraph->durations[source];
11870 
11871  SCIP_CALL( createPrecedenceCons(scip, name, vars[source], vars[sink], distance) );
11872  (*naddconss)++;
11873  }
11874 
11875  SCIPfreeBufferArray(scip, &cliquenodes);
11876 
11877  return SCIP_OKAY;
11878 }
11879 
11880 /** search for precedence constraints
11881  *
11882  * for each arc of the transitive closure of the precedence graph, we are computing a minimum distance between the
11883  * corresponding two jobs
11884  */
11885 static
11887  SCIP* scip, /**< SCIP data structure */
11888  TCLIQUE_GRAPH* tcliquegraph, /**< conflict set graph */
11889  int* naddconss /**< pointer to store the number of added constraints */
11890  )
11891 {
11892  int* sources;
11893  int* sinks;
11894  int nconss;
11895  int nnodes;
11896  int nsources;
11897  int nsinks;
11898  int i;
11899 
11900  nnodes = tcliquegraph->nnodes;
11901  nconss = 0;
11902 
11903  nsources = 0;
11904  nsinks = 0;
11905 
11906  SCIP_CALL( SCIPallocBufferArray(scip, &sources, nnodes) );
11907  SCIP_CALL( SCIPallocBufferArray(scip, &sinks, nnodes) );
11908 
11909  /* first collect all sources and sinks */
11910  for( i = 0; i < nnodes; ++i )
11911  {
11912  if( tcliquegraph->ninarcs[i] == 0 )
11913  {
11914  sources[nsources] = i;
11915  nsources++;
11916  }
11917 
11918  if( tcliquegraph->ninarcs[i] == 0 )
11919  {
11920  sinks[nsinks] = i;
11921  nsinks++;
11922  }
11923  }
11924 
11925  /* compute for each node a minimum distance to each sources and each sink */
11926  for( i = 0; i < nnodes && !SCIPisStopped(scip); ++i )
11927  {
11928  int j;
11929 
11930  for( j = 0; j < nsources && !SCIPisStopped(scip); ++j )
11931  {
11932  SCIP_CALL( computeMinDistance(scip, tcliquegraph, sources[j], i, &nconss) );
11933  }
11934 
11935  for( j = 0; j < nsinks && !SCIPisStopped(scip); ++j )
11936  {
11937  SCIP_CALL( computeMinDistance(scip, tcliquegraph, i, sinks[j], &nconss) );
11938  }
11939  }
11940 
11941  (*naddconss) += nconss;
11942 
11943  /* for the statistic we count the number added variable constraints */
11944  SCIPstatistic( SCIPconshdlrGetData(SCIPfindConshdlr(scip, CONSHDLR_NAME))->naddedvarbounds += nconss );
11945 
11946  SCIPfreeBufferArray(scip, &sinks);
11947  SCIPfreeBufferArray(scip, &sources);
11948 
11949  return SCIP_OKAY;
11950 }
11951 
11952 /** initialize the assumed durations for each variable */
11953 static
11955  SCIP* scip, /**< SCIP data structure */
11956  TCLIQUE_GRAPH* tcliquegraph, /**< the incompatibility graph */
11957  SCIP_CONS** conss, /**< cumulative constraints */
11958  int nconss /**< number of cumulative constraints */
11959  )
11960 {
11961  int c;
11962 
11963  /* use the cumulative structure to define the duration we are using for each job */
11964  for( c = 0; c < nconss; ++c )
11965  {
11966  SCIP_CONSDATA* consdata;
11967  SCIP_VAR** vars;
11968  int nvars;
11969  int v;
11970 
11971  consdata = SCIPconsGetData(conss[c]);
11972  assert(consdata != NULL);
11973 
11974  vars = consdata->vars;
11975  nvars = consdata->nvars;
11976 
11977  for( v = 0; v < nvars; ++v )
11978  {
11979  int idx;
11980 
11981  SCIP_CALL( getNodeIdx(scip, tcliquegraph, vars[v], &idx) );
11982  assert(idx >= 0);
11983 
11984  /**@todo For the test sets, which we are considere, the durations are independent of the cumulative
11985  * constaints. Meaning each job has a fixed duration which is the same for all cumulative constraints. In
11986  * general this is not the case. Therefore, the question would be which duration should be used?
11987  */
11988  tcliquegraph->durations[idx] = MAX(tcliquegraph->durations[idx], consdata->durations[v]);
11989  assert(tcliquegraph->durations[idx] > 0);
11990  }
11991  }
11992 
11993  return SCIP_OKAY;
11994 }
11995 
11996 /** create tclique graph */
11997 static
11999  SCIP* scip, /**< SCIP data structure */
12000  TCLIQUE_GRAPH** tcliquegraph /**< reference to the incompatibility graph */
12001  )
12002 {
12003  SCIP_VAR** vars;
12004  SCIP_HASHMAP* varmap;
12005  SCIP_Bool** precedencematrix;
12006  SCIP_Bool** demandmatrix;
12007  int* ninarcs;
12008  int* noutarcs;
12009  int* durations;
12010  int* weights;
12011  int nvars;
12012  int v;
12013 
12014  vars = SCIPgetVars(scip);
12015  nvars = SCIPgetNVars(scip);
12016 
12017  /* allocate memory for the tclique graph data structure */
12018  SCIP_CALL( SCIPallocBuffer(scip, tcliquegraph) );
12019 
12020  /* create the variable mapping hash map */
12021  SCIP_CALL( SCIPhashmapCreate(&varmap, SCIPblkmem(scip), SCIPcalcHashtableSize(5 * nvars)) );
12022 
12023  /* each active variables get a node in the graph */
12024  SCIP_CALL( SCIPduplicateBufferArray(scip, &(*tcliquegraph)->vars, vars, nvars) );
12025 
12026  /* allocate memory for the projected variables bound graph and the none overlapping graph */
12027  SCIP_CALL( SCIPallocBufferArray(scip, &precedencematrix, nvars) );
12028  SCIP_CALL( SCIPallocBufferArray(scip, &demandmatrix, nvars) );
12029 
12030  /* array to buffer the weights of the nodes for the maximum weighted clique computation */
12031  SCIP_CALL( SCIPallocBufferArray(scip, &weights, nvars) );
12032  BMSclearMemoryArray(weights, nvars);
12033 
12034  /* array to store the number of in arc of the precedence graph */
12035  SCIP_CALL( SCIPallocBufferArray(scip, &ninarcs, nvars) );
12036  BMSclearMemoryArray(ninarcs, nvars);
12037 
12038  /* array to store the number of out arc of the precedence graph */
12039  SCIP_CALL( SCIPallocBufferArray(scip, &noutarcs, nvars) );
12040  BMSclearMemoryArray(noutarcs, nvars);
12041 
12042  /* array to store the used duration for each node */
12043  SCIP_CALL( SCIPallocBufferArray(scip, &durations, nvars) );
12044  BMSclearMemoryArray(durations, nvars);
12045 
12046  for( v = 0; v < nvars; ++v )
12047  {
12048  SCIP_VAR* var;
12049 
12050  var = vars[v];
12051  assert(var != NULL);
12052 
12053  SCIP_CALL( SCIPallocBufferArray(scip, &precedencematrix[v], nvars) ); /*lint !e866*/
12054  BMSclearMemoryArray(precedencematrix[v], nvars); /*lint !e866*/
12055 
12056  SCIP_CALL( SCIPallocBufferArray(scip, &demandmatrix[v], nvars) ); /*lint !e866*/
12057  BMSclearMemoryArray(demandmatrix[v], nvars); /*lint !e866*/
12058 
12059  /* insert all active variables into the garph */
12060  assert(SCIPvarGetProbindex(var) == v);
12061  SCIP_CALL( SCIPhashmapInsert(varmap, (void*)var, (void*)(size_t)v) );
12062  }
12063 
12064  (*tcliquegraph)->nnodes = nvars;
12065  (*tcliquegraph)->varmap = varmap;
12066  (*tcliquegraph)->precedencematrix = precedencematrix;
12067  (*tcliquegraph)->demandmatrix = demandmatrix;
12068  (*tcliquegraph)->weights = weights;
12069  (*tcliquegraph)->ninarcs = ninarcs;
12070  (*tcliquegraph)->noutarcs = noutarcs;
12071  (*tcliquegraph)->durations = durations;
12072  (*tcliquegraph)->size = nvars;
12073 
12074  return SCIP_OKAY;
12075 }
12076 
12077 /** frees the tclique graph */
12078 static
12080  SCIP* scip, /**< SCIP data structure */
12081  TCLIQUE_GRAPH** tcliquegraph /**< reference to the incompatibility graph */
12082  )
12083 {
12084  int v;
12085 
12086  for( v = (*tcliquegraph)->nnodes-1; v >= 0; --v )
12087  {
12088  SCIPfreeBufferArray(scip, &(*tcliquegraph)->demandmatrix[v]);
12089  SCIPfreeBufferArray(scip, &(*tcliquegraph)->precedencematrix[v]);
12090  }
12091 
12092  SCIPfreeBufferArray(scip, &(*tcliquegraph)->durations);
12093  SCIPfreeBufferArray(scip, &(*tcliquegraph)->ninarcs);
12094  SCIPfreeBufferArray(scip, &(*tcliquegraph)->noutarcs);
12095  SCIPfreeBufferArray(scip, &(*tcliquegraph)->weights);
12096  SCIPfreeBufferArray(scip, &(*tcliquegraph)->demandmatrix);
12097  SCIPfreeBufferArray(scip, &(*tcliquegraph)->precedencematrix);
12098  SCIPfreeBufferArray(scip, &(*tcliquegraph)->vars);
12099  SCIPhashmapFree(&(*tcliquegraph)->varmap);
12100 
12101  SCIPfreeBuffer(scip, tcliquegraph);
12102 }
12103 
12104 /** construct an incompatibility graph and search for precedence constraints (variables bounds) and unary cumulative
12105  * constrains (disjunctive constraint)
12106  */
12107 static
12109  SCIP* scip, /**< SCIP data structure */
12110  SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */
12111  SCIP_CONS** conss, /**< array of cumulative constraints */
12112  int nconss, /**< number of cumulative constraints */
12113  int* naddconss /**< pointer to store the number of added constraints */
12114  )
12115 {
12116  TCLIQUE_GRAPH* tcliquegraph;
12117 
12118  /* create tclique graph */
12119  SCIP_CALL( createTcliqueGraph(scip, &tcliquegraph) );
12120 
12121  /* define for each job a duration */
12122  SCIP_CALL( initializeDurations(scip, tcliquegraph, conss, nconss) );
12123 
12124  /* constuct incompatibility graph */
12125  SCIP_CALL( constructIncompatibilityGraph(scip, tcliquegraph, conss, nconss) );
12126 
12127  /* search for new precedence constraints */
12128  if( conshdlrdata->detectvarbounds )
12129  {
12130  SCIP_CALL( findPrecedenceConss(scip, tcliquegraph, naddconss) );
12131  }
12132 
12133  /* search for new cumulative constraints */
12134  if( conshdlrdata->detectdisjunctive )
12135  {
12136  SCIP_CALL( findCumulativeConss(scip, tcliquegraph, naddconss) );
12137  }
12138 
12139  /* free tclique graph data structure */
12140  freeTcliqueGraph(scip, &tcliquegraph);
12141 
12142  return SCIP_OKAY;
12143 }
12144 
12145 /** compute the constraint signature which is used to detect constraints which contain potentially the same set of variables */
12146 static
12148  SCIP_CONSDATA* consdata /**< cumulative constraint data */
12149  )
12150 {
12151  SCIP_VAR** vars;
12152  int nvars;
12153  int v;
12154 
12155  if( consdata->validsignature )
12156  return;
12157 
12158  vars = consdata->vars;
12159  nvars = consdata->nvars;
12160 
12161  for( v = 0; v < nvars; ++v )
12162  {
12163  consdata->signature |= ((unsigned int)1 << ((unsigned int)SCIPvarGetIndex(vars[v]) % (sizeof(unsigned int) * 8)));
12164  }
12165 
12166  consdata->validsignature = TRUE;
12167 }
12168 
12169 /** index comparison method of linear constraints: compares two indices of the variable set in the linear constraint */
12170 static
12171 SCIP_DECL_SORTINDCOMP(consdataCompVar)
12172 { /*lint --e{715}*/
12173  SCIP_CONSDATA* consdata = (SCIP_CONSDATA*)dataptr;
12174 
12175  assert(consdata != NULL);
12176  assert(0 <= ind1 && ind1 < consdata->nvars);
12177  assert(0 <= ind2 && ind2 < consdata->nvars);
12178 
12179  return SCIPvarCompare(consdata->vars[ind1], consdata->vars[ind2]);
12180 }
12181 
12182 /** run a pairwise comparison */
12183 static
12185  SCIP* scip, /**< SCIP data structure */
12186  SCIP_CONS** conss, /**< array of cumulative constraints */
12187  int nconss, /**< number of cumulative constraints */
12188  int* ndelconss /**< pointer to store the number of deletedconstraints */
12189  )
12190 {
12191  int i;
12192  int j;
12193 
12194  for( i = 0; i < nconss; ++i )
12195  {
12196  SCIP_CONSDATA* consdata0;
12197  SCIP_CONS* cons0;
12198 
12199  cons0 = conss[i];
12200  assert(cons0 != NULL);
12201 
12202  consdata0 = SCIPconsGetData(cons0);
12203  assert(consdata0 != NULL);
12204 
12205  consdataCalcSignature(consdata0);
12206  assert(consdata0->validsignature);
12207 
12208  for( j = i+1; j < nconss; ++j )
12209  {
12210  SCIP_CONSDATA* consdata1;
12211  SCIP_CONS* cons1;
12212 
12213  cons1 = conss[j];
12214  assert(cons1 != NULL);
12215 
12216  consdata1 = SCIPconsGetData(cons1);
12217  assert(consdata1 != NULL);
12218 
12219  if( consdata0->capacity != consdata1->capacity )
12220  continue;
12221 
12222  consdataCalcSignature(consdata1);
12223  assert(consdata1->validsignature);
12224 
12225  if( (consdata1->signature & (~consdata0->signature)) == 0 )
12226  {
12227  SCIPswapPointers((void**)&consdata0, (void**)&consdata1);
12228  SCIPswapPointers((void**)&cons0, (void**)&cons1);
12229  assert((consdata0->signature & (~consdata1->signature)) == 0);
12230  }
12231 
12232  if( (consdata0->signature & (~consdata1->signature)) == 0 )
12233  {
12234  int* perm0;
12235  int* perm1;
12236  int v0;
12237  int v1;
12238 
12239  if( consdata0->nvars > consdata1->nvars )
12240  continue;
12241 
12242  if( consdata0->hmin < consdata1->hmin )
12243  continue;
12244 
12245  if( consdata0->hmax > consdata1->hmax )
12246  continue;
12247 
12248  SCIP_CALL( SCIPallocBufferArray(scip, &perm0, consdata0->nvars) );
12249  SCIP_CALL( SCIPallocBufferArray(scip, &perm1, consdata1->nvars) );
12250 
12251  /* call sorting method */
12252  SCIPsort(perm0, consdataCompVar, (void*)consdata0, consdata0->nvars);
12253  SCIPsort(perm1, consdataCompVar, (void*)consdata1, consdata1->nvars);
12254 
12255  for( v0 = 0, v1 = 0; v0 < consdata0->nvars && v1 < consdata1->nvars; )
12256  {
12257  SCIP_VAR* var0;
12258  SCIP_VAR* var1;
12259  int idx0;
12260  int idx1;
12261  int comp;
12262 
12263  idx0 = perm0[v0];
12264  idx1 = perm1[v1];
12265 
12266  var0 = consdata0->vars[idx0];
12267 
12268  var1 = consdata1->vars[idx1];
12269 
12270  comp = SCIPvarCompare(var0, var1);
12271 
12272  if( comp == 0 )
12273  {
12274  int duration0;
12275  int duration1;
12276  int demand0;
12277  int demand1;
12278 
12279  demand0 = consdata0->demands[idx0];
12280  duration0 = consdata0->durations[idx0];
12281 
12282  demand1 = consdata1->demands[idx1];
12283  duration1 = consdata1->durations[idx1];
12284 
12285  if( demand0 != demand1 )
12286  break;
12287 
12288  if( duration0 != duration1 )
12289  break;
12290 
12291  v0++;
12292  v1++;
12293  }
12294  else if( comp > 0 )
12295  v1++;
12296  else
12297  break;
12298  }
12299 
12300  if( v0 == consdata0->nvars )
12301  {
12302  if( SCIPconsIsChecked(cons0) && !SCIPconsIsChecked(cons1) )
12303  {
12304  initializeLocks(consdata1, TRUE);
12305  }
12306 
12307  SCIP_CALL( SCIPupdateConsFlags(scip, cons1, cons0) );
12308 
12309  SCIP_CALL( SCIPdelCons(scip, cons0) );
12310  (*ndelconss)++;
12311  }
12312 
12313  SCIPfreeBufferArray(scip, &perm1);
12314  SCIPfreeBufferArray(scip, &perm0);
12315  }
12316  }
12317  }
12318 
12319  return SCIP_OKAY;
12320 }
12321 
12322 /** strength the variable bounds using the cumulative condition */
12323 static
12325  SCIP* scip, /**< SCIP data structure */
12326  SCIP_CONS* cons, /**< constraint to propagate */
12327  int* nchgbds, /**< pointer to store the number of changed bounds */
12328  int* naddconss /**< pointer to store the number of added constraints */
12329  )
12330 {
12331  SCIP_CONSDATA* consdata;
12332  SCIP_VAR** vars;
12333  int* durations;
12334  int* demands;
12335  int capacity;
12336  int nvars;
12337  int nconss;
12338  int i;
12339 
12340  consdata = SCIPconsGetData(cons);
12341  assert(consdata != NULL);
12342 
12343  /* check if the variable bounds got already strengthen by the cumulative constraint */
12344  if( consdata->varbounds )
12345  return SCIP_OKAY;
12346 
12347  vars = consdata->vars;
12348  durations = consdata->durations;
12349  demands = consdata->demands;
12350  capacity = consdata->capacity;
12351  nvars = consdata->nvars;
12352 
12353  nconss = 0;
12354 
12355  for( i = 0; i < nvars && !SCIPisStopped(scip); ++i )
12356  {
12357  SCIP_VAR** vbdvars;
12358  SCIP_VAR* var;
12359  SCIP_Real* vbdcoefs;
12360  SCIP_Real* vbdconsts;
12361  int nvbdvars;
12362  int b;
12363  int j;
12364 
12365  var = consdata->vars[i];
12366  assert(var != NULL);
12367 
12368  vbdvars = SCIPvarGetVlbVars(var);
12369  vbdcoefs = SCIPvarGetVlbCoefs(var);
12370  vbdconsts = SCIPvarGetVlbConstants(var);
12371  nvbdvars = SCIPvarGetNVlbs(var);
12372 
12373  for( b = 0; b < nvbdvars; ++b )
12374  {
12375  if( SCIPisEQ(scip, vbdcoefs[b], 1.0) )
12376  {
12377  if( convertBoundToInt(scip, vbdconsts[b]) > -durations[i] )
12378  {
12379  for( j = 0; j < nvars; ++j )
12380  {
12381  if( vars[j] == vbdvars[b] )
12382  break;
12383  }
12384  if( j == nvars )
12385  continue;
12386 
12387  if( demands[i] + demands[j] > capacity && convertBoundToInt(scip, vbdconsts[b]) < durations[j] )
12388  {
12389  SCIP_Bool infeasible;
12390  char name[SCIP_MAXSTRLEN];
12391  int nlocalbdchgs;
12392 
12393  SCIPdebugMessage("<%s>[%d] + %g <= <%s>[%d]\n", SCIPvarGetName(vbdvars[b]), durations[j], vbdconsts[b], SCIPvarGetName(var), durations[i]);
12394 
12395  /* construct constraint name */
12396  (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "varbound_%d_%d", SCIPgetNRuns(scip), nconss);
12397 
12398  SCIP_CALL( createPrecedenceCons(scip, name, vars[j], vars[i], durations[j]) );
12399  nconss++;
12400 
12401  SCIP_CALL( SCIPaddVarVlb(scip, var, vbdvars[b], 1.0, (SCIP_Real) durations[j], &infeasible, &nlocalbdchgs) );
12402  assert(!infeasible);
12403 
12404  (*nchgbds) += nlocalbdchgs;
12405  }
12406  }
12407  }
12408  }
12409  }
12410 
12411  (*naddconss) += nconss;
12412 
12413  consdata->varbounds = TRUE;
12414 
12415  return SCIP_OKAY;
12416 }
12417 
12418 /**@} */
12419 
12420 
12421 /**@name Callback methods of constraint handler
12422  *
12423  * @{
12424  */
12425 
12426 /** copy method for constraint handler plugins (called when SCIP copies plugins) */
12427 static
12428 SCIP_DECL_CONSHDLRCOPY(conshdlrCopyCumulative)
12429 { /*lint --e{715}*/
12430  assert(scip != NULL);
12431  assert(conshdlr != NULL);
12432  assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
12433 
12434  /* call inclusion method of constraint handler */
12436 
12438 
12439  *valid = TRUE;
12440 
12441  return SCIP_OKAY;
12442 }
12443 
12444 /** destructor of constraint handler to free constraint handler data (called when SCIP is exiting) */
12445 static
12446 SCIP_DECL_CONSFREE(consFreeCumulative)
12447 { /*lint --e{715}*/
12448  SCIP_CONSHDLRDATA* conshdlrdata;
12449 
12450  assert(conshdlr != NULL);
12451  assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
12452 
12453  conshdlrdata = SCIPconshdlrGetData(conshdlr);
12454  assert(conshdlrdata != NULL);
12455 
12456 #ifdef SCIP_STATISTIC
12457  if( !conshdlrdata->iscopy )
12458  {
12459  /* statisitc output if SCIP_STATISTIC is defined */
12460  SCIPstatisticPrintf("time-table: lb=%"SCIP_LONGINT_FORMAT", ub=%"SCIP_LONGINT_FORMAT", cutoff=%"SCIP_LONGINT_FORMAT"\n",
12461  conshdlrdata->nlbtimetable, conshdlrdata->nubtimetable, conshdlrdata->ncutofftimetable);
12462  SCIPstatisticPrintf("edge-finder: lb=%"SCIP_LONGINT_FORMAT", ub=%"SCIP_LONGINT_FORMAT", cutoff=%"SCIP_LONGINT_FORMAT"\n",
12463  conshdlrdata->nlbedgefinder, conshdlrdata->nubedgefinder, conshdlrdata->ncutoffedgefinder);
12464  SCIPstatisticPrintf("overload: time-table=%"SCIP_LONGINT_FORMAT" time-time edge-finding=%"SCIP_LONGINT_FORMAT"\n",
12465  conshdlrdata->ncutoffoverload, conshdlrdata->ncutoffoverloadTTEF);
12466  }
12467 #endif
12468 
12469  conshdlrdataFree(scip, &conshdlrdata);
12470 
12471  SCIPconshdlrSetData(conshdlr, NULL);
12472 
12473  return SCIP_OKAY;
12474 }
12475 
12476 
12477 /** presolving initialization method of constraint handler (called when presolving is about to begin) */
12478 static
12479 SCIP_DECL_CONSINITPRE(consInitpreCumulative)
12480 { /*lint --e{715}*/
12481  int c;
12482 
12483  for( c = 0; c < nconss; ++c )
12484  {
12485  /* remove jobs which have a duration or demand of zero (zero energy) or lay outside the effective horizon [hmin,
12486  * hmax)
12487  */
12488  SCIP_CALL( removeIrrelevantJobs(scip, conss[c]) );
12489  }
12490 
12491  return SCIP_OKAY;
12492 }
12493 
12494 
12495 /** presolving deinitialization method of constraint handler (called after presolving has been finished) */
12496 #ifdef SCIP_STATISTIC
12497 static
12498 SCIP_DECL_CONSEXITPRE(consExitpreCumulative)
12499 { /*lint --e{715}*/
12500  SCIP_CONSHDLRDATA* conshdlrdata;
12501  int c;
12502 
12503  conshdlrdata = SCIPconshdlrGetData(conshdlr);
12504  assert(conshdlrdata != NULL);
12505 
12506  for( c = 0; c < nconss; ++c )
12507  {
12508  SCIP_CALL( evaluateCumulativeness(scip, conss[c]) );
12509 
12510 #if 0
12511  SCIP_CALL( SCIPvisualizeConsCumulative(scip, conss[c]) );
12512 #endif
12513  }
12514 
12515  if( !conshdlrdata->iscopy )
12516  {
12517  SCIPstatisticPrintf("@11 added variables bounds constraints %d\n", conshdlrdata->naddedvarbounds);
12518  SCIPstatisticPrintf("@22 added disjunctive constraints %d\n", conshdlrdata->naddeddisjunctives);
12519  SCIPstatisticPrintf("@33 irrelevant %d\n", conshdlrdata->nirrelevantjobs);
12520  SCIPstatisticPrintf("@44 dual %d\n", conshdlrdata->ndualfixs);
12521  SCIPstatisticPrintf("@55 locks %d\n", conshdlrdata->nremovedlocks);
12522  SCIPstatisticPrintf("@66 decomp %d\n", conshdlrdata->ndecomps);
12523  SCIPstatisticPrintf("@77 allconsdual %d\n", conshdlrdata->nallconsdualfixs);
12524  SCIPstatisticPrintf("@88 alwaysruns %d\n", conshdlrdata->nalwaysruns);
12525  SCIPstatisticPrintf("@99 dualbranch %d\n", conshdlrdata->ndualbranchs);
12526  }
12527 
12528  return SCIP_OKAY;
12529 }
12530 #endif
12531 
12532 
12533 /** solving process deinitialization method of constraint handler (called before branch and bound process data is freed) */
12534 static
12535 SCIP_DECL_CONSEXITSOL(consExitsolCumulative)
12536 { /*lint --e{715}*/
12537  SCIP_CONSDATA* consdata;
12538  int c;
12539 
12540  assert(conshdlr != NULL);
12541  assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
12542 
12543  /* release the rows of all constraints */
12544  for( c = 0; c < nconss; ++c )
12545  {
12546  consdata = SCIPconsGetData(conss[c]);
12547  assert(consdata != NULL);
12548 
12549  /* free rows */
12550  SCIP_CALL( consdataFreeRows(scip, &consdata) );
12551  }
12552 
12553  return SCIP_OKAY;
12554 }
12555 
12556 /** frees specific constraint data */
12557 static
12558 SCIP_DECL_CONSDELETE(consDeleteCumulative)
12559 { /*lint --e{715}*/
12560  assert(conshdlr != NULL);
12561  assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
12562  assert(consdata != NULL );
12563  assert(*consdata != NULL );
12564 
12565  /* if constraint belongs to transformed problem space, drop bound change events on variables */
12566  if( (*consdata)->nvars > 0 && SCIPvarIsTransformed((*consdata)->vars[0]) )
12567  {
12568  SCIP_CONSHDLRDATA* conshdlrdata;
12569 
12570  conshdlrdata = SCIPconshdlrGetData(conshdlr);
12571  assert(conshdlrdata != NULL);
12572 
12573  SCIP_CALL( consdataDropAllEvents(scip, *consdata, conshdlrdata->eventhdlr) );
12574  }
12575 
12576  /* free cumulative constraint data */
12577  SCIP_CALL( consdataFree(scip, consdata) );
12578 
12579  return SCIP_OKAY;
12580 }
12581 
12582 /** transforms constraint data into data belonging to the transformed problem */
12583 static
12584 SCIP_DECL_CONSTRANS(consTransCumulative)
12585 { /*lint --e{715}*/
12586  SCIP_CONSHDLRDATA* conshdlrdata;
12587  SCIP_CONSDATA* sourcedata;
12588  SCIP_CONSDATA* targetdata;
12589 
12590  assert(conshdlr != NULL);
12591  assert(SCIPgetStage(scip) == SCIP_STAGE_TRANSFORMING);
12592  assert(sourcecons != NULL);
12593  assert(targetcons != NULL);
12594 
12595  sourcedata = SCIPconsGetData(sourcecons);
12596  assert(sourcedata != NULL);
12597  assert(sourcedata->demandrows == NULL);
12598 
12599  SCIPdebugMessage("transform cumulative constraint <%s>\n", SCIPconsGetName(sourcecons));
12600 
12601  /* get event handler */
12602  conshdlrdata = SCIPconshdlrGetData(conshdlr);
12603  assert(conshdlrdata != NULL);
12604  assert(conshdlrdata->eventhdlr != NULL);
12605 
12606  /* create constraint data for target constraint */
12607  SCIP_CALL( consdataCreate(scip, &targetdata, sourcedata->vars, sourcedata->linkingconss,
12608  sourcedata->durations, sourcedata->demands, sourcedata->nvars, sourcedata->capacity,
12609  sourcedata->hmin, sourcedata->hmax, SCIPconsIsChecked(sourcecons)) );
12610 
12611  /* create target constraint */
12612  SCIP_CALL( SCIPcreateCons(scip, targetcons, SCIPconsGetName(sourcecons), conshdlr, targetdata,
12613  SCIPconsIsInitial(sourcecons), SCIPconsIsSeparated(sourcecons), SCIPconsIsEnforced(sourcecons),
12614  SCIPconsIsChecked(sourcecons), SCIPconsIsPropagated(sourcecons),
12615  SCIPconsIsLocal(sourcecons), SCIPconsIsModifiable(sourcecons),
12616  SCIPconsIsDynamic(sourcecons), SCIPconsIsRemovable(sourcecons), SCIPconsIsStickingAtNode(sourcecons)) );
12617 
12618  /* catch bound change events of variables */
12619  SCIP_CALL( consdataCatchEvents(scip, targetdata, conshdlrdata->eventhdlr) );
12620 
12621  return SCIP_OKAY;
12622 }
12623 
12624 /** LP initialization method of constraint handler */
12625 static
12626 SCIP_DECL_CONSINITLP(consInitlpCumulative)
12627 {
12628  SCIP_CONSHDLRDATA* conshdlrdata;
12629  int c;
12630 
12631  assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
12632  assert(conshdlr != NULL);
12633  conshdlrdata = SCIPconshdlrGetData(conshdlr);
12634  assert(conshdlrdata != NULL);
12635 
12636  SCIPdebugMessage("initialize LP relaxation for %d cumulative constraints\n", nconss);
12637 
12638  if( conshdlrdata->usebinvars )
12639  {
12640  /* add rows to LP */
12641  for( c = 0; c < nconss; ++c )
12642  {
12643  assert(SCIPconsIsInitial(conss[c]));
12644  SCIP_CALL( addRelaxation(scip, conss[c], conshdlrdata->cutsasconss) );
12645 
12646  if( conshdlrdata->cutsasconss )
12647  {
12648  SCIP_CALL( SCIPrestartSolve(scip) );
12649  }
12650  }
12651  }
12652 
12653  /**@todo if we want to use only the integer variables; only these will be in cuts
12654  * create some initial cuts, currently these are only separated */
12655 
12656  return SCIP_OKAY;
12657 }
12658 
12659 /** separation method of constraint handler for LP solutions */
12660 static
12661 SCIP_DECL_CONSSEPALP(consSepalpCumulative)
12662 {
12663  SCIP_CONSHDLRDATA* conshdlrdata;
12664  SCIP_Bool cutoff;
12665  SCIP_Bool reducedom;
12666  SCIP_Bool separated;
12667  int c;
12668 
12669  SCIPdebugMessage("consSepalpCumulative\n");
12670 
12671  assert(conshdlr != NULL);
12672  assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
12673  assert(nconss == 0 || conss != NULL);
12674  assert(result != NULL);
12675  conshdlrdata = SCIPconshdlrGetData(conshdlr);
12676  assert(conshdlrdata != NULL);
12677 
12678  SCIPdebugMessage("separating %d/%d cumulative constraints\n", nusefulconss, nconss);
12679 
12680  cutoff = FALSE;
12681  reducedom = FALSE;
12682  separated = FALSE;
12683  (*result) = SCIP_DIDNOTRUN;
12684 
12685  if( !conshdlrdata->localcuts && SCIPgetDepth(scip) > 0 )
12686  return SCIP_OKAY;
12687 
12688  (*result) = SCIP_DIDNOTFIND;
12689 
12690  if( conshdlrdata->usebinvars )
12691  {
12692  /* check all useful cumulative constraints for feasibility */
12693  for( c = 0; c < nusefulconss && !reducedom && !cutoff; ++c )
12694  {
12695  SCIP_CALL( separateConsBinaryRepresentation(scip, conss[c], NULL, &separated, &cutoff) );
12696  }
12697 
12698  if( !cutoff && !reducedom && conshdlrdata->usecovercuts )
12699  {
12700  for( c = 0; c < nusefulconss; ++c )
12701  {
12702  SCIP_CALL( separateCoverCutsCons(scip, conss[c], NULL, &separated, &cutoff) );
12703  }
12704  }
12705  }
12706 
12707  if( conshdlrdata->sepaold )
12708  {
12709  /* separate cuts containing only integer variables */
12710  for( c = 0; c < nusefulconss; ++c )
12711  {
12712  SCIP_CALL( separateConsOnIntegerVariables(scip, conss[c], NULL, TRUE, &separated) );
12713  SCIP_CALL( separateConsOnIntegerVariables(scip, conss[c], NULL, FALSE, &separated) );
12714  }
12715  }
12716 
12717  if( cutoff )
12718  *result = SCIP_CUTOFF;
12719  else if( reducedom )
12720  *result = SCIP_REDUCEDDOM;
12721  else if( separated )
12722  *result = SCIP_SEPARATED;
12723 
12724  return SCIP_OKAY;
12725 }
12726 
12727 /** separation method of constraint handler for arbitrary primal solutions */
12728 static
12729 SCIP_DECL_CONSSEPASOL(consSepasolCumulative)
12730 { /*lint --e{715}*/
12731  SCIP_CONSHDLRDATA* conshdlrdata;
12732  SCIP_Bool cutoff;
12733  SCIP_Bool reducedom;
12734  SCIP_Bool separated;
12735  int c;
12736 
12737  assert(conshdlr != NULL);
12738  assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
12739  assert(nconss == 0 || conss != NULL);
12740  assert(result != NULL);
12741 
12742  conshdlrdata = SCIPconshdlrGetData(conshdlr);
12743  assert(conshdlrdata != NULL);
12744 
12745  if( !conshdlrdata->localcuts && SCIPgetDepth(scip) > 0 )
12746  return SCIP_OKAY;
12747 
12748  SCIPdebugMessage("separating %d/%d cumulative constraints\n", nusefulconss, nconss);
12749 
12750  cutoff = FALSE;
12751  reducedom = FALSE;
12752  separated = FALSE;
12753  (*result) = SCIP_DIDNOTFIND;
12754 
12755  if( conshdlrdata->usebinvars )
12756  {
12757  /* check all useful cumulative constraints for feasibility */
12758  for( c = 0; c < nusefulconss && !cutoff && !reducedom; ++c )
12759  {
12760  SCIP_CALL( separateConsBinaryRepresentation(scip, conss[c], NULL, &separated, &cutoff) );
12761  }
12762 
12763  if( !cutoff && !reducedom && conshdlrdata->usecovercuts )
12764  {
12765  for( c = 0; c < nusefulconss; ++c )
12766  {
12767  SCIP_CALL( separateCoverCutsCons(scip, conss[c], sol, &separated, &cutoff) );
12768  }
12769  }
12770  }
12771  if( conshdlrdata->sepaold )
12772  {
12773  /* separate cuts containing only integer variables */
12774  for( c = 0; c < nusefulconss; ++c )
12775  {
12776  SCIP_CALL( separateConsOnIntegerVariables(scip, conss[c], NULL, TRUE, &separated) );
12777  SCIP_CALL( separateConsOnIntegerVariables(scip, conss[c], NULL, FALSE, &separated) );
12778  }
12779  }
12780 
12781  if( cutoff )
12782  *result = SCIP_CUTOFF;
12783  else if( reducedom )
12784  *result = SCIP_REDUCEDDOM;
12785  else if( separated )
12786  *result = SCIP_SEPARATED;
12787 
12788  return SCIP_OKAY;
12789 }
12790 
12791 /** constraint enforcing method of constraint handler for LP solutions */
12792 static
12793 SCIP_DECL_CONSENFOLP(consEnfolpCumulative)
12794 { /*lint --e{715}*/
12795  SCIP_CONSHDLRDATA* conshdlrdata;
12796 
12797  assert(conshdlr != NULL);
12798  assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
12799  assert(nconss == 0 || conss != NULL);
12800  assert(result != NULL);
12801 
12802  if( solinfeasible )
12803  {
12804  *result = SCIP_INFEASIBLE;
12805  return SCIP_OKAY;
12806  }
12807 
12808  SCIPdebugMessage("LP enforcing %d useful cumulative constraints of %d constraints\n", nusefulconss, nconss);
12809 
12810  conshdlrdata = SCIPconshdlrGetData(conshdlr);
12811  assert(conshdlrdata != NULL);
12812 
12813  (*result) = SCIP_FEASIBLE;
12814 
12815  if( conshdlrdata->usebinvars )
12816  {
12817  SCIP_Bool separated;
12818  SCIP_Bool cutoff;
12819  int c;
12820 
12821  separated = FALSE;
12822 
12823  /* first check if a constraints is violated */
12824  for( c = 0; c < nusefulconss; ++c )
12825  {
12826  SCIP_CONS* cons;
12827  SCIP_Bool violated;
12828 
12829  cons = conss[c];
12830  assert(cons != NULL);
12831 
12832  SCIP_CALL( checkCons(scip, cons, NULL, &violated, FALSE) );
12833 
12834  if( !violated )
12835  continue;
12836 
12837  SCIP_CALL( separateConsBinaryRepresentation(scip, cons, NULL, &separated, &cutoff) );
12838  if ( cutoff )
12839  {
12840  *result = SCIP_CUTOFF;
12841  return SCIP_OKAY;
12842  }
12843  }
12844 
12845  for( ; c < nconss && !separated; ++c )
12846  {
12847  SCIP_CONS* cons;
12848  SCIP_Bool violated;
12849 
12850  cons = conss[c];
12851  assert(cons != NULL);
12852 
12853  SCIP_CALL( checkCons(scip, cons, NULL, &violated, FALSE) );
12854 
12855  if( !violated )
12856  continue;
12857 
12858  SCIP_CALL( separateConsBinaryRepresentation(scip, cons, NULL, &separated, &cutoff) );
12859  if ( cutoff )
12860  {
12861  *result = SCIP_CUTOFF;
12862  return SCIP_OKAY;
12863  }
12864  }
12865 
12866  if( separated )
12867  (*result) = SCIP_SEPARATED;
12868  }
12869  else
12870  {
12871  SCIP_CALL( enforceSolution(scip, conss, nconss, conshdlrdata->fillbranchcands, result) );
12872  }
12873 
12874  return SCIP_OKAY;
12875 }
12876 
12877 /** constraint enforcing method of constraint handler for pseudo solutions */
12878 static
12879 SCIP_DECL_CONSENFOPS(consEnfopsCumulative)
12880 { /*lint --e{715}*/
12881  SCIP_CONSHDLRDATA* conshdlrdata;
12882 
12883  SCIPdebugMessage("method: enforce pseudo solution\n");
12884 
12885  assert(conshdlr != NULL);
12886  assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
12887  assert(nconss == 0 || conss != NULL);
12888  assert(result != NULL);
12889 
12890  if( objinfeasible )
12891  {
12892  *result = SCIP_DIDNOTRUN;
12893  return SCIP_OKAY;
12894  }
12895 
12896  (*result) = SCIP_FEASIBLE;
12897 
12898  conshdlrdata = SCIPconshdlrGetData(conshdlr);
12899  assert(conshdlrdata != NULL);
12900 
12901  SCIP_CALL( enforceSolution(scip, conss, nconss, conshdlrdata->fillbranchcands, result) );
12902 
12903  return SCIP_OKAY;
12904 }
12905 
12906 /** feasibility check method of constraint handler for integral solutions */
12907 static
12908 SCIP_DECL_CONSCHECK(consCheckCumulative)
12909 { /*lint --e{715}*/
12910  SCIP_Bool violated;
12911  int c;
12912 
12913  assert(conshdlr != NULL);
12914  assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
12915  assert(nconss == 0 || conss != NULL);
12916  assert(result != NULL);
12917 
12918  violated = FALSE;
12919 
12920  SCIPdebugMessage("check %d cumulative constraints\n", nconss);
12921 
12922  for( c = 0; c < nconss && !violated; ++c )
12923  {
12924  SCIP_CALL( checkCons(scip, conss[c], sol, &violated, printreason) );
12925  }
12926 
12927  if( violated )
12928  *result = SCIP_INFEASIBLE;
12929  else
12930  *result = SCIP_FEASIBLE;
12931 
12932  return SCIP_OKAY;
12933 }
12934 
12935 /** domain propagation method of constraint handler */
12936 static
12937 SCIP_DECL_CONSPROP(consPropCumulative)
12938 { /*lint --e{715}*/
12939  SCIP_CONSHDLRDATA* conshdlrdata;
12940  SCIP_Bool cutoff;
12941  int nchgbds;
12942  int ndelconss;
12943  int c;
12944 #if 0
12945  int naggrvars = 0;
12946 #endif
12947 
12948  SCIPdebugMessage("propagate %d of %d useful cumulative constraints\n", nusefulconss, nconss);
12949 
12950  assert(conshdlr != NULL);
12951  assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
12952  assert(nconss == 0 || conss != NULL);
12953  assert(result != NULL);
12954 
12955  conshdlrdata = SCIPconshdlrGetData(conshdlr);
12956  assert(conshdlrdata != NULL);
12957 
12958  nchgbds = 0;
12959  ndelconss = 0;
12960  cutoff = FALSE;
12961  (*result) = SCIP_DIDNOTRUN;
12962 
12963  /* propgate all useful constraints */
12964  for( c = 0; c < nusefulconss && !cutoff; ++c )
12965  {
12966  SCIP_CONS* cons;
12967 
12968  cons = conss[c];
12969  assert(cons != NULL);
12970 
12971  if( SCIPgetDepth(scip) == 0 )
12972  {
12973 #if 0
12974  SCIP_CALL( presolveCons(scip, cons, conshdlrdata,
12975  &nchgbds, &naggrvars, &nchgbds, &ndelconss, &nchgbds, &nchgbds, &nchgbds, &cutoff, &cutoff) );
12976 #else
12977  SCIP_CALL( presolveCons(scip, cons, conshdlrdata,
12978  &nchgbds, &nchgbds, &ndelconss, &nchgbds, &nchgbds, &nchgbds, &cutoff, &cutoff) );
12979 #endif
12980  if( cutoff )
12981  break;
12982 
12983  if( SCIPconsIsDeleted(cons) )
12984  continue;
12985  }
12986 
12987  SCIP_CALL( propagateCons(scip, cons, conshdlrdata, &nchgbds, &ndelconss, &cutoff) );
12988  }
12989 
12990  if( !cutoff && nchgbds == 0 )
12991  {
12992  /* propgate all other constraints */
12993  for( c = nusefulconss; c < nconss && !cutoff; ++c )
12994  {
12995  SCIP_CALL( propagateCons(scip, conss[c], conshdlrdata, &nchgbds, &ndelconss, &cutoff) );
12996  }
12997  }
12998 
12999 #if 0
13000  if( !cutoff && conshdlrdata->dualpresolve && nconss > 1 )
13001  {
13002  SCIP_CALL( propagateAllConss(scip, conshdlrdata, conss, nconss, TRUE, &nchgbds, &cutoff, NULL) );
13003  }
13004 #endif
13005 
13006  if( cutoff )
13007  {
13008  SCIPdebugMessage("detected infeasible\n");
13009  *result = SCIP_CUTOFF;
13010  }
13011  else if( nchgbds > 0 )
13012  {
13013  SCIPdebugMessage("delete (locally) %d constraints and changed %d variable bounds\n", ndelconss, nchgbds);
13014  *result = SCIP_REDUCEDDOM;
13015  }
13016  else
13017  *result = SCIP_DIDNOTFIND;
13018 
13019  return SCIP_OKAY;
13020 }
13021 
13022 /** presolving method of constraint handler */
13023 static
13024 SCIP_DECL_CONSPRESOL(consPresolCumulative)
13025 { /*lint --e{715}*/
13026  SCIP_CONSHDLRDATA* conshdlrdata;
13027  SCIP_CONS* cons;
13028  SCIP_Bool cutoff;
13029  SCIP_Bool unbounded;
13030  int oldnfixedvars;
13031  int oldnchgbds;
13032  int oldndelconss;
13033  int oldnaddconss;
13034  int oldnupgdconss;
13035  int oldnchgsides;
13036  int oldnchgcoefs;
13037  int c;
13038 
13039  assert(conshdlr != NULL);
13040  assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
13041  assert(scip != NULL);
13042  assert(result != NULL);
13043 
13044  SCIPdebugMessage("presolve %d cumulative constraints\n", nconss);
13045 
13046  conshdlrdata = SCIPconshdlrGetData(conshdlr);
13047  assert(conshdlrdata != NULL);
13048 
13049  *result = SCIP_DIDNOTRUN;
13050 
13051  oldnfixedvars = *nfixedvars;
13052  oldnchgbds = *nchgbds;
13053  oldnchgsides = *nchgsides;
13054  oldnchgcoefs = *nchgcoefs;
13055  oldnupgdconss = *nupgdconss;
13056  oldndelconss = *ndelconss;
13057  oldnaddconss = *naddconss;
13058  cutoff = FALSE;
13059  unbounded = FALSE;
13060 
13061  /* process constraints */
13062  for( c = 0; c < nconss && !cutoff; ++c )
13063  {
13064  cons = conss[c];
13065 
13066  /* remove jobs which have a duration or demand of zero (zero energy) or lay outside the effective horizon [hmin,
13067  * hmax)
13068  */
13069  SCIP_CALL( removeIrrelevantJobs(scip, conss[c]) );
13070 
13071 #if 0
13072  SCIP_CALL( presolveCons(scip, cons, conshdlrdata,
13073  nfixedvars, naggrvars, nchgbds, ndelconss, naddconss, nchgcoefs, nchgsides, &cutoff, &unbounded) );
13074 #else
13075  SCIP_CALL( presolveCons(scip, cons, conshdlrdata,
13076  nfixedvars, nchgbds, ndelconss, naddconss, nchgcoefs, nchgsides, &cutoff, &unbounded) );
13077 #endif
13078 
13079  if( cutoff || unbounded )
13080  break;
13081 
13082  if( SCIPconsIsDeleted(cons) )
13083  continue;
13084 
13085  /* in the first round we create a disjunctive constraint containing those jobs which cannot run in parallel */
13086  if( nrounds == 1 && SCIPgetNRuns(scip) == 1 && conshdlrdata->disjunctive )
13087  {
13088  SCIP_CALL( createDisjuctiveCons(scip, cons, naddconss) );
13089  }
13090 
13091  /* strength existing variable bounds using the cumulative condition */
13092  SCIP_CALL( strengthVarbaounds(scip, cons, nchgbds, naddconss) );
13093 
13094  /* propagate cumulative constraint */
13095  SCIP_CALL( propagateCons(scip, cons, conshdlrdata, nchgbds, ndelconss, &cutoff) );
13096  assert(checkDemands(scip, cons) || cutoff);
13097  }
13098 
13099  if( !cutoff && !unbounded && conshdlrdata->dualpresolve && nconss > 1 )
13100  {
13101  SCIP_CALL( propagateAllConss(scip, conshdlrdata, conss, nconss, FALSE,
13102  nfixedvars, &cutoff, NULL) );
13103  }
13104 
13105  /* only perform the detection of variable bounds and disjunctive constraint once */
13106  if( !cutoff && SCIPgetNRuns(scip) == 1 && nrounds == 2
13107  && (conshdlrdata->detectvarbounds || conshdlrdata->detectdisjunctive) )
13108  {
13109  /* combine different source and detect disjunctive constraints and variable bound constraints to improve the
13110  * propagation
13111  */
13112  SCIP_CALL( detectRedundantConss(scip, conshdlrdata, conss, nconss, naddconss) );
13113  }
13114 
13115  if( !cutoff && conshdlrdata->presolpairwise )
13116  {
13117  SCIP_CALL( removeRedundantConss(scip, conss, nconss, ndelconss) );
13118  }
13119 
13120  SCIPdebugMessage("delete %d constraints and changed %d variable bounds (cutoff %u)\n",
13121  *ndelconss - oldndelconss, *nchgbds - oldnchgbds, cutoff);
13122 
13123  if( cutoff )
13124  *result = SCIP_CUTOFF;
13125  else if( unbounded )
13126  *result = SCIP_UNBOUNDED;
13127  else if( *nchgbds > oldnchgbds || *nfixedvars > oldnfixedvars || *nchgsides > oldnchgsides
13128  || *nchgcoefs > oldnchgcoefs || *nupgdconss > oldnupgdconss || *ndelconss > oldndelconss || *naddconss > oldnaddconss )
13129  *result = SCIP_SUCCESS;
13130  else
13131  *result = SCIP_DIDNOTFIND;
13132 
13133  return SCIP_OKAY;
13134 }
13135 
13136 /** propagation conflict resolving method of constraint handler */
13137 static
13138 SCIP_DECL_CONSRESPROP(consRespropCumulative)
13139 { /*lint --e{715}*/
13140  SCIP_CONSHDLRDATA* conshdlrdata;
13141  SCIP_CONSDATA* consdata;
13142 
13143  assert(conshdlr != NULL);
13144  assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
13145  assert(scip != NULL);
13146  assert(result != NULL);
13147  assert(infervar != NULL);
13148  assert(bdchgidx != NULL);
13149 
13150  conshdlrdata = SCIPconshdlrGetData(conshdlr);
13151  assert(conshdlrdata != NULL);
13152 
13153  /* process constraint */
13154  assert(cons != NULL);
13155 
13156  consdata = SCIPconsGetData(cons);
13157  assert(consdata != NULL);
13158 
13159  SCIPdebugMessage("resolve propagation: variable <%s>, cumulative constraint <%s> (capacity %d, propagation %d, H=[%d,%d))\n",
13160  SCIPvarGetName(infervar), SCIPconsGetName(cons), consdata->capacity, inferInfoGetProprule(intToInferInfo(inferinfo)),
13161  SCIPgetHminCumulative(scip, cons), SCIPgetHmaxCumulative(scip, cons));
13162 
13163  SCIP_CALL( respropCumulativeCondition(scip, consdata->nvars, consdata->vars,
13164  consdata->durations, consdata->demands, consdata->capacity, consdata->hmin, consdata->hmax,
13165  infervar, intToInferInfo(inferinfo), boundtype, bdchgidx, relaxedbd, conshdlrdata->usebdwidening, NULL, result) );
13166 
13167  return SCIP_OKAY;
13168 }
13169 
13170 /** variable rounding lock method of constraint handler */
13171 static
13172 SCIP_DECL_CONSLOCK(consLockCumulative)
13173 { /*lint --e{715}*/
13174  SCIP_CONSDATA* consdata;
13175  SCIP_VAR** vars;
13176  int v;
13177 
13178  SCIPdebugMessage("lock cumulative constraint <%s> with nlockspos = %d, nlocksneg = %d\n", SCIPconsGetName(cons), nlockspos, nlocksneg);
13179 
13180  assert(scip != NULL);
13181  assert(cons != NULL);
13182 
13183  consdata = SCIPconsGetData(cons);
13184  assert(consdata != NULL);
13185 
13186  vars = consdata->vars;
13187  assert(vars != NULL);
13188 
13189  for( v = 0; v < consdata->nvars; ++v )
13190  {
13191  if( consdata->downlocks[v] && consdata->uplocks[v] )
13192  {
13193  /* the integer start variable should not get rounded in both direction */
13194  SCIP_CALL( SCIPaddVarLocks(scip, vars[v], nlockspos + nlocksneg, nlockspos + nlocksneg) );
13195  }
13196  else if( consdata->downlocks[v] )
13197  {
13198  SCIP_CALL( SCIPaddVarLocks(scip, vars[v], nlockspos, nlocksneg) );
13199  }
13200  else if( consdata->uplocks[v] )
13201  {
13202  SCIP_CALL( SCIPaddVarLocks(scip, vars[v], nlocksneg, nlockspos) );
13203  }
13204  }
13205 
13206  return SCIP_OKAY;
13207 }
13208 
13209 
13210 /** constraint display method of constraint handler */
13211 static
13212 SCIP_DECL_CONSPRINT(consPrintCumulative)
13213 { /*lint --e{715}*/
13214  assert(scip != NULL);
13215  assert(conshdlr != NULL);
13216  assert(cons != NULL);
13217 
13218  consdataPrint(scip, SCIPconsGetData(cons), file);
13219 
13220  return SCIP_OKAY;
13221 }
13222 
13223 /** constraint copying method of constraint handler */
13224 static
13225 SCIP_DECL_CONSCOPY(consCopyCumulative)
13226 { /*lint --e{715}*/
13227  SCIP_CONSDATA* sourceconsdata;
13228  SCIP_VAR** sourcevars;
13229  SCIP_VAR** vars;
13230  const char* consname;
13231 
13232  int nvars;
13233  int v;
13234 
13235  sourceconsdata = SCIPconsGetData(sourcecons);
13236  assert(sourceconsdata != NULL);
13237 
13238  /* get variables of the source constraint */
13239  nvars = sourceconsdata->nvars;
13240  sourcevars = sourceconsdata->vars;
13241 
13242  (*valid) = TRUE;
13243 
13244  if( nvars == 0 )
13245  return SCIP_OKAY;
13246 
13247  /* allocate buffer array */
13248  SCIP_CALL( SCIPallocBufferArray(scip, &vars, nvars) );
13249 
13250  for( v = 0; v < nvars && *valid; ++v )
13251  {
13252  SCIP_CALL( SCIPgetVarCopy(sourcescip, scip, sourcevars[v], &vars[v], varmap, consmap, global, valid) );
13253  assert(!(*valid) || vars[v] != NULL);
13254  }
13255 
13256  /* only create the target constraint, if all variables could be copied */
13257  if( *valid )
13258  {
13259  if( name != NULL )
13260  consname = name;
13261  else
13262  consname = SCIPconsGetName(sourcecons);
13263 
13264  /* create a copy of the cumulative constraint */
13265  SCIP_CALL( SCIPcreateConsCumulative(scip, cons, consname, nvars, vars,
13266  sourceconsdata->durations, sourceconsdata->demands, sourceconsdata->capacity,
13267  initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode) );
13268 
13269  /* adjust left side if the time axis if needed */
13270  if( sourceconsdata->hmin > 0 )
13271  {
13272  SCIP_CALL( SCIPsetHminCumulative(scip, *cons, sourceconsdata->hmin) );
13273  }
13274 
13275  /* adjust right side if the time axis if needed */
13276  if( sourceconsdata->hmax < INT_MAX )
13277  {
13278  SCIP_CALL( SCIPsetHmaxCumulative(scip, *cons, sourceconsdata->hmax) );
13279  }
13280  }
13281 
13282  /* free buffer array */
13283  SCIPfreeBufferArray(scip, &vars);
13284 
13285  return SCIP_OKAY;
13286 }
13287 
13288 
13289 /** constraint parsing method of constraint handler */
13290 static
13291 SCIP_DECL_CONSPARSE(consParseCumulative)
13292 { /*lint --e{715}*/
13293  SCIP_VAR** vars;
13294  SCIP_VAR* var;
13295  SCIP_Real value;
13296  char strvalue[SCIP_MAXSTRLEN];
13297  char* endptr;
13298  int* demands;
13299  int* durations;
13300  int capacity;
13301  int duration;
13302  int demand;
13303  int hmin;
13304  int hmax;
13305  int varssize;
13306  int nvars;
13307 
13308  SCIPdebugMessage("parse <%s> as cumulative constraint\n", str);
13309 
13310  /* cutoff "cumulative" form the constraint string */
13311  SCIPstrCopySection(str, 'c', '(', strvalue, SCIP_MAXSTRLEN, &endptr);
13312  str = endptr;
13313 
13314  varssize = 100;
13315  nvars = 0;
13316 
13317  /* allocate buffer array for variables */
13318  SCIP_CALL( SCIPallocBufferArray(scip, &vars, varssize) );
13319  SCIP_CALL( SCIPallocBufferArray(scip, &demands, varssize) );
13320  SCIP_CALL( SCIPallocBufferArray(scip, &durations, varssize) );
13321 
13322  do
13323  {
13324  SCIP_CALL( SCIPparseVarName(scip, str, &var, &endptr) );
13325 
13326  if( var != NULL )
13327  {
13328  str = endptr;
13329 
13330  SCIPstrCopySection(str, '(', ')', strvalue, SCIP_MAXSTRLEN, &endptr);
13331  duration = atoi(strvalue);
13332  str = endptr;
13333 
13334  SCIPstrCopySection(str, '[', ']', strvalue, SCIP_MAXSTRLEN, &endptr);
13335  demand = atoi(strvalue);
13336  str = endptr;
13337 
13338  SCIPdebugMessage("parse job <%s>, duration %d, demand %d\n", SCIPvarGetName(var), duration, demand);
13339 
13340  vars[nvars] = var;
13341  demands[nvars] = demand;
13342  durations[nvars] = duration;
13343  nvars++;
13344  }
13345  }
13346  while( var != NULL );
13347 
13348  /* parse effective time window */
13349  SCIPstrCopySection(str, '[', ',', strvalue, SCIP_MAXSTRLEN, &endptr);
13350  hmin = atoi(strvalue);
13351  str = endptr;
13352 
13353  if( SCIPstrToRealValue(str, &value, &endptr) )
13354  {
13355  hmax = (int)(value);
13356  str = endptr;
13357 
13358  /* parse capacity */
13359  SCIPstrCopySection(str, ')', '=', strvalue, SCIP_MAXSTRLEN, &endptr);
13360  str = endptr;
13361  if( SCIPstrToRealValue(str, &value, &endptr) )
13362  {
13363  capacity = (int)value;
13364 
13365  /* create cumulative constraint */
13366  SCIP_CALL( SCIPcreateConsCumulative(scip, cons, name, nvars, vars, durations, demands, capacity,
13367  initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode) );
13368 
13369  SCIP_CALL( SCIPsetHminCumulative(scip, *cons, hmin) );
13370  SCIP_CALL( SCIPsetHmaxCumulative(scip, *cons, hmax) );
13371 
13372  (*success) = TRUE;
13373  }
13374  }
13375 
13376  /* free buffer arrays */
13377  SCIPfreeBufferArray(scip, &durations);
13378  SCIPfreeBufferArray(scip, &demands);
13379  SCIPfreeBufferArray(scip, &vars);
13380 
13381  return SCIP_OKAY;
13382 }
13383 
13384 /** constraint method of constraint handler which returns the variables (if possible) */
13385 static
13386 SCIP_DECL_CONSGETVARS(consGetVarsCumulative)
13387 { /*lint --e{715}*/
13388  SCIP_CONSDATA* consdata;
13389 
13390  consdata = SCIPconsGetData(cons);
13391  assert(consdata != NULL);
13392 
13393  if( varssize < consdata->nvars )
13394  (*success) = FALSE;
13395  else
13396  {
13397  assert(vars != NULL);
13398 
13399  BMScopyMemoryArray(vars, consdata->vars, consdata->nvars);
13400  (*success) = TRUE;
13401  }
13402 
13403  return SCIP_OKAY;
13404 }
13405 
13406 /** constraint method of constraint handler which returns the number of variables (if possible) */
13407 static
13408 SCIP_DECL_CONSGETNVARS(consGetNVarsCumulative)
13409 { /*lint --e{715}*/
13410  SCIP_CONSDATA* consdata;
13411 
13412  consdata = SCIPconsGetData(cons);
13413  assert(consdata != NULL);
13414 
13415  (*nvars) = consdata->nvars;
13416  (*success) = TRUE;
13417 
13418  return SCIP_OKAY;
13419 }
13420 
13421 /**@} */
13422 
13423 /**@name Callback methods of event handler
13424  *
13425  * @{
13426  */
13427 
13428 
13429 /** execution method of event handler */
13430 static
13431 SCIP_DECL_EVENTEXEC(eventExecCumulative)
13432 { /*lint --e{715}*/
13433  SCIP_CONSDATA* consdata;
13434 
13435  assert(scip != NULL);
13436  assert(eventhdlr != NULL);
13437  assert(eventdata != NULL);
13438  assert(strcmp(SCIPeventhdlrGetName(eventhdlr), EVENTHDLR_NAME) == 0);
13439  assert(event != NULL);
13440 
13441  consdata = (SCIP_CONSDATA*)eventdata;
13442  assert(consdata != NULL);
13443 
13444  /* mark the constraint to be not propagated */
13445  consdata->propagated = FALSE;
13446 
13447  return SCIP_OKAY;
13448 }
13449 
13450 /**@} */
13451 
13452 /**@name Interface methods
13453  *
13454  * @{
13455  */
13456 
13457 /*
13458  * constraint specific interface methods
13459  */
13460 
13461 /** creates the handler for cumulative constraints and includes it in SCIP */
13463  SCIP* scip /**< SCIP data structure */
13464  )
13465 {
13466  SCIP_CONSHDLRDATA* conshdlrdata;
13467  SCIP_CONSHDLR* conshdlr;
13468  SCIP_EVENTHDLR* eventhdlr;
13469 
13470  /* create event handler for bound change events */
13471  SCIP_CALL( SCIPincludeEventhdlrBasic(scip, &eventhdlr, EVENTHDLR_NAME, EVENTHDLR_DESC, eventExecCumulative, NULL) );
13472 
13473  /* create cumulative constraint handler data */
13474  SCIP_CALL( conshdlrdataCreate(scip, &conshdlrdata, eventhdlr) );
13475 
13476  /* include constraint handler */
13479  consEnfolpCumulative, consEnfopsCumulative, consCheckCumulative, consLockCumulative,
13480  conshdlrdata) );
13481 
13482  assert(conshdlr != NULL);
13483 
13484  /* set non-fundamental callbacks via specific setter functions */
13485  SCIP_CALL( SCIPsetConshdlrCopy(scip, conshdlr, conshdlrCopyCumulative, consCopyCumulative) );
13486  SCIP_CALL( SCIPsetConshdlrDelete(scip, conshdlr, consDeleteCumulative) );
13487 #ifdef SCIP_STATISTIC
13488  SCIP_CALL( SCIPsetConshdlrExitpre(scip, conshdlr, consExitpreCumulative) );
13489 #endif
13490  SCIP_CALL( SCIPsetConshdlrExitsol(scip, conshdlr, consExitsolCumulative) );
13491  SCIP_CALL( SCIPsetConshdlrFree(scip, conshdlr, consFreeCumulative) );
13492  SCIP_CALL( SCIPsetConshdlrGetVars(scip, conshdlr, consGetVarsCumulative) );
13493  SCIP_CALL( SCIPsetConshdlrGetNVars(scip, conshdlr, consGetNVarsCumulative) );
13494  SCIP_CALL( SCIPsetConshdlrInitpre(scip, conshdlr, consInitpreCumulative) );
13495  SCIP_CALL( SCIPsetConshdlrInitlp(scip, conshdlr, consInitlpCumulative) );
13496  SCIP_CALL( SCIPsetConshdlrParse(scip, conshdlr, consParseCumulative) );
13497  SCIP_CALL( SCIPsetConshdlrPresol(scip, conshdlr, consPresolCumulative, CONSHDLR_MAXPREROUNDS,
13499  SCIP_CALL( SCIPsetConshdlrPrint(scip, conshdlr, consPrintCumulative) );
13500  SCIP_CALL( SCIPsetConshdlrProp(scip, conshdlr, consPropCumulative, CONSHDLR_PROPFREQ, CONSHDLR_DELAYPROP,
13502  SCIP_CALL( SCIPsetConshdlrResprop(scip, conshdlr, consRespropCumulative) );
13503  SCIP_CALL( SCIPsetConshdlrSepa(scip, conshdlr, consSepalpCumulative, consSepasolCumulative, CONSHDLR_SEPAFREQ,
13505  SCIP_CALL( SCIPsetConshdlrTrans(scip, conshdlr, consTransCumulative) );
13506 
13507  /* add cumulative constraint handler parameters */
13509  "constraints/"CONSHDLR_NAME"/ttinfer",
13510  "should time-table (core-times) propagator be used to infer bounds?",
13511  &conshdlrdata->ttinfer, FALSE, DEFAULT_TTINFER, NULL, NULL) );
13513  "constraints/"CONSHDLR_NAME"/efcheck",
13514  "should edge-finding be used to detect an overload?",
13515  &conshdlrdata->efcheck, FALSE, DEFAULT_EFCHECK, NULL, NULL) );
13517  "constraints/"CONSHDLR_NAME"/efinfer",
13518  "should edge-finding be used to infer bounds?",
13519  &conshdlrdata->efinfer, FALSE, DEFAULT_EFINFER, NULL, NULL) );
13521  "constraints/"CONSHDLR_NAME"/useadjustedjobs", "should edge-finding be executed?",
13522  &conshdlrdata->useadjustedjobs, TRUE, DEFAULT_USEADJUSTEDJOBS, NULL, NULL) );
13524  "constraints/"CONSHDLR_NAME"/ttefcheck",
13525  "should time-table edge-finding be used to detect an overload?",
13526  &conshdlrdata->ttefcheck, FALSE, DEFAULT_TTEFCHECK, NULL, NULL) );
13528  "constraints/"CONSHDLR_NAME"/ttefinfer",
13529  "should time-table edge-finding be used to infer bounds?",
13530  &conshdlrdata->ttefinfer, FALSE, DEFAULT_TTEFINFER, NULL, NULL) );
13531 
13533  "constraints/"CONSHDLR_NAME"/usebinvars", "should the binary representation be used?",
13534  &conshdlrdata->usebinvars, FALSE, DEFAULT_USEBINVARS, NULL, NULL) );
13536  "constraints/"CONSHDLR_NAME"/localcuts", "should cuts be added only locally?",
13537  &conshdlrdata->localcuts, FALSE, DEFAULT_LOCALCUTS, NULL, NULL) );
13539  "constraints/"CONSHDLR_NAME"/usecovercuts", "should covering cuts be added every node?",
13540  &conshdlrdata->usecovercuts, FALSE, DEFAULT_USECOVERCUTS, NULL, NULL) );
13542  "constraints/"CONSHDLR_NAME"/cutsasconss",
13543  "should the cumulative constraint create cuts as knapsack constraints?",
13544  &conshdlrdata->cutsasconss, FALSE, DEFAULT_CUTSASCONSS, NULL, NULL) );
13546  "constraints/"CONSHDLR_NAME"/sepaold",
13547  "shall old sepa algo be applied?",
13548  &conshdlrdata->sepaold, FALSE, DEFAULT_SEPAOLD, NULL, NULL) );
13549 
13551  "constraints/"CONSHDLR_NAME"/fillbranchcands", "should branching candidates be added to storage?",
13552  &conshdlrdata->fillbranchcands, FALSE, DEFAULT_FILLBRANCHCANDS, NULL, NULL) );
13553 
13554  /* presolving parameters */
13556  "constraints/"CONSHDLR_NAME"/dualpresolve", "should dual presolving be applied?",
13557  &conshdlrdata->dualpresolve, FALSE, DEFAULT_DUALPRESOLVE, NULL, NULL) );
13559  "constraints/"CONSHDLR_NAME"/coeftightening", "should coefficient tightening be applied?",
13560  &conshdlrdata->coeftightening, FALSE, DEFAULT_COEFTIGHTENING, NULL, NULL) );
13562  "constraints/"CONSHDLR_NAME"/normalize", "should demands and capacity be normalized?",
13563  &conshdlrdata->normalize, FALSE, DEFAULT_NORMALIZE, NULL, NULL) );
13565  "constraints/"CONSHDLR_NAME"/presolpairwise",
13566  "should pairwise constraint comparison be performed in presolving?",
13567  &conshdlrdata->presolpairwise, TRUE, DEFAULT_PRESOLPAIRWISE, NULL, NULL) );
13569  "constraints/"CONSHDLR_NAME"/disjunctive", "extract disjunctive constraints?",
13570  &conshdlrdata->disjunctive, FALSE, DEFAULT_DISJUNCTIVE, NULL, NULL) );
13571 
13573  "constraints/"CONSHDLR_NAME"/maxnodes",
13574  "number of branch-and-bound nodes to solve an independent cumulative constraint (-1: no limit)?",
13575  &conshdlrdata->maxnodes, FALSE, DEFAULT_MAXNODES, -1LL, SCIP_LONGINT_MAX, NULL, NULL) );
13577  "constraints/"CONSHDLR_NAME"/detectdisjunctive", "search for conflict set via maximal cliques to detect disjunctive constraints",
13578  &conshdlrdata->detectdisjunctive, FALSE, DEFAULT_DETECTDISJUNCTIVE, NULL, NULL) );
13580  "constraints/"CONSHDLR_NAME"/detectvarbounds", "search for conflict set via maximal cliques to detect variable bound constraints",
13581  &conshdlrdata->detectvarbounds, FALSE, DEFAULT_DETECTVARBOUNDS, NULL, NULL) );
13582 
13583  /* conflict analysis parameters */
13585  "constraints/"CONSHDLR_NAME"/usebdwidening", "should bound widening be used during the conflict analysis?",
13586  &conshdlrdata->usebdwidening, FALSE, DEFAULT_USEBDWIDENING, NULL, NULL) );
13587 
13588  return SCIP_OKAY;
13589 }
13590 
13591 /** creates and captures a cumulative constraint */
13593  SCIP* scip, /**< SCIP data structure */
13594  SCIP_CONS** cons, /**< pointer to hold the created constraint */
13595  const char* name, /**< name of constraint */
13596  int nvars, /**< number of variables (jobs) */
13597  SCIP_VAR** vars, /**< array of integer variable which corresponds to starting times for a job */
13598  int* durations, /**< array containing corresponding durations */
13599  int* demands, /**< array containing corresponding demands */
13600  int capacity, /**< available cumulative capacity */
13601  SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP?
13602  * Usually set to TRUE. Set to FALSE for 'lazy constraints'. */
13603  SCIP_Bool separate, /**< should the constraint be separated during LP processing?
13604  * Usually set to TRUE. */
13605  SCIP_Bool enforce, /**< should the constraint be enforced during node processing?
13606  * TRUE for model constraints, FALSE for additional, redundant constraints. */
13607  SCIP_Bool check, /**< should the constraint be checked for feasibility?
13608  * TRUE for model constraints, FALSE for additional, redundant constraints. */
13609  SCIP_Bool propagate, /**< should the constraint be propagated during node processing?
13610  * Usually set to TRUE. */
13611  SCIP_Bool local, /**< is constraint only valid locally?
13612  * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */
13613  SCIP_Bool modifiable, /**< is constraint modifiable (subject to column generation)?
13614  * Usually set to FALSE. In column generation applications, set to TRUE if pricing
13615  * adds coefficients to this constraint. */
13616  SCIP_Bool dynamic, /**< is constraint subject to aging?
13617  * Usually set to FALSE. Set to TRUE for own cuts which
13618  * are seperated as constraints. */
13619  SCIP_Bool removable, /**< should the relaxation be removed from the LP due to aging or cleanup?
13620  * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */
13621  SCIP_Bool stickingatnode /**< should the constraint always be kept at the node where it was added, even
13622  * if it may be moved to a more global node?
13623  * Usually set to FALSE. Set to TRUE to for constraints that represent node data. */
13624  )
13625 {
13626  SCIP_CONSHDLR* conshdlr;
13627  SCIP_CONSDATA* consdata;
13628 
13629  assert(scip != NULL);
13630 
13631  /* find the cumulative constraint handler */
13632  conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
13633  if( conshdlr == NULL )
13634  {
13635  SCIPerrorMessage(""CONSHDLR_NAME" constraint handler not found\n");
13636  return SCIP_PLUGINNOTFOUND;
13637  }
13638 
13639  SCIPdebugMessage("create cumulative constraint <%s> with %d jobs\n", name, nvars);
13640 
13641  /* create constraint data */
13642  SCIP_CALL( consdataCreate(scip, &consdata, vars, NULL, durations, demands, nvars, capacity, 0, INT_MAX, check) );
13643 
13644  /* create constraint */
13645  SCIP_CALL( SCIPcreateCons(scip, cons, name, conshdlr, consdata,
13646  initial, separate, enforce, check, propagate,
13647  local, modifiable, dynamic, removable, stickingatnode) );
13648 
13649 
13650  if( SCIPgetStage(scip) != SCIP_STAGE_PROBLEM )
13651  {
13652  SCIP_CONSHDLRDATA* conshdlrdata;
13653 
13654  /* get event handler */
13655  conshdlrdata = SCIPconshdlrGetData(conshdlr);
13656  assert(conshdlrdata != NULL);
13657  assert(conshdlrdata->eventhdlr != NULL);
13658 
13659  /* catch bound change events of variables */
13660  SCIP_CALL( consdataCatchEvents(scip, consdata, conshdlrdata->eventhdlr) );
13661  }
13662 
13663  return SCIP_OKAY;
13664 }
13665 
13666 /** creates and captures a cumulative constraint
13667  * in its most basic version, i. e., all constraint flags are set to their basic value as explained for the
13668  * method SCIPcreateConsCumulative(); all flags can be set via SCIPsetConsFLAGNAME-methods in scip.h
13669  *
13670  * @see SCIPcreateConsCumulative() for information about the basic constraint flag configuration
13671  *
13672  * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
13673  */
13675  SCIP* scip, /**< SCIP data structure */
13676  SCIP_CONS** cons, /**< pointer to hold the created constraint */
13677  const char* name, /**< name of constraint */
13678  int nvars, /**< number of variables (jobs) */
13679  SCIP_VAR** vars, /**< array of integer variable which corresponds to starting times for a job */
13680  int* durations, /**< array containing corresponding durations */
13681  int* demands, /**< array containing corresponding demands */
13682  int capacity /**< available cumulative capacity */
13683  )
13684 {
13685  assert(scip != NULL);
13686 
13687  SCIP_CALL( SCIPcreateConsCumulative(scip, cons, name, nvars, vars, durations, demands, capacity,
13688  TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
13689 
13690  return SCIP_OKAY;
13691 }
13692 
13693 /** set the left bound of the time axis to be considered (including hmin) */
13695  SCIP* scip, /**< SCIP data structure */
13696  SCIP_CONS* cons, /**< constraint data */
13697  int hmin /**< left bound of time axis to be considered */
13698  )
13699 {
13700  SCIP_CONSDATA* consdata;
13701  if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
13702  {
13703  SCIPerrorMessage("constraint is not a cumulative constraint\n");
13704  return SCIP_INVALIDCALL;
13705  }
13706 
13707  consdata = SCIPconsGetData(cons);
13708  assert(consdata != NULL);
13709  assert(hmin >= 0);
13710  assert(hmin <= consdata->hmax);
13711 
13712  consdata->hmin = hmin;
13713 
13714  return SCIP_OKAY;
13715 }
13716 
13717 /** returns the left bound of the time axis to be considered */
13719  SCIP* scip, /**< SCIP data structure */
13720  SCIP_CONS* cons /**< constraint */
13721  )
13722 {
13723  SCIP_CONSDATA* consdata;
13724  if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
13725  {
13726  SCIPerrorMessage("constraint is not a cumulative constraint\n");
13727  SCIPABORT();
13728  return 0; /*lint !e527*/
13729  }
13730 
13731  consdata = SCIPconsGetData(cons);
13732  assert(consdata != NULL);
13733 
13734  return consdata->hmin;
13735 }
13736 
13737 /** set the right bound of the time axis to be considered (not including hmax) */
13739  SCIP* scip, /**< SCIP data structure */
13740  SCIP_CONS* cons, /**< constraint data */
13741  int hmax /**< right bound of time axis to be considered */
13742  )
13743 {
13744  SCIP_CONSDATA* consdata;
13745  if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
13746  {
13747  SCIPerrorMessage("constraint is not a cumulative constraint\n");
13748  SCIPABORT();
13749  return SCIP_INVALIDCALL; /*lint !e527*/
13750  }
13751 
13752  consdata = SCIPconsGetData(cons);
13753  assert(consdata != NULL);
13754  assert(hmax >= consdata->hmin);
13755 
13756  consdata->hmax = hmax;
13757 
13758  return SCIP_OKAY;
13759 }
13760 
13761 /** returns the right bound of the time axis to be considered */
13763  SCIP* scip, /**< SCIP data structure */
13764  SCIP_CONS* cons /**< constraint */
13765  )
13766 {
13767  SCIP_CONSDATA* consdata;
13768  if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
13769  {
13770  SCIPerrorMessage("constraint is not a cumulative constraint\n");
13771  SCIPABORT();
13772  return 0; /*lint !e527*/
13773  }
13774 
13775  consdata = SCIPconsGetData(cons);
13776  assert(consdata != NULL);
13777 
13778  return consdata->hmax;
13779 }
13780 
13781 /** returns the activities of the cumulative constraint */
13783  SCIP* scip, /**< SCIP data structure */
13784  SCIP_CONS* cons /**< constraint data */
13785  )
13786 {
13787  SCIP_CONSDATA* consdata;
13788 
13789  if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
13790  {
13791  SCIPerrorMessage("constraint is not a cumulative constraint\n");
13792  SCIPABORT();
13793  return NULL; /*lint !e527*/
13794  }
13795 
13796  consdata = SCIPconsGetData(cons);
13797  assert(consdata != NULL);
13798 
13799  return consdata->vars;
13800 }
13801 
13802 /** returns the activities of the cumulative constraint */
13804  SCIP* scip, /**< SCIP data structure */
13805  SCIP_CONS* cons /**< constraint data */
13806  )
13807 {
13808  SCIP_CONSDATA* consdata;
13809 
13810  if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
13811  {
13812  SCIPerrorMessage("constraint is not a cumulative constraint\n");
13813  SCIPABORT();
13814  return -1; /*lint !e527*/
13815  }
13816 
13817  consdata = SCIPconsGetData(cons);
13818  assert(consdata != NULL);
13819 
13820  return consdata->nvars;
13821 }
13822 
13823 /** returns the capacity of the cumulative constraint */
13825  SCIP* scip, /**< SCIP data structure */
13826  SCIP_CONS* cons /**< constraint data */
13827  )
13828 {
13829  SCIP_CONSDATA* consdata;
13830 
13831  if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
13832  {
13833  SCIPerrorMessage("constraint is not a cumulative constraint\n");
13834  SCIPABORT();
13835  return -1; /*lint !e527*/
13836  }
13837 
13838  consdata = SCIPconsGetData(cons);
13839  assert(consdata != NULL);
13840 
13841  return consdata->capacity;
13842 }
13843 
13844 /** returns the durations of the cumulative constraint */
13846  SCIP* scip, /**< SCIP data structure */
13847  SCIP_CONS* cons /**< constraint data */
13848  )
13849 {
13850  SCIP_CONSDATA* consdata;
13851 
13852  if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
13853  {
13854  SCIPerrorMessage("constraint is not a cumulative constraint\n");
13855  SCIPABORT();
13856  return NULL; /*lint !e527*/
13857  }
13858 
13859  consdata = SCIPconsGetData(cons);
13860  assert(consdata != NULL);
13861 
13862  return consdata->durations;
13863 }
13864 
13865 /** returns the demands of the cumulative constraint */
13867  SCIP* scip, /**< SCIP data structure */
13868  SCIP_CONS* cons /**< constraint data */
13869  )
13870 {
13871  SCIP_CONSDATA* consdata;
13872 
13873  if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
13874  {
13875  SCIPerrorMessage("constraint is not a cumulative constraint\n");
13876  SCIPABORT();
13877  return NULL; /*lint !e527*/
13878  }
13879 
13880  consdata = SCIPconsGetData(cons);
13881  assert(consdata != NULL);
13882 
13883  return consdata->demands;
13884 }
13885 
13886 /** check for the given starting time variables with their demands and durations if the cumulative conditions for the
13887  * given solution is satisfied
13888  */
13890  SCIP* scip, /**< SCIP data structure */
13891  SCIP_SOL* sol, /**< primal solution, or NULL for current LP/pseudo solution */
13892  int nvars, /**< number of variables (jobs) */
13893  SCIP_VAR** vars, /**< array of integer variable which corresponds to starting times for a job */
13894  int* durations, /**< array containing corresponding durations */
13895  int* demands, /**< array containing corresponding demands */
13896  int capacity, /**< available cumulative capacity */
13897  int hmin, /**< left bound of time axis to be considered (including hmin) */
13898  int hmax, /**< right bound of time axis to be considered (not including hmax) */
13899  SCIP_Bool* violated, /**< pointer to store if the cumulative condition is violated */
13900  SCIP_CONS* cons, /**< constraint which is checked */
13901  SCIP_Bool printreason /**< should the reason for the violation be printed? */
13902  )
13903 {
13904  assert(scip != NULL);
13905  assert(violated != NULL);
13906 
13907  SCIP_CALL( checkCumulativeCondition(scip, sol, nvars, vars, durations, demands, capacity, hmin, hmax,
13908  violated, cons, printreason) );
13909 
13910  return SCIP_OKAY;
13911 }
13912 
13913 /** normalize cumulative condition */
13915  SCIP* scip, /**< SCIP data structure */
13916  int nvars, /**< number of start time variables (activities) */
13917  SCIP_VAR** vars, /**< array of start time variables */
13918  int* durations, /**< array of durations */
13919  int* demands, /**< array of demands */
13920  int* capacity, /**< pointer to store the changed cumulative capacity */
13921  int* nchgcoefs, /**< pointer to count total number of changed coefficients */
13922  int* nchgsides /**< pointer to count number of side changes */
13923  )
13924 {
13925  SCIP_CALL( normalizeCumulativeCondition(scip, nvars, vars, durations, demands, capacity,
13926  nchgcoefs, nchgsides) );
13927 
13928  return SCIP_OKAY;
13929 }
13930 
13931 /** searches for a time point within the cumulative condition were the cumulative condition can be split */
13933  SCIP* scip, /**< SCIP data structure */
13934  int nvars, /**< number of variables (jobs) */
13935  SCIP_VAR** vars, /**< array of integer variable which corresponds to starting times for a job */
13936  int* durations, /**< array containing corresponding durations */
13937  int* demands, /**< array containing corresponding demands */
13938  int capacity, /**< available cumulative capacity */
13939  int* hmin, /**< pointer to store the left bound of the effective horizon */
13940  int* hmax, /**< pointer to store the right bound of the effective horizon */
13941  int* split /**< point were the cumulative condition can be split */
13942  )
13943 {
13944  SCIP_CALL( computeEffectiveHorizonCumulativeCondition(scip, nvars, vars, durations, demands, capacity,
13945  hmin, hmax, split) );
13946 
13947  return SCIP_OKAY;
13948 }
13949 
13950 /** presolve cumulative condition w.r.t. effective horizon by detecting irrelevant variables */
13952  SCIP* scip, /**< SCIP data structure */
13953  int nvars, /**< number of start time variables (activities) */
13954  SCIP_VAR** vars, /**< array of start time variables */
13955  int* durations, /**< array of durations */
13956  int hmin, /**< left bound of time axis to be considered */
13957  int hmax, /**< right bound of time axis to be considered (not including hmax) */
13958  SCIP_Bool* downlocks, /**< array storing if the variable has a down lock, or NULL */
13959  SCIP_Bool* uplocks, /**< array storing if the variable has an up lock, or NULL */
13960  SCIP_CONS* cons, /**< constraint which gets propagated, or NULL */
13961  SCIP_Bool* irrelevants, /**< array mark those variables which are irrelevant for the cumulative condition */
13962  int* nfixedvars, /**< pointer to store the number of fixed variables */
13963  int* nchgsides, /**< pointer to store the number of changed sides */
13964  SCIP_Bool* cutoff /**< buffer to store whether a cutoff is detected */
13965  )
13966 {
13967  if( nvars <= 1 )
13968  return SCIP_OKAY;
13969 
13970  /* presolve constraint form the earlier start time point of view */
13971  SCIP_CALL( presolveConsEst(scip, nvars, vars, durations, hmin, hmax, downlocks, uplocks, cons,
13972  irrelevants, nfixedvars, nchgsides, cutoff) );
13973 
13974  /* presolve constraint form the latest completion time point of view */
13975  SCIP_CALL( presolveConsLct(scip, nvars, vars, durations, hmin, hmax, downlocks, uplocks, cons,
13976  irrelevants, nfixedvars, nchgsides, cutoff) );
13977 
13978  return SCIP_OKAY;
13979 }
13980 
13981 /** propagate the given cumulative condition */
13983  SCIP* scip, /**< SCIP data structure */
13984  int nvars, /**< number of variables (jobs) */
13985  SCIP_VAR** vars, /**< array of integer variable which corresponds to starting times for a job */
13986  int* durations, /**< array containing corresponding durations */
13987  int* demands, /**< array containing corresponding demands */
13988  int capacity, /**< available cumulative capacity */
13989  int hmin, /**< left bound of time axis to be considered (including hmin) */
13990  int hmax, /**< right bound of time axis to be considered (not including hmax) */
13991  SCIP_CONS* cons, /**< constraint which gets propagated */
13992  int* nchgbds, /**< pointer to store the number of variable bound changes */
13993  SCIP_Bool* initialized, /**< was conflict analysis initialized */
13994  SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */
13995  SCIP_Bool* cutoff /**< pointer to store if the cumulative condition is violated */
13996  )
13997 {
13998  SCIP_CONSHDLR* conshdlr;
13999  SCIP_CONSHDLRDATA* conshdlrdata;
14000  SCIP_Bool redundant;
14001 
14002  assert(scip != NULL);
14003  assert(cons != NULL);
14004  assert(initialized != NULL);
14005  assert(*initialized == FALSE);
14006  assert(cutoff != NULL);
14007  assert(*cutoff == FALSE);
14008 
14009  /* find the cumulative constraint handler */
14010  conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
14011  if( conshdlr == NULL )
14012  {
14013  SCIPerrorMessage(""CONSHDLR_NAME" constraint handler not found\n");
14014  return SCIP_PLUGINNOTFOUND;
14015  }
14016 
14017  conshdlrdata = SCIPconshdlrGetData(conshdlr);
14018  assert(conshdlrdata != NULL);
14019 
14020  redundant = FALSE;
14021 
14022  SCIP_CALL( propagateCumulativeCondition(scip, conshdlrdata,
14023  nvars, vars, durations, demands, capacity, hmin, hmax, cons,
14024  nchgbds, &redundant, initialized, explanation, cutoff) );
14025 
14026  return SCIP_OKAY;
14027 }
14028 
14029 /** resolve propagation w.r.t. the cumulative condition */
14031  SCIP* scip, /**< SCIP data structure */
14032  int nvars, /**< number of start time variables (activities) */
14033  SCIP_VAR** vars, /**< array of start time variables */
14034  int* durations, /**< array of durations */
14035  int* demands, /**< array of demands */
14036  int capacity, /**< cumulative capacity */
14037  int hmin, /**< left bound of time axis to be considered (including hmin) */
14038  int hmax, /**< right bound of time axis to be considered (not including hmax) */
14039  SCIP_VAR* infervar, /**< the conflict variable whose bound change has to be resolved */
14040  int inferinfo, /**< the user information */
14041  SCIP_BOUNDTYPE boundtype, /**< the type of the changed bound (lower or upper bound) */
14042  SCIP_BDCHGIDX* bdchgidx, /**< the index of the bound change, representing the point of time where the change took place */
14043  SCIP_Real relaxedbd, /**< the relaxed bound which is sufficient to be explained */
14044  SCIP_Bool* explanation, /**< bool array which marks the variable which are part of the explanation if a cutoff was detected, or NULL */
14045  SCIP_RESULT* result /**< pointer to store the result of the propagation conflict resolving call */
14046  )
14047 {
14048  SCIP_CALL( respropCumulativeCondition(scip, nvars, vars, durations, demands, capacity, hmin, hmax,
14049  infervar, intToInferInfo(inferinfo), boundtype, bdchgidx, relaxedbd, TRUE, explanation, result) );
14050 
14051  return SCIP_OKAY;
14052 }
14053 
14054 /** this method visualizes the cumulative structure in GML format */
14056  SCIP* scip, /**< SCIP data structure */
14057  SCIP_CONS* cons /**< cumulative constraint */
14058  )
14059 {
14060  SCIP_CONSDATA* consdata;
14061  SCIP_HASHTABLE* vars;
14062  FILE* file;
14063  SCIP_VAR* var;
14064  char filename[SCIP_MAXSTRLEN];
14065  int nvars;
14066  int v;
14067 
14068  /* open file */
14069  (void)SCIPsnprintf(filename, SCIP_MAXSTRLEN, "%s.gml", SCIPconsGetName(cons));
14070  file = fopen(filename, "w");
14071 
14072  /* check if the file was open */
14073  if( file == NULL )
14074  {
14075  SCIPerrorMessage("cannot create file <%s> for writing\n", filename);
14076  SCIPprintSysError(filename);
14077  return SCIP_FILECREATEERROR;
14078  }
14079 
14080  consdata = SCIPconsGetData(cons);
14081  assert(consdata != NULL);
14082 
14083  nvars = consdata->nvars;
14084 
14086  SCIPvarGetHashkey, SCIPvarIsHashkeyEq, SCIPvarGetHashkeyVal, NULL) );
14087 
14088  /* create opening of the GML format */
14089  SCIPgmlWriteOpening(file, TRUE);
14090 
14091  for( v = 0; v < nvars; ++v )
14092  {
14093  char color[SCIP_MAXSTRLEN];
14094 
14095  var = consdata->vars[v];
14096  assert(var != NULL);
14097 
14098  SCIP_CALL( SCIPhashtableInsert(vars, (void*)var) );
14099 
14100  if( SCIPvarGetUbGlobal(var) - SCIPvarGetLbGlobal(var) < 0.5 )
14101  (void)SCIPsnprintf(color, SCIP_MAXSTRLEN, "%s", "#0000ff");
14102  else if( !consdata->downlocks[v] || !consdata->uplocks[v] )
14103  (void)SCIPsnprintf(color, SCIP_MAXSTRLEN, "%s", "#00ff00");
14104  else
14105  (void)SCIPsnprintf(color, SCIP_MAXSTRLEN, "%s", "#ff0000");
14106 
14107  SCIPgmlWriteNode(file, (unsigned int)(size_t)var, SCIPvarGetName(var), "rectangle", color, NULL);
14108  }
14109 
14110  for( v = 0; v < nvars; ++v )
14111  {
14112  SCIP_VAR** vbdvars;
14113  int nvbdvars;
14114  int b;
14115 
14116  var = consdata->vars[v];
14117  assert(var != NULL);
14118 
14119  vbdvars = SCIPvarGetVlbVars(var);
14120  nvbdvars = SCIPvarGetNVlbs(var);
14121 
14122  for( b = 0; b < nvbdvars; ++b )
14123  {
14124  if( SCIPhashtableExists(vars, (void*)vbdvars[b]) )
14125  {
14126  SCIPgmlWriteArc(file, (unsigned int)(size_t)vbdvars[b], (unsigned int)(size_t)var, NULL, NULL);
14127  }
14128  }
14129 
14130 #if 0
14131  vbdvars = SCIPvarGetVubVars(var);
14132  nvbdvars = SCIPvarGetNVubs(var);
14133 
14134  for( b = 0; b < nvbdvars; ++b )
14135  {
14136  if( SCIPhashtableExists(vars, vbdvars[b]) )
14137  {
14138  SCIPgmlWriteArc(file, (unsigned int)(size_t)var, (unsigned int)(size_t)vbdvars[b], NULL, NULL);
14139  }
14140  }
14141 #endif
14142  }
14143 
14144  /* create closing of the GML format */
14145  SCIPgmlWriteClosing(file);
14146 
14147  /* close file */
14148  fclose(file);
14149 
14150  SCIPhashtableFree(&vars);
14151 
14152  return SCIP_OKAY;
14153 }
14154 
14155 /** sets method to solve an individual cumulative condition */
14157  SCIP* scip, /**< SCIP data structure */
14158  SCIP_DECL_SOLVECUMULATIVE((*solveCumulative)) /**< method to use an individual cumulative condition */
14159  )
14160 {
14161  SCIP_CONSHDLR* conshdlr;
14162  SCIP_CONSHDLRDATA* conshdlrdata;
14163 
14164  /* find the cumulative constraint handler */
14165  conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
14166  if( conshdlr == NULL )
14167  {
14168  SCIPerrorMessage(""CONSHDLR_NAME" constraint handler not found\n");
14169  return SCIP_PLUGINNOTFOUND;
14170  }
14171 
14172  conshdlrdata = SCIPconshdlrGetData(conshdlr);
14173  assert(conshdlrdata != NULL);
14174 
14175  conshdlrdata->solveCumulative = solveCumulative;
14176 
14177  return SCIP_OKAY;
14178 }
14179 
14180 /** solves given cumulative condition as independent sub problem
14181  *
14182  * @note If the problem was solved to the earliest start times (ests) and latest start times (lsts) array contain the
14183  * solution values; If the problem was not solved these two arrays contain the global bounds at the time the sub
14184  * solver was interrupted.
14185  */
14187  SCIP* scip, /**< SCIP data structure */
14188  int njobs, /**< number of jobs (activities) */
14189  SCIP_Real* ests, /**< array with the earlier start time for each job */
14190  SCIP_Real* lsts, /**< array with the latest start time for each job */
14191  SCIP_Real* objvals, /**< array of objective coefficients for each job (linear objective function), or NULL if none */
14192  int* durations, /**< array of durations */
14193  int* demands, /**< array of demands */
14194  int capacity, /**< cumulative capacity */
14195  int hmin, /**< left bound of time axis to be considered (including hmin) */
14196  int hmax, /**< right bound of time axis to be considered (not including hmax) */
14197  SCIP_Real timelimit, /**< time limit for solving in seconds */
14198  SCIP_Real memorylimit, /**< memory limit for solving in mega bytes (MB) */
14199  SCIP_Longint maxnodes, /**< maximum number of branch-and-bound nodes to solve the single cumulative constraint (-1: no limit) */
14200  SCIP_Bool* solved, /**< pointer to store if the problem is solved (to optimality) */
14201  SCIP_Bool* infeasible, /**< pointer to store if the problem is infeasible */
14202  SCIP_Bool* unbounded, /**< pointer to store if the problem is unbounded */
14203  SCIP_Bool* error /**< pointer to store if an error occurred */
14204  )
14205 {
14206  SCIP_CONSHDLR* conshdlr;
14207  SCIP_CONSHDLRDATA* conshdlrdata;
14208 
14209  (*solved) = TRUE;
14210  (*infeasible) = FALSE;
14211  (*unbounded) = FALSE;
14212  (*error) = FALSE;
14213 
14214  if( njobs == 0 )
14215  return SCIP_OKAY;
14216 
14217  /* find the cumulative constraint handler */
14218  conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
14219  if( conshdlr == NULL )
14220  {
14221  SCIPerrorMessage(""CONSHDLR_NAME" constraint handler not found\n");
14222  (*error) = TRUE;
14223  return SCIP_PLUGINNOTFOUND;
14224  }
14225 
14226  conshdlrdata = SCIPconshdlrGetData(conshdlr);
14227  assert(conshdlrdata != NULL);
14228 
14229  /* abort if no time is left or not enough memory to create a copy of SCIP, including external memory usage */
14230  if( timelimit > 0.0 && memorylimit > 10 )
14231  {
14232  SCIP_CALL( conshdlrdata->solveCumulative(njobs, ests, lsts, objvals, durations, demands, capacity,
14233  hmin, hmax, timelimit, memorylimit, maxnodes, solved, infeasible, unbounded, error) );
14234  }
14235 
14236  return SCIP_OKAY;
14237 }
14238 
14239 /** creates the worst case resource profile, that is, all jobs are inserted with the earliest start and latest
14240  * completion time
14241  */
14243  SCIP* scip, /**< SCIP data structure */
14244  SCIP_PROFILE* profile, /**< resource profile */
14245  int nvars, /**< number of variables (jobs) */
14246  SCIP_VAR** vars, /**< array of integer variable which corresponds to starting times for a job */
14247  int* durations, /**< array containing corresponding durations */
14248  int* demands /**< array containing corresponding demands */
14249  )
14250 {
14251  SCIP_VAR* var;
14252  SCIP_HASHMAP* addedvars;
14253  int* copydemands;
14254  int* perm;
14255  int duration;
14256  int impliedest;
14257  int est;
14258  int impliedlct;
14259  int lct;
14260  int v;
14261 
14262  /* create hash map for variables which are added, mapping to their duration */
14263  SCIP_CALL( SCIPhashmapCreate(&addedvars, SCIPblkmem(scip), SCIPcalcHashtableSize(nvars)) );
14264 
14265  SCIP_CALL( SCIPallocBufferArray(scip, &perm, nvars) );
14266  SCIP_CALL( SCIPallocBufferArray(scip, &copydemands, nvars) );
14267 
14268  /* sort variables w.r.t. job demands */
14269  for( v = 0; v < nvars; ++v )
14270  {
14271  copydemands[v] = demands[v];
14272  perm[v] = v;
14273  }
14274  SCIPsortDownIntInt(copydemands, perm, nvars);
14275 
14276  /* add each job with its earliest start and latest completion time into the resource profile */
14277  for( v = 0; v < nvars; ++v )
14278  {
14279  int idx;
14280 
14281  idx = perm[v];
14282  assert(idx >= 0 && idx < nvars);
14283 
14284  var = vars[idx];
14285  assert(var != NULL);
14286 
14287  duration = durations[idx];
14288  assert(duration > 0);
14289 
14290  est = convertBoundToInt(scip, SCIPvarGetLbLocal(var));
14291  SCIP_CALL( computeImpliedEst(scip, var, addedvars, &impliedest) );
14292 
14293  lct = convertBoundToInt(scip, SCIPvarGetUbLocal(var)) + duration;
14294  SCIP_CALL( computeImpliedLct(scip, var, duration, addedvars, &impliedlct) );
14295 
14296  if( impliedest < impliedlct )
14297  {
14298  SCIP_Bool infeasible;
14299  int pos;
14300 
14301  SCIP_CALL( SCIPprofileInsertCore(profile, impliedest, impliedlct, copydemands[v], &pos, &infeasible) );
14302  assert(!infeasible);
14303  assert(pos == -1);
14304  }
14305 
14306  if( est == impliedest && lct == impliedlct )
14307  {
14308  SCIP_CALL( SCIPhashmapInsert(addedvars, (void*)var, (void*)(size_t)duration) );
14309  }
14310  }
14311 
14312  SCIPfreeBufferArray(scip, &copydemands);
14313  SCIPfreeBufferArray(scip, &perm);
14314 
14315  SCIPhashmapFree(&addedvars);
14316 
14317  return SCIP_OKAY;
14318 }
14319 
14320 /** computes w.r.t. the given worst case resource profile the first time point where the given capacity can be violated */
14322  SCIP* scip, /**< SCIP data structure */
14323  SCIP_PROFILE* profile, /**< worst case resource profile */
14324  int capacity /**< capacity to check */
14325  )
14326 {
14327  int* timepoints;
14328  int* loads;
14329  int ntimepoints;
14330  int t;
14331 
14332  ntimepoints = SCIPprofileGetNTimepoints(profile);
14333  timepoints = SCIPprofileGetTimepoints(profile);
14334  loads = SCIPprofileGetLoads(profile);
14335 
14336  /* find first time point which potentially violates the capacity restriction */
14337  for( t = 0; t < ntimepoints - 1; ++t )
14338  {
14339  /* check if the time point exceed w.r.t. worst case profile the capacity */
14340  if( loads[t] > capacity )
14341  {
14342  assert(t == 0 || loads[t-1] <= capacity);
14343  return timepoints[t];
14344  }
14345  }
14346 
14347  return INT_MAX;
14348 }
14349 
14350 /** computes w.r.t. the given worst case resource profile the first time point where the given capacity is satisfied for sure */
14352  SCIP* scip, /**< SCIP data structure */
14353  SCIP_PROFILE* profile, /**< worst case profile */
14354  int capacity /**< capacity to check */
14355  )
14356 {
14357  int* timepoints;
14358  int* loads;
14359  int ntimepoints;
14360  int t;
14361 
14362  ntimepoints = SCIPprofileGetNTimepoints(profile);
14363  timepoints = SCIPprofileGetTimepoints(profile);
14364  loads = SCIPprofileGetLoads(profile);
14365 
14366  /* find last time point which potentially violates the capacity restriction */
14367  for( t = ntimepoints - 1; t >= 0; --t )
14368  {
14369  /* check if at time point t the worst case resource profile exceeds the capacity */
14370  if( loads[t] > capacity )
14371  {
14372  assert(t == ntimepoints-1 || loads[t+1] <= capacity);
14373  return timepoints[t+1];
14374  }
14375  }
14376 
14377  return INT_MIN;
14378 }
14379 
14380 /**@} */
14381