❮❮❮
.NET instability
❮❮❮
❯❯❯
Workout, yoga and PMR
❯❯❯
Reading Notes: Comparison of MILP and CP models for balancing partially automated assembly lines
Reading notes, links and models.
Reading time: 15 min.
Metadata
Dimény, Imre; Koltai, Tamás Comparison of MILP and CP models for balancing partially automated assembly lines. Cent. Eur. J. Oper. Res. 32, No. 4, 945-959 (2024).
Authors:
- corresponding: Imre Dimény
dimeny.imre@gtk.bme.hu
- research group
- hp koltai
koltai.tamas@gtk.bme.hu
Summary
- No code published
- Constraint (6) seems wrong, makes model 1 not specialisation of model 2
- Constraint (15) is wrong as written
- Constraint (3) could be disaggregated
- Execution precedence variable could be replaced by something deterministic
- Constraint (4) could be a cut in relaxation of model 3
- Precedence data has strong impact on benchmarking
Questions
- graph structure of precedence data
- loops
- acyclic
- transitive closure
- errors
- typos and missing indices in constraints
- constraint (6) and counterexample
- constraint (15)
- benchmarking:
- faster MILP than CPLEX: HigHs, Gurobi, …
- shape of precedence data: transitive closure or not
- constraint (4) as cut?
- open implementation
- sourcecode
- data files
Notes
Glossary
- ALB = Assembly line balancing
- scheduling = execution (see def of $y$)
- allocation: task <-> station, station <-> workforce type
Table 1: Notation
- Parameter $N^\star$ is defined by nowhere used or explained
- Parameter $P_i$ defines a directed graph on $\{1, \dotsc, I\}$.
- It may contain loops => bad
- It may not be acyclic (i.e, no DAG) => worse => model may be infeasible
- It does not have the requirement “single-sink-for-each-source”:
- initial task = without precedent tasks
- final task (without antecedent tasks)
- every initial task leads to only one final task
- Set $WA_i$ and variables $xe_i$, $ue_j$, $a_i$, $a_{ijw}$ and $r_{jw}$ are exclusive to the CP formulation
- Set $NA_w$: typo “resource type” => “workforce type”
- Decision variables: MILP and CP variables are mutually distinct but here intermingled
- MILP: $x$, $y$, $u$, $s$, $e$, $N$
- CP: $xe$, $ue$, $a$ in two forms :-(, $r$, $N$
- Variables $s_i$ and $e_i$: should be in $[0, J T_c]$
Inconsistent notation for multiplication
- uses $*$ in (6) and $\cdot$ in (9) for multiplication
- see also (26) vs (29)
Constraint (4)
- this is $j(i) := \sum_{jw} x_{ijw} \ge{} j(k)$
- this is an aggregated form
- disaggregated: $\forall{}j: \sum_{j’\le{}j, w} x_{kjw} \le{} \sum_{j’\le{}j, w} x_{ijw}$
Constraint (6)
- $\forall{}i,w:\quad{}N\ge\sum_l j * x_{ljw}$
- easier readable: $\forall{}i,w:\quad{}N\ge j\sum_l x_{ljw}$
- see subsection “Problems with constraint (6)”
- easier readable: $\forall{}i,w:\quad{}N\ge j\sum_l x_{ljw}$
Index errors in constraints
- Constraints (9) and (10): index should range over all $(j, w)$ and not $(i, w)$
- Constraint (12): index should range over all $j$
Time constraints as variable bounds
- Constraints (16) is two sets of constraints
- Constraints (16) and (18) could be included in variable domains
- Error in explanation of (16): $s$ and $e$ are non-negative, not positive
- (4) in models 1 and 2 is replaced by (16)-(18) in model 3
- but (4) could be useful as a cut in model 3
Execution precedence constraints
- (13): no self-precedence
- (14): execution-precedence between all pairs
- (13) and (14) render $y$ a matrix with $I(I-1)/2$ entries of $1$
- there should be a fixed assignment: $k\in{}P_i$ => $y_{ki}=1$ and $y_{ik}=0$
The problem with constraint (15)
- (15): different stations allocated or no precedence => execution precedence defined by station indices
- this is a conditional constraint
- if, for fixed station $j$, $k$ is scheduled there and $i$ before, then $y_{ij}=1$
- $k$ scheduled <=> $\sum_w x_{kjw} = 1$
- $i$ scheduled before <=> $\sum_{j_1=1}^{j-1} \sum_w x_{ij_1w} = 1$
- sum of above lhs equals $2$ => $y_{ij}=1$
- yields $y_{ij}\ge{}(\sum_{j_1=1}^{j-1} \sum_w x_{ij_1w}) + (\sum_w x_{kjw}) - 1$
Precedence information transitiveness
- definition: $P$ is transitive, iff $i\in{}P_k$ and $k\in{}P_{k_2}$, then $i\in{}P_{k_2}$
- why not just leave (15) out?
- is not just an ordering by $t$ better?
- i.e., $y_{ij}=[t_i > t_j]$ (Iverson notation) with a random choice in case of a tie
- it makes sense to schedule longer tasks first
- but does this always work?
- does not work, if $P$ is not transitive => collides with (21)
- but (3) ensures that along precedence chains everything works
- information based on topological ordering alone is not enough, either
- Big question:
- $P$ not transitive => minimal as DAG => lots of (14), (15), (20) and (21), but less (19)
- $P$ transitively closed => less of (14), (15), (20) and (21), but more (19)
- which one is better?
Conditional scheduling constraints
- for fixed station $j$: $k$ preceeds $i$ and both scheduled at $j$
- => $e_k\le{}_i$ (irrespective of workforce type)
- $i,k$ scheduled at $j$ <=> $S:=\sum_{w} x_{ijw} + x{kjw} = 2$ (lhs in $\{0, 1, 2\}$)
- $S=2 \Leftrightarrow{} s_i \ge{} e_k$
- $S<2$: no constraint
- upper bound (big M) for $e_k$ (as by (16) $s_i\ge{}0$) is $T_c$
- hence (19): $s_i \ge{} e_k - T_c (2 - \sum_{w} (x_{ijw} + x{kjw}))$
- for fixed station $j$ :
- $k$ and $i$ have no precedence between them: $i\not\in{}P_k$, $k\not\in{}P_i$
- want to respect scheduling order $y_{ik}$
- for workforce type $w$ => scheduled task may not overlap
- $y_{ik}=x_{ijw}=x_{kjw}=1 \Leftrightarrow{} s_i \ge{} e_k$
- sum has values in $\{0, 1, 2, 3\}$
- upper bound (big M) for $e_k$ is $T_c$
- hence (20): $s_i \ge{} e_k - T_c (3 - y_{ik} - x_{ijw} - x_{kjw})$
- between different workforce types: starts must be ordered
- $y_{ik}=\sum_w x_{ijw}=\sum_w x_{kjw}=1 \Leftrightarrow{} s_i \ge{} e_k$
- sum has values in $\{0, 1, 2, 3\}$
- upper bound (big M) for $s_k$ is $T_c$
- hence (21): $s_i \ge{} s_k - T_c (3 - y_{ik} - \sum_w x_{ijw} - \sum_w x_{kjw} x_{kjw})$
Station allocation of workforce types in model 3
- in models 1 and 2 not all all stations had to be allocated a workforce
- in model 3 constraint (12) forces a workforce allocation to all stations
- so in order to minimize $N$ in (11), we have to assume “free” robots for everything else
Missing checks on parameters
- $t_{ij}\le{}T_c$
- $N^\star\le{}J$
- checks on graph defined by $P$
- $\sum_{i} t_{iH}\le{}T_c J$
- assuming that robot processing times are higher, see paragraph 3 of section 4
- that means: $t_{iH}\le{}t_{iR}$
- check fails => model infeasible
Problems with constraint (6)
Parameters
- jobs:
- $I=3$
- $NA_R=\{1, 2, 3\}$ and $NA_H=\emptyset$
- precedence $P$: $P_1=\emptyset$, $P_2=P_3=\{1\})$
- induced graph structure: 1 -> 2, 1 -> 3
- this is a DAG, but violates “single-sink-for-each-source”
- stations: $J=2$
- times: $T_c = 2$, $t_{iw}=1$ if $i\in\{2, 3\}$ and $w=H$, $t_{iw}=2$ if $t_{iw}=2$, and $t_{iw}=0$ else
Optimal solution with model 1
- total work time $\sum_{i} t_{iH} = 4$
- constraint (4) implies: $J\ge{}4/T_c=2$ or infeasible
- tasks 2 and 3 can never be scheduled at station 1
- hence optimal solution $x_{1,1,H}=x_{2,2,H}=x_{3,2,H}=1$
- constraint (6) has $N\ge{} 2(x_{2,2,H}+x_{3,2,H})=4$
- Note that $N\le{}J$ (spelled out in model 2 in (8))
- hence: 4 workers for 2 stations … this clashes with the formulation in model 2
Optimal solution with model 2
- Extra data
- $u_{1H}=u_{2H}=1$, $u_{1R}=u_{2R} =0$
- hence (11) gives $N=u_{1H}+u_{2H}=2$
Possible fixes
- Require a single final task $f$
- Task $f$ is always assigned to the highest numbered station among scheduled stations
- (6) turns into: $N\ge{}l(f)$ (the station number of task $f$) or $N\ge{}0$ (all other tasks)
- Data adaption if there are several final tasks in data
- add a special task $i^\star$ with time $t_{i^\star, w}=0$
- this task is for free and does not change the model
- previous final tasks are precedent to this task => $i^\star$ is the sole final task $f$
- Use the formulation of model 2
Examples of execution precedence
Scenario 1
- jobs:
- $I=3$
- $NA_R=\{1, 2, 3\}$ and $NA_H=\emptyset$ => no robots
- precedence $P$: $P_1=\emptyset$, $P_2=\{1\}$, $P_3=\{2\}$
- resulting graph structure: 1 -> 2 -> 3
- stations: $J=1$
- times:
- $T_c = 3$
- $t_{iw}=1$ always
- Optimal solution with model 3
- everything has to be scheduled within the single station
- so it is a pure scheduling problem without any allocation
- $x_i:=x_{i1H}=1$ is optimal solution
- (16)-(19) => $0=s_1$, $e_1=1=s_2$, $e_2=2=s_3$, $e_4=3$
- (13)-(15):
- (14) $y_{ii}=0$
- (15) only triggers for $y_{13}$ and $y_{31}$ => no constraint
- everything has to be scheduled within the single station
Scenario 2
- jobs:
- $I=3$
- $NA_R=\{1, 2, 3\}$ and $NA_H=\emptyset$ => no robots
- precedence $P$: $P_i=\emptyset$
- resulting graph structure: 1, 2, 3 (isolated vertices)
- stations: $J=1$
- times:
- $T_c = 3$
- $t_{iw}=1$ always
- Optimal solution with model 3
- everything has to be scheduled within the single station
- so it is a pure scheduling problem without any allocation
- $x_i:=x_{i1H}=1$ is optimal solution
- any choice of order on $[3]$ yields values for $y$
- wlog: $y_{12}=y_{23}=y_{13}=1$
- (19) does not trigger
- (20) yields:
- $s_1\ge{}e_2 - T_c$ => no constraint
- $s_2\ge{}e_1$
- $s_1\ge{}e_3 - T_c$ => no constraint
- $s_3\ge{}s_1$
- $s_2\ge{}e_3 - T_c$ => no constraint
- $s_3\ge{}s_2$
- this yields $0=s_1$, $e_1=1=s_2$, $e_2=2=s_3$, $e_4=3$
- everything has to be scheduled within the single station
Models
In ASCI-printable Symplex notation.
Model MILP1 as in paper
- as close as possibly translated
local W := {H, R}
index w on W
input I in {0, 1, ...}
index i on I
index k on I
input J in {0, 1, ...}
index j on I
input t[i][w] in [0, infinity[
input TC in [0, infinity[
input P[i] in {1, ..., I}
input L subsetof {1, ..., I}
check forall[l in L]: forall [i]: l not in P[i]
decision N in {0, 1, ...}
decision x[i][j][w] in {0, 1}
constraint EQ_2[i] is
sum[j][w in W] x[i][j][w] = 1
constraint EQ_3[i][k in P[i]] is
sum[j][w in W] j (x[i][j][w] - x[k][j][w]) >= 0
constraint EQ_4[j][w in W] is
sum[i] x[i][j][w] t[i][w] <= TC
constraint EQ_5[i][j] is
x[i][j][R] = 0
constraint EQ_6[j][w in W] is
N >= sum[i in L] j * x[i][j][w]
minimize N
Model MILP1 rewritten
- semantic names
- from global indices to local indices and explicit index domains
- extra helpful entities
- more domain constraints
- more input checks
- fixed model by restricting final tasks to 1
- tasks are made abstract
local Workforces := {Human, Robot}
input Tasks is distinct finite set
input NumberStations in {0, 1, ...}
local Stations := {1, ..., NumberStations}
input CycleTime in [0, infinity[
input TaskTime[Tasks][Workforces] in [0, CycleTime]
check sum[t in Tasks] TaskTime[t][Human] <= NumberStations * CycleTime
input Precedents[Tasks] in Tasks
local PrecedenceArcs := setunion[t in Tasks] { (tt, t) | tt in Precedents[t]}
check PrecedenceArcs is acyclic graph
local Antecedents[t in Tasks] := {tt in Tasks | t in Precedents[tt]}
local FinalTasks := {t in Tasks | Antecedents[t] = empty set}
check cardinality FinalTasks = 1
decision NumberWorkersUsed in Stations
decision Assign[Tasks][Stations][Workforces] in {0, 1}
constraint TaskWorked[t in Tasks] is
sum[s in Stations][w in Workforces] Assign[t][s][w] = 1
constraint TaskPrecedence[t in Tasks][k in Precedents[t]] is
sum[s in Stations][w in Workforces] s (Assign[t][s][w] - Assign[k][s][w]) >= 0
constraint CycleTimeLimit[s in Stations][w in Workforces] is
sum[t in Tasks] Assign[t][s][w] TaskTime[t][w] <= CycleTime
constraint NoRobots[t in Tasks][s in Stations] is
Assign[t][s][Robot] = 0
constraint FinalTasks[s in Stations][w in Workforces] is
NumberWorkersUsed >= s * sum[t in FinalTasks] Assign[t][s][w]
minimize NumberWorkersUsed
Model MILP1 simplified
- robot workforce type removed => only human, indices removed
input NumberTasks in {0, 1, ...}
input Tasks is distinct finite set
local Stations := {1, ..., NumberStations}
input CycleTime in [0, infinity[
input TaskTime[Tasks] in [0, CycleTime]
check sum[t in Tasks] TaskTime[t] <= NumberStations * CycleTime
input Precedents[Tasks] in Tasks
local PrecedenceArcs := setunion[t in Tasks] { (tt, t) | tt in Precedents[t]}
check PrecedenceArcs is acyclic graph
local Antecedents[t in Tasks] := {tt in Tasks | t in Precedents[tt]}
local FinalTasks := {t in Tasks | Antecedents[t] = empty set}
check cardinality FinalTasks = 1
decision NumberStationsUsed in Stations
decision Assign[Tasks][Stations] in {0, 1}
constraint TaskWorked[t in Tasks] is
sum[s in Stations] Assign[t][s] = 1
constraint TaskPrecedence[t in Tasks][k in Precedents[t]] is
sum[s in Stations] s (Assign[t][s] - Assign[k][s]) >= 0
constraint CycleTimeLimit[s in Stations] is
sum[t in Tasks] Assign[t][s] TaskTime[t] <= CycleTime
constraint FinalTasks[s in Stations] is
NumberStationsUsed >= s * sum[t in FinalTasks] Assign[t][s]
minimize NumberStationsUsed
Model MILP2 as in paper
- as close as possibly translated
- leaves index errors from (9) and (10)
local W := {H, R}
index w on W
input I in {0, 1, ...}
index i on I
index k on I
input J in {0, 1, ...}
index j on I
input t[i][w] in [0, infinity[
input TC in [0, infinity[
input P[i] in {1, ..., I}
input L subsetof {1, ..., I}
check forall[l in L]: forall [i]: l not in P[i]
input NA[w] in {1, ..., I}
decision N in {0, 1, ...}
decision x[i][j][w] in {0, 1}
decision u[i][w] in {0, 1}
constraint EQ_2[i] is
sum[j][w in W] x[i][j][w] = 1
constraint EQ_3[i][k in P[i]] is
sum[j][w in W] j (x[i][j][w] - x[k][j][w]) >= 0
constraint EQ_4[j][w in W] is
sum[i] x[i][j][w] t[i][w] <= TC
constraint EQ_7[w][j][i in NA[w]] is
x[i][j][w] = 0
constraint EQ_8[j] is
sum[w] u[j][w] = 1
constraint EQ_9[i][w] is
sum[i] x[i][j][w] <= I * u[j][w]
constraint EQ_10[i][w] is
sum[i] x[i][j][w] >= u[j][w]
constraint EQ_11 is
N = sum[j] u[j][H]
minimize N
Model MILP2 rewritten
- includes all changes from rewritten model MILP1
- fixes index errors in constraints
local Workforces := {Human, Robot}
input Tasks is distinct finite set
input NumberStations in {0, 1, ...}
local Stations := {1, ..., NumberStations}
input CycleTime in [0, infinity[
input TaskTime[Tasks][Workforces] in [0, CycleTime]
check sum[t in Tasks] min[w in Workforces] TaskTime[t][w] <= NumberStations * CycleTime
input Precedents[Tasks] in Tasks
local PrecedenceArcs := setunion[t in Tasks] { (tt, t) | tt in Precedents[t]}
check PrecedenceArcs is acyclic graph
local Antecedents[t in Tasks] := {tt in Tasks | t in Precedents[tt]}
local FinalTasks := {t in Tasks | Antecedents[t] = empty set}
check cardinality FinalTasks = 1
input NoAbility[Workforces] in Tasks
decision NumberWorkersUsed in Stations
decision Assign[Tasks][Stations][Workforces] in {0, 1}
decision UsesWorkforce[Stations][Workforces] in {0, 1}
constraint TaskWorked[t in Tasks] is
sum[s in Stations][w in Workforces] Assign[t][s][w] = 1
constraint TaskPrecedence[t in Tasks][k in Precedents[t]] is
sum[s in Stations][w in Workforces] s (Assign[t][s][w] - Assign[k][s][w]) >= 0
constraint CycleTimeLimit[s in Stations][w in Workforces] is
sum[t in Tasks] Assign[t][s][w] TaskTime[t][w] <= CycleTime
constraint NoAbilityRespected[w in Workforces][s in Stations][t in NoAbility[w]] is
Assign[t][s]][w] = 0
constraint EachStationAssigned[s in Stations] is
sum[w in Workforces] UsesWorkforce[s][w] = 1
constraint AssignedTasksNeedWorkforcesAllocated[s in Stations][w in Workforces] is
sum[t in Tasks] Assign[t][s][w] <= NumberTasks * UsesWorkforce[s][w]
constraint AllocatedWorkforcesGetTasksAssigned[s in Stations][w in Workforces] is
sum[t in Tasks] Assign[t][s][w] >= UsesWorkforce[s][w]
constraint NumberWorkersUsedDef is
NumberWorkersUsed = sum[s in Stations] UsesWorkforce[s][Human]
minimize NumberWorkersUsed
Model MILP3 as in paper
- as close as possibly translated
- leaves index errors from (9), (10) and (12)
- leaves error in (15)
local W := {H, R}
index w on W
input I in {0, 1, ...}
index i on I
index k on I
input J in {0, 1, ...}
index j on I
input t[i][w] in [0, infinity[
input TC in [0, infinity[
input P[i] in {1, ..., I}
input L subsetof {1, ..., I}
check forall[l in L]: forall [i]: l not in P[i]
input NA[w] in {1, ..., I}
decision N in {0, 1, ...}
decision x[i][j][w] in {0, 1}
decision u[i][w] in {0, 1}
decision y[i][k] in {0, 1}
decision s[i] in [0, infinity[
decision e[i] in [0, infinity[
constraint EQ_2[i] is
sum[j][w in W] x[i][j][w] = 1
constraint EQ_3[i][k in P[i]] is
sum[j][w in W] j (x[i][j][w] - x[k][j][w]) >= 0
constraint EQ_7[w][j][i in NA[w]] is
x[i][j][w] = 0
constraint EQ_8[j] is
sum[w] u[j][w] = 1
constraint EQ_9[i][w] is
sum[i] x[i][j][w] <= I * u[j][w]
constraint EQ_10[i][w] is
sum[i] x[i][j][w] >= u[j][w]
constraint EQ_11 is
N = sum[j] u[j][H]
constraint EQ_12 is
sum[w] u[j][w] >= 1
constraint EQ_13[i] is
y[i][i] = 0
constraint EQ_14[(i,k) | k not in P[i], i not in P[k], i <> k] is
y[i][k] + y[k][i] = 1
constraint EQ_15[(i, k) | k not in P[i], i not in P[k], i <> k][j] is
y[i][k] >= (sum[j_1 | j_1 < j][w] x[i][j][w] + x[k][j_1][w]) - 1
constraint EQ_16a[i] is
e[i] >= 0
constraint EQ_16b[i] is
s[i] >= 0
constraint EQ_17[i] is
e[i] = s[i] + sum[j][w] x[i][j][w] * t[j][w]
constraint EQ_18[i] is
e[i] <= T_c
constraint EQ_19[(i, k) | k not in P[i]][j] is
s[i] >= e[k] - T_c * (2 - sum[w] (x[i][j][w] + x[i][k][w]))
constraint EQ_20[(i, k) | k not in P[i], i not in P[k], i <> k][j][w] is
s[k] >= e[i] - T_c * (3 - y[i][k] - x[i][j][w] + x[i][k][w])
constraint EQ_21[(i, k) | k not in P[i], i not in P[k], i <> k][j] is
s[k] >= s[i] - T_c * (3 - y[i][k] - sum[w] x[i][j][w] - sum[w] x[i][k][w])
minimize N
Model MILP3 rewritten
- includes all changes from rewritten model MILP2
- fixes index errors
- fixes (15)
- adds more checks on precedence data
- merges times bounds from (16) and (18) into variable domains
local Workforces := {Human, Robot}
input Tasks is distinct finite set
input NumberStations in {0, 1, ...}
local Stations := {1, ..., NumberStations}
input CycleTime in [0, infinity[
input TaskTime[Tasks][Workforces] in [0, CycleTime]
check sum[t in Tasks] min[w in Workforces] TaskTime[t][w] <= NumberStations * CycleTime
input Precedents[Tasks] in Tasks
check forall[t in Tasks][tt in Precedents[t]][ttt in Precedents[tt]]:
ttt not in Precedents[t]
local PrecedenceArcs := setunion[t in Tasks] { (tt, t) | tt in Precedents[t]}
check PrecedenceArcs is acyclic graph
local Antecedents[t in Tasks] := {tt in Tasks | t in Precedents[tt]}
local FinalTasks := {t in Tasks | Antecedents[t] = empty set}
check cardinality FinalTasks = 1
input NoAbility[Workforces] in Tasks
decision NumberWorkersUsed in Stations
decision Assign[Tasks][Stations][Workforces] in {0, 1}
decision UsesWorkforce[Stations][Workforces] in {0, 1}
decision ExecOrder[i][k] in {0, 1}
decision StartTime[Tasks] in [0, CycleTime]
decision EndTime[Tasks] in [0, CycleTime]
constraint TaskWorked[t in Tasks] is
sum[s in Stations][w in Workforces] Assign[t][s][w] = 1
constraint TaskPrecedence[t in Tasks][k in Precedents[t]] is
sum[s in Stations][w in Workforces] s (Assign[t][s][w] - Assign[k][s][w]) >= 0
constraint NoAbilityRespected[w in Workforces][s in Stations][t in NoAbility[w]] is
Assign[t][s]][w] = 0
constraint EachStationAssigned[s in Stations] is
sum[w in Workforces] UsesWorkforce[s][w] = 1
constraint AssignedTasksNeedWorkforcesAllocated[s in Stations][w in Workforces] is
sum[t in Tasks] Assign[t][s][w] <= NumberTasks * UsesWorkforce[s][w]
constraint AllocatedWorkforcesGetTasksAssigned[s in Stations][w in Workforces] is
sum[t in Tasks] Assign[t][s][w] >= UsesWorkforce[s][w]
constraint NumberWorkersUsedDef is
NumberWorkersUsed = sum[s in Stations] UsesWorkforce[s][Human]
constraint StationAllocatedWorkforce[s in Stations] is
sum[w in Workforces] UsesWorkforce[s][w] >= 1
constraint NoSelfExecOrder[t in Tasks] is
ExecOrder[t][t] = 0
local DiffNoArcs :=
{ (t, tt) in Task^2 | t not in Precedents[tt], tt not in Precedents[t], t not = tt }
constraint ExecOrderExists[(t, tt) in DiffNoArcs] is
ExecOrder[t][tt] + ExecOrder[tt][t] = 1
constraint ExecOrderDefinition[(t, tt) in DiffNoArcs]][s in Stations] is
ExecOrder[t][tt] >=
(sum[ss | ss < s][w in Workforces] Assign[t][ss][w])
+ (sum[w in Workforces] + Assign[tt][s][w])
- 1
constraint TimeDefinition[t in Tasks] is
EndTime[t] - StartTime[t] =
sum[s in Stations][w in Workforces] Assign[t][s][w] * TaskTime[t][w]
constraint SchedulingPreceding[t in Task][tt in Precedents[t]][s in Stations] is
StartTime][t] >=
EndTime[tt]
- CycleTime * (2 - sum[w in Workforces] (Assign[t][s][w] - Assign[tt][s][w]))
constraint SchedulingOthers[(t, tt) in DiffNoArcs][s in Stations][w in Workforces] is
StartTime[tt] >=
EndTime[t]
- CycleTime * (3 - ExecOrder[t][tt] - Assign[t][s][w] + Assign[tt][s][w])
constraint SchedulingStarts[(t, tt) in DiffNoArcs][s in Stations] is
StartTime[tt] >=
StartTime[t]
- CycleTime * (3 - ExecOrder[t][tt]
- (sum[w in Workforces] Assign[t][s][w])
- (sum[w in Workforces] Assign[tt][s][w])
)
minimize NumberWorkersUsed