A small company can produce a number of product types
The company has received a number of orders
Some pairs of products ⟨p1,p2⟩
are associated to a setup time:
p1
, before switching to p2
Goal:
Model #1
Main idea: variables = manufacturing times of all product units
Parameters:
n=
number of product unitsdi=
deadline for product unit i
pi=
product type for unit i
eoh=
safe time horizon (largest deadline)S={(pa,pb),...} =
ordered pairs with setups timesThe full model:
min
The s_j \neq s_i + 1
constraints correspond to the setup times:
i
and j
...Model #2
Main idea: variables = products to be manufactured at each time point
Parameters:
n =
number of productseoh =
safe time horizon (largest deadline)m =
number of ordersd_k =
deadline for order k
p_k =
product type for order k
n_k =
number of product units for order k
S = \{(p_a, p_b),...\} =
ordered pairs with setups timesThe full model:
\begin{align}
\min z = \ & \max_{t = 0..eoh} t\, (x_t \neq -1) \\
\text{subject to: }\
& \sum_{t = 0..d_k} (x_t = p_k) \geq \sum_{\substack{h = 0..m-1,\\ d_h \leq d_k}} n_h && \forall k = 0..m-1\\
& (x_t = p_a) \leq (x_{t+1} \neq p_b) && \forall i, j : (p_a, p_b) \in S\\
& x_t \in \{-1..n-1\} && \forall t = 0..eoh
\end{align}
-1
is used for idle production timesk
, the sum of units of p_k
produced before d_k
...p_k
needed up to d_k
x_{i,j}
variable per cellx_{1,1}
and x_{2,1}
\{1, 2\}
, which has cardinality 2x_{0,1}
!Main idea: reasoning on the whole column
X
and the union of their domains V
If we find a a set of values W \subset V
and a set of variables Y \subset X
, s.t.:
|Y| = |W| \quad\text{ and }\quad D(x_i) \subseteq W,\ \ \forall x_i \in Y
Then:
W
is called a Hall set for X
W
will all be taken by the variables in Y
W
from the domains of the other variablesFormally, we should have \forall W \subset V, Y \subset X
with |Y| = |W|
:
D(x_i) \subseteq W, \ \ \forall x_i \in Y \quad \Rightarrow \quad D(x_j) = D(x_j) \setminus W, \ \forall x_j \in X \setminus Y
Now, we could encode the implications as additional constraints
Still, we have no luck
D(x_i) \subseteq W, \ \ \forall x_i \in Y \quad \Rightarrow \quad D(x_j) = D(x_j) \setminus W, \ \forall x_j \in X \setminus Y
\forall W \subset V, Y \subset X \text{ with } |Y| = |W|
And this is bad :-(
V
is exponentialX
is exponentialShall we give up? Let's take a different appraoch instead
We can introduce a new global constraint:
\displaystyle {\rm\scriptsize ALLDIFFERENT}(X)
, where X
is a vector of variables
x_i \neq x_j, \forall i \neq j
...In the case of {\rm\scriptsize ALLDIFFERENT}
polynomial GAC propagators do exist
{\rm\scriptsize ALLDIFFERENT}
We will see an {\rm\scriptsize ALLDIFFERENT}
propagator based on network flows:
We consider two distinct problems:
We will use this example instance:
{\rm\scriptsize ALLDIFFERENT}(X), \text{ with } x_0 \in \{0, 2\}, x_1 \in \{0, 2\}, x_3 \in \{1,2,3\}
For any constraint, we can define the so-called value graph
For any constraint, we can define the so-called value graph
Now, let's add:
s
, connected to all valuest
, to which all vars are connectedWe can view this structure as a "pipe network":
s
and move toward t
How do we "read" the flow?
v_j \rightarrow x_i
, then v_j
is assigned to x_i
v_j
cannot be assigned twice to the same varHow do we "read" the flow?
s \rightarrow v_j
, then v_j
is used\Rightarrow
each v_j
can be used at most onceHow do we "read" the flow?
x_i \rightarrow t
, then x_i
is assigned\Rightarrow
each x_i
can be assigned at most onceImportant consequence:
|X|
Hence, we have our feasibility check:
s
to t
X
{\rm\scriptsize ALLDIFFERENT}
How do we solve this maximum flow problem?
{\rm\scriptsize ALLDIFFERENT}
General idea:
{\rm\scriptsize ALLDIFFERENT}
Our (trivial) initial flow:
f(a \rightarrow b)
for all arcs is 0f(a \rightarrow b) = 1
{\rm\scriptsize ALLDIFFERENT}
Augmenting the flow value
s-t
path...f(a \rightarrow b) < 1
){\rm\scriptsize ALLDIFFERENT}
Augmenting the flow value
{\rm\scriptsize ALLDIFFERENT}
Augmenting the flow value
{\rm\scriptsize ALLDIFFERENT}
Augmenting the flow value
{\rm\scriptsize ALLDIFFERENT}
Is that all? No, actually.
{\rm\scriptsize ALLDIFFERENT}
But solutions do actually exist!
x_0 = 0, x_1 = 2, x_2 = 1
{\rm\scriptsize ALLDIFFERENT}
We are missing the ability to "undo" past choices
{\rm\scriptsize ALLDIFFERENT}
Main idea: we search for paths on a Residual Graph
a \rightarrow b
in the residual graph iff:a \rightarrow b
in the original graph and f(a \rightarrow b) = 0
b \rightarrow a
in the original graph and f(b \rightarrow a) = 1
Intuitively, the residual graph:
a \rightarrow b
if we can increase the flow along a \rightarrow b
b \rightarrow a
if we can decrease the flow along a \rightarrow b
{\rm\scriptsize ALLDIFFERENT}
{\rm\scriptsize ALLDIFFERENT}
Notation: black arrow heads for the residual graph
{\rm\scriptsize ALLDIFFERENT}
s-t
paths on the residual graph{\rm\scriptsize ALLDIFFERENT}
When we route flow along the path we need to:
f(a \rightarrow b) = 0
in the orig. graph)f(b \rightarrow a) = 1
in the orig. graph){\rm\scriptsize ALLDIFFERENT}
For example:
{\rm\scriptsize ALLDIFFERENT}
For example:
{\rm\scriptsize ALLDIFFERENT}
The total flow value is |X|
, hence the constraint is feasible
x_0 = 2, x_1 = 0, x_2 = 3
(look at the solid arcs){\rm\scriptsize ALLDIFFERENT}
O\left(\text{#edges}\right)
for finding paths via Dijkstra\text{#edges} = O\left( \sum_{x_i \in X} |D(x_i)| \right)
|X|
iterations, hence total complexity O\left(|X|\sum_{x_i \in X} |D(x_i)|\right)
{\rm\scriptsize ALLDIFFERENT}
Ok, we have our consistency checker!
|X|
Side note:
And what about the filtering?
{\rm\scriptsize ALLDIFFERENT}
What about the value-variable arcs with no flow?
{\rm\scriptsize ALLDIFFERENT}
Consider for example arc 2 \rightarrow x_1
{\rm\scriptsize ALLDIFFERENT}
Flow routing takes place on the residual graph
{\rm\scriptsize ALLDIFFERENT}
Hence, we are looking for a cycle on the residual graph!
2 \rightarrow x_1
x_1
to 2
{\rm\scriptsize ALLDIFFERENT}
For example this one
{\rm\scriptsize ALLDIFFERENT}
This is how it looks on the original graph
{\rm\scriptsize ALLDIFFERENT}
And this is what would happen by routing flow along the cycle
|X|
{\rm\scriptsize ALLDIFFERENT}
In practice, there is no need to route flow along the cycle
Let's check another value-variable pair...
{\rm\scriptsize ALLDIFFERENT}
For example, let us check arc 2 \rightarrow x_2
{\rm\scriptsize ALLDIFFERENT}
For example, let us check arc 2 \rightarrow x_2
2 \rightarrow x_2
in the residual graph{\rm\scriptsize ALLDIFFERENT}
Therefore, there is no way we can route flow through 2 \rightarrow x_2
2 \rightarrow x_2
from the original graph2
from the domain of x_2
{\rm\scriptsize ALLDIFFERENT}
We can prune a value v
from the domain of x_i
iff:
f(v \rightarrow x_i) = 0
v \rightarrow x_i
in the residual graphAn important note:
v \rightarrow x_i
or x_i \rightarrow v
Hence, we can simplify the second condition:
v
and x_i
in the residual graph"I.e. iff v
and x_i
are in different strongly connected components
{\rm\scriptsize ALLDIFFERENT}
Strongly Connected Component:
{\rm\scriptsize ALLDIFFERENT}
Here are the SCC of our residual graph (before filtering)
O\left(|X| + \sum_{x_i \in X} |D(x_i)|\right)
{\rm\scriptsize ALLDIFFERENT}
We can speed-up filtering by using SCC
v \rightarrow x_i
with f(v \rightarrow x_i) = 0
v
and x_i
are in different SCC{\rm\scriptsize ALLDIFFERENT}
is our first example of global constraint:
Global constraints are very important in CP
Global constraints will be the focus of the next few blocks of slides
A small shop makes use of shift work
Each employee should perform:
Moreover:
Let's try to model the shift assignment for a single employee:
X = \{x_0, x_1, x_2, x_3\}
)We can encode the allowed types of shift in the domains:
x_0 \in \{0, 1\}, x_1 \in \{0, 2\}, x_2 \in \{0, 2\}, x_3 \in \{1, 2\}
And the other restrictions?
{\rm\scriptsize ALLDIFFERENT}
\sum_{x_i \in X} (x_i = 2) = 1
\sum_{x_i \in X} (x_i = 1) \geq 1
\sum_{x_i \in X} (x_i = 0) \leq 1
This approach is correct, but has poor propagation
\begin{align}
& (x_0 = 2) + (x_1 = 2) + (x_2 = 2) + (x_3 = 2) = 1\\
& (x_0 = 1) + (x_1 = 1) + (x_2 = 1) + (x_3 = 1) \geq 1\\
& (x_0 = 0) + (x_1 = 0) + (x_2 = 0) + (x_3 = 0) \leq 1\\
& x_0 \in \{0, 1\}, x_1 \in \{0, 2\}, x_2 \in \{0, 2\}, x_3 \in \{1, 2\}
\end{align}
This approach is correct, but has poor propagation
\begin{align}
& \{0\} + (x_1 = 2) + (x_2 = 2) + (x_3 = 2) = 1\\
& (x_0 = 1) + \{0\} + \{0\} + (x_3 = 1) \geq 1\\
& (x_0 = 0) + (x_1 = 0) + (x_2 = 0) + \{0\} \leq 1\\
& x_0 \in \{0, 1\}, x_1 \in \{0, 2\}, x_2 \in \{0, 2\}, x_3 \in \{1, 2\}
\end{align}
(x_i = v)
constraints we get thisNo filtering can be done based on the sums
This approach is correct, but has poor propagation
\begin{align}
& \{0\} + (x_1 = 2) + (x_2 = 2) + (x_3 = 2) = 1\\
& (x_0 = 1) + \{0\} + \{0\} + (x_3 = 1) \geq 1\\
& (x_0 = 0) + (x_1 = 0) + (x_2 = 0) + \{0\} \leq 1\\
& x_0 \in \{0, 1\}, x_1 \in \{0, 2\}, x_2 \in \{0, 2\}, x_3 \in \{1, 2\}
\end{align}
By reasoning globally, however, we can deduce that:
0
and 2
cannot be assigned more than once1
must be assigned twicex_0 = 1
, x_3 = 1
We can try embed this reasoning inside a global constraint
How shall we define it? In our example, we are interested in:
So, we could define a Global Cardinality Constraint:
\displaystyle {\rm\scriptsize GCC}(X, V, L, U)
, where:
X
is a vector of variables x_i
V
is a vector of values v_j
L
is a vector of cardinality lower bounds l_j
for v_j
U
is a vector of cardinality upper bounds u_j
for v_j
The Global Cardinality Constraint is very important in practice:
{\rm\scriptsize ALLDIFFERENT}(X)
could be encoded as {\rm\scriptsize GCC}(X, V, [0..0], [1..1])
{\rm\scriptsize ALLDIFFERENT}
when possible{\rm\scriptsize GCC}
How do we perform filtering for {\rm\scriptsize GCC}
?
Main idea: exploit the similarity with the {\rm\scriptsize ALLDIFFERENT}
{\rm\scriptsize ALLDIFFERENT}
Actually, our {\rm\scriptsize ALLDIFFERENT}
propagator was originally designed for {\rm\scriptsize GCC}
!
Once again we start from the value graph...
{\rm\scriptsize GCC}
: Consistency Checking{\rm\scriptsize GCC}
instance{\rm\scriptsize GCC}
: Consistency Checking{\rm\scriptsize GCC}
instances
and sink t
node, as for {\rm\scriptsize ALLDIFFERENT}
{\rm\scriptsize GCC}
: Consistency Checking1
, as for the {\rm\scriptsize ALLDIFFERENT}
{\rm\scriptsize GCC}
: Consistency Checking1
, as for the {\rm\scriptsize ALLDIFFERENT}
1
, as for the {\rm\scriptsize ALLDIFFERENT}
{\rm\scriptsize GCC}
: Consistency CheckingU_i
...U_i
timesL_i
L_i
times{\rm\scriptsize GCC}
: Consistency CheckingNotation:
[L_i, U_i]
to show the demand and capacity{\rm\scriptsize GCC}
: Consistency CheckingThe constraint is feasible iff we can find a flow such that:
x_i \rightarrow t
arcs (i.e the max flow value is |X|
){\rm\scriptsize GCC}
: Consistency CheckingLike in the {\rm\scriptsize ALLDIFFERENT}
case we start from a zero flow
{\rm\scriptsize GCC}
the zero flow may be infeasible{\rm\scriptsize GCC}
: Consistency Checkings \rightarrow 1
and s \rightarrow 2
are not satisfiedHence, we need to fix the flow before starting to maximize
{\rm\scriptsize GCC}
: Consistency CheckingMain idea for fixing the flow:
0, 1, 2
) as source nodesl_i
flow units from these nodes to the sink{\rm\scriptsize GCC}
Routing flow is done on the residual graph
The residual graph for the {\rm\scriptsize GCC}
flow network:
a \rightarrow b
in two casesCase 1 (forward arcs):
a \rightarrow b
with capacity c
c - f(a \rightarrow b) > 0
Intuitively: it possible to route more flow along a \rightarrow b
c - f(a \rightarrow b)
{\rm\scriptsize GCC}
Routing flow is done on the residual graph
The residual graph for the {\rm\scriptsize GCC}
flow network:
a \rightarrow b
in two casesCase 2 (backward arcs):
b \rightarrow a
with demand d
f(b \rightarrow a) - d > 0
Intuitively: it is possible to reduce the flow along b \rightarrow a
f(b \rightarrow a) - d
{\rm\scriptsize GCC}
Routing flow is done on the residual graph
The residual graph for the {\rm\scriptsize GCC}
flow network:
a \rightarrow b
in two cases (see previous slides)Side effect: there can be no arc with demand in the residual graph
{\rm\scriptsize ALLDIFFERENT}
was a special case{\rm\scriptsize GCC}
This the residual graph for the zero flow
{\rm\scriptsize GCC}
The demand on arc s \rightarrow 2
in the original graph is 1
{\rm\scriptsize GCC}
The demand on arc s \rightarrow 2
in the original graph is 1
2
to t
(e.g. Dijkstra's algorithm){\rm\scriptsize GCC}
graph, this value is always 1{\rm\scriptsize GCC}
This is the resulting flow status on the original graph
{\rm\scriptsize GCC}
Next, we try to satisfy the demand on arc s \rightarrow 1
{\rm\scriptsize GCC}
Next, we try to satisfy the demand on arc s \rightarrow 1
{\rm\scriptsize GCC}
Next, we try to satisfy the demand on arc s \rightarrow 1
1
to t
{\rm\scriptsize GCC}
And we obtain the following flow on the original graph
{\rm\scriptsize GCC}
The flow is feasible
We can now maximize the flow by routing flow on s-t
paths
{\rm\scriptsize GCC}
: Consistency CheckingThis is the flow at the end of the maximization process
x_0 = 1, x_1 = 0, x_2 = 2, x_3 = 1
|X|
, the constraint is infeasible{\rm\scriptsize GCC}
: FilteringFiltering can be performed by reasoning on the residual graph
{\rm\scriptsize GCC}
: FilteringFiltering can be performed by reasoning on the residual graph
1
and s
{\rm\scriptsize GCC}
: FilteringFiltering can be performed by reasoning on the residual graph
{\rm\scriptsize ALLDIFFERENT}
{\rm\scriptsize GCC}
: FilteringIn our case, no cycle including 0 \rightarrow x_0
and 2 \rightarrow x_3
exists
0
from D(x_0)
, and 2
from D(x_3)
{\rm\scriptsize DISTRIBUTE}
ConstraintIn solvers the {\rm\scriptsize GCC}
is sometimes called {\rm\scriptsize DISTRIBUTE}
Actually, {\rm\scriptsize DISTRIBUTE}
is a more general constraint, with signature:
{\rm\scriptsize DISTRIBUTE}(X, V, N)
X
is a vector of variables x_i
V
is a vector of values v_j
N
is a vector of cardinality variables n_j
{\rm\scriptsize DISTRIBUTE}
ConstraintIn solvers the {\rm\scriptsize GCC}
is sometimes called {\rm\scriptsize DISTRIBUTE}
Actually, {\rm\scriptsize DISTRIBUTE}
is a more general constraint, with signature:
{\rm\scriptsize DISTRIBUTE}(X, V, N)
Two important differences w.r.t. our definition:
D(n_j)
n_j
variablesWhich means that we can use {\rm\scriptsize DISTRIBUTE}
for counting!
Many solver provide other similar constraints
If we simply need to count the occurrences of a value, we have:
{\rm\scriptsize COUNT}(X, v, c)
Where:
X
is a vector of integer variablesv
is an integer valuec
is either an integer or a variableThe constraint is satisfied if value v
is taken c
times in X
Many solver provide other similar constraints
If we need to limit the occurrences of a single value, we have:
{\rm\scriptsize ATMOST}(X, v, c)
Where:
X
is a vector of integer variablesv
is an integer valuec
is an integerThe constraint is satisfied if value v
is taken less than c
times in X
{\rm\scriptsize COUNT}(X, v, c)
Many solver provide other similar constraints
If all values must be different, except for a special one, we have:
{\rm\scriptsize ALLDIFFERENTEXCEPT}(X, v)
Where:
X
is a vector of integer variablesv
is an integer valueAll value can be taken at most once, except for v