Originally posted by: SystemAdmin
[phlab said:]
Optional interval variables can be used to capture the intervals of times during which at least one task is executed and intervals of time during which no task executes.
Given a set of tasks represented as interval variables A[i in 1..n], a chain of intervals W[j] that exactly cover the set of points where at least one task A executes can be defined as follows:
1. int n=...;
2. dvar interval A[i in 1..n] ...;
3. cumulFunction CA = sum(i in 1..n) pulse(A[i],1);
4. dvar interval W[i in 1..n] optional in 1..horizon; // Some work being done by some A[j]
5. cumulFunction CW = sum(i in 1..n) pulse(W[i],1);
6. constraints {
7. forall(i in 1..n-1) {
8. endBeforeStart(W[i],W[i+1],1);
9. presenceOf(W[i+1]) => presenceOf(W[i]);
10. }
11. forall(i in 1..n) {
12. alwaysIn(CA, W[i], 1, n);
13. alwaysIn(CW, A[i], 1, n);
14. }
15. };
Rationale:
Line 3: the cumul function CA counts the number of tasks A executing at any time t
Lines 4 and 7..10: W[i in 1..n] is a chain of optional intervals (to break symmetries, only the first k intervals will be present, indirectly, k is a decision variable of the problem).
Line 4: the minimal size of 1 for intervals W (when they are present) also allows breaking some symmetries: if an interval W is present, it must represent a non null interval of time where some task execute
Line 8: the minimal delay of 1 in the chain ensure that a block of tasks executing without interruption will be covered by exactly one interval W
Line 5: the cumul function CW counts the number of intervals W executing at any time t (it can be either 0 or 1 as the intervals form a chain)
Line 12: during the execution of a present interval W there must be at least one task A executed (that is: 1<=CA<=n)<br />Line 13: during the execution of a task A there must be at least one interval W present (that is: 1<=CW<=n)<br />
One can easily extend this model to also explicit the intervals of time during which no task executes by using an interval in between the intervals W:
1. int n=...;
2. dvar interval A[i in 1..n] ...;
3. cumulFunction CA = sum(i in 1..n) pulse(A[i],1);
4. dvar interval W[i in 1..n] optional in 1..horizon; // Work
5. dvar interval M[i in 1..n-1] optional in 1..horizon; // Maintenance
6. cumulFunction CW = sum(i in 1..n) pulse(W[i],1);
7. constraints {
8. forall(i in 1..n-1) {
9. endAtStart(W[i],M[i]);
10. endAtStart(M[i],W[i+1]);
11. presenceOf(M[i]) => presenceOf(W[i]);
12. presenceOf(W[i+1]) => presenceOf(M[i]);
13. }
14. forall(i in 1..n) {
15. alwaysIn(CA, W[i], 1, n);
16. alwaysIn(CW, A[i], 1, n);
17. }
18. };
Using these interval variables W and M you can avoid explicitly enumerating time.
Here is a small example that specifies that one cannot work more than 10 units without a maintenance period that will last 10 units. In this pedagogical example, tasks A[i in 1..n] are supposed to have a processing time i and to require (n+1-i) units of a cumulative resource of capacity n.
using CP;
int n=10;
dvar interval A[i in 1..n] size i;
cumulFunction Res = sum(i in 1..n) pulse(A[i], n+1-i); // Constraints on activities
cumulFunction CA = sum(i in 1..n) pulse(A[i],1);
dvar interval W[i in 1..n] optional size 1..10; // Work
dvar interval M[i in 1..n-1] optional size 10; // Maintenance
cumulFunction CW = sum(i in 1..n) pulse(W[i],1);
minimize max(i in 1..n) endOf(A[i]);
constraints {
forall(i in 1..n-1) {
endAtStart(W[i],M[i]);
endAtStart(M[i],W[i+1]);
presenceOf(M[i]) => presenceOf(W[i]);
presenceOf(W[i+1]) => presenceOf(M[i]);
}
forall(i in 1..n) {
alwaysIn(CA, W[i], 1, n);
alwaysIn(CW, A[i], 1, n);
}
Res <= n;<br />};
#DecisionOptimization#OPLusingCPOptimizer