Support for complex nested expressions is a key benefit of the logical expression system.
Below are examples of expressions that we support, and with some, an explanation of their implementation.
Expressions within CP-type operators
\[\text{atleast}(3, Y_1, Y_2 \vee Y_3, Y_4 \Rightarrow Y_5, Y_6)\]
Here, augmented variables may be automatically added to the model as follows:
\[\begin{split}\text{atleast}(3, Y_1, Y_A, Y_B, Y_6)\\
Y_A \Leftrightarrow Y_2 \vee Y_3\\
Y_B \Leftrightarrow (Y_4 \Rightarrow Y_5)\end{split}\]
m.p = LogicalConstraint(
expr=atleast(3, m.Y[1], Or(m.Y[2], m.Y[3]), m.Y[4].implies(m.Y[5]), m.Y[6]))
Nested CP-style operators
\[\text{atleast}(2, Y_1, \text{exactly}(2, Y_2, Y_3, Y_4), Y_5, Y_6)\]
Here, we again need to add augmented variables:
\[\begin{split}\text{atleast}(2, Y_1, Y_A, Y_5, Y_6)\\
Y_A \Leftrightarrow \text{exactly}(2, Y_2, Y_3, Y_4)\end{split}\]
However, we also need to further interpret the second statement as a disjunction:
\begin{gather*}
\text{atleast}(2, Y_1, Y_A, Y_5, Y_6)\\
\left[\begin{gathered}Y_A\\\text{exactly}(2, Y_2, Y_3, Y_4)\end{gathered}\right]
\vee
\left[\begin{gathered}\neg Y_A\\
\left[\begin{gathered}Y_B\\\text{atleast}(3, Y_2, Y_3, Y_4)\end{gathered}\right] \vee \left[\begin{gathered}Y_C\\\text{atmost}(1, Y_2, Y_3, Y_4)\end{gathered}\right]
\end{gathered}\right]
\end{gather*}
or equivalently,
\begin{gather*}
\text{atleast}(2, Y_1, Y_A, Y_5, Y_6)\\
\text{exactly}(1, Y_A, Y_B, Y_C)\\
\left[\begin{gathered}Y_A\\\text{exactly}(2, Y_2, Y_3, Y_4)\end{gathered}\right]
\vee
\left[\begin{gathered}Y_B\\\text{atleast}(3, Y_2, Y_3, Y_4)\end{gathered}\right] \vee \left[\begin{gathered}Y_C\\\text{atmost}(1, Y_2, Y_3, Y_4)\end{gathered}\right]
\end{gather*}
m.p = LogicalConstraint(
expr=atleast(2, m.Y[1], exactly(2, m.Y[2], m.Y[3], m.Y[4]), m.Y[5], m.Y[6]))
In the logical_to_linear
transformation, we automatically convert these special disjunctions to linear form using a Big M reformulation.