15.3 Logical Constraints
Logical constraints can also be formalised as integer constraints. For example, let’s say that \(X_1\) is whether I decide to buy a new house, and \(X_2\) is whether I decide to renovate the new house. We restrict both \(X_1\), \(X_2\) to be either \(0\) or \(1\). Obviously, I need to purchase the house before I can renovate it, so \(X_1\) is a pre-requisite for \(X_2\).
Using logic, I can write that:
\[ \text{if I choose } X_2, \text{then I must choose } X_1 \text{ as well.} \]
This logical constraint can be formalised as the following integer-valued constraint:
\[X_1 - X_2 \geq 0\] (In fact, this is not just an integer-valued constraint, but it is even stronger: the two variables are binary-valued).
The intuition is that if \(X_2\) is 1, then this forces \(X_1\) to be 1 too. Let us check:
- \(X_1 = 0\), \(X_2 = 0\): Constraint: \(0 - 0 = 0 \geq 0\) is satisfied. This is fine since I don’t purchase the house and I don’t renovate the house.
- \(X_1 = 1\), \(X_2 = 0\): Constraint: \(1 - 0 = 1 \geq 0\) is satisfied. This is fine since I can choose to purchase the house without renovating it.
- \(X_1 = 1\), \(X_2 = 1\): Constraint: \(1 - 1 = 0 \geq 0\) is satisfied. Again, this is fine since I can choose to both purchase and renovate the house.
- Most importantly: \(X_1 = 0\), \(X_2 = 1\): Constraint: \(0 - 1 = -1 \not \geq 0\). The constraint is not satisfied. This makes sense, because I cannot not purchase the house (\(X_1=0\)) but still choose to renovate it (\(X_2=1\)).
Thus, we can check and see that this integer constraint correctly captures the intuition behind the logical constraint.
In general, the following table summaries how some logical constraints can be represented using binary decision variables:
Logical Condition | Linear Constraint |
---|---|
if \(A\) then \(B\) | \(B - A \geq 0\) |
if not \(A\) then \(B\) | \(B - (1 - A) \geq 0\), or \(A + B \geq 1\) |
At most one of \(A\), \(B\) | \(A + B \leq 1\) |
At least one of \(A\), \(B\) | \(A + B \geq 1\) |
if (\(A\) or \(C\)) then \(B\) | \(B - (A+C) \geq 0\) |
if (\(A\) and \(C\)) then \(B\) | \(B - (A+C) \geq -1\), or \((A+C) - B \leq 1\) |
if \(A\) then (\(B\) or \(C\)) | \((B+C) - A \geq 0\) |
if \(A\) then (\(B\) and \(C\)) | \((B+C) - 2A \geq 0\) |
15.3.1 How to specify logical constraints
Earlier we saw that to tell lpSolve
that certain decision variables are integer-valued, we used an option int.vec = c(...)
. If we want to tell lpSolve
that instead some decision variables are binary-valued, then we use: binary.vec = c(...)
. And that’s it! lpSolve
does everything else for us.
(In fact, it’s harder to ensure that you have written down the constraints correctly!)
15.3.2 Logical Constraints Example: Planning university courses
Let’s see logical constraints in action in the following example.
Natalie has decided to switch her career to data science, as she feels it has more prospects than her previous industry. She is eyeing a “Pico-masters (TM)” program at her local university, where she has to complete 40 units of courses to satisfy the pico-degree. (“Pico-masters” and “nano-masters” are fictional—at least, for now—but some online education platforms and universities like edX are offering “MicroMasters®” and other similar products.)
The program offers the following courses, in STatistics, ProGramming, and Data Management, along with their associated costs and pre-requisites. The pre-requisites for each course must be fulfilled before students are allowed to take that course. In order to finish the PicoMasters, she needs to finish a specialization in one of the three tracks, which is fulfilled by completing the “Level 3” version of that course. Natalie has also indicated her personal interest in each course, in the following table.
Course | Units | Pre-requisites | Interest |
---|---|---|---|
ST1 | 10 | - | 8 |
ST2 | 10 | ST1 | 4 |
ST3 | 10 | ST2 | 6 |
PG1 | 10 | - | 7 |
PG2a | 10 | PG1 | 5 |
PG2b | 10 | PG1 | 6 |
PG3 | 10 | PG2a or PG2b | 3 |
DM1 | 10 | - | 4 |
DM2 | 10 | DM1 | 6 |
DM3 | 10 | DM2 | 7 |
Imagine that her goal is to maximize her interest and satisfy the requirements of the degree, while also taking exactly 40 units (in order to keep costs low). Which classes should she take?
First, let’s define our decision variables, by just using \(X_1\) to \(X_{10}\) to correspond to taking each of the ten courses, in the order in the table above. That is,
\(X_1\) = Take ST1, \(X_2\) = Take ST2, \(X_3\) = Take ST3,
\(X_4\) = Take PG1, \(X_5\) = Take PG2a, \(X_6\) = Take PG2b, \(X_7\) = Take PG3,
\(X_8\) = Take DM1, \(X_9\) = Take DM2, \(X_{10}\) = Take DM3
Now let’s tackle the constraints one-by-one.
Consider the following pre-requisite:
Natalie needs to take ST1 before being allowed to take ST2.
We can represent this using the following constraint:
\[ X_1 - X_2 \geq 0 \]
You can easily check if this is correct by ‘testing’ it by subtituting values. If Natalie takes ST2 (i.e., \(X_2 = 1\)), then we have the following inequality: \(X_1 - 1 \geq 0\). In order for this also to be true, we need \(X_1\) to also be 1. This means that Natalie taking ST2 (\(X_2 = 1\)) actually forces her to also have taken ST1 (\(X_1 = 1\)).
Similarly, we can write out the pre-requisite constraints for the rest. Now, let’s consider one more case:
Natalie needs to take either PG2a OR PG2b before being allowed to take PG3.
One easy thing to try is to replace ST2 (\(X_2\)) in the previous inequality with PG3 (\(X_7\)), and replace ST1 (\(X_1\)) with (PG2a or PG2b), or (\((X_5+X_6)\)). So let’s try:
\[ (X_5 + X_6) - X_7 \geq 0 \]
And actually this is the correct inequality! Let’s test this out. If Natalie takes PG3, then \(X_7=1\), and so we are left with: \((X_5 + X_6) - 1 \geq 0\). This inequality will be satisfied if \(X_5\) is 1, or if \(X_6\) is 1, that is, if she takes either PG2a or PG2b. (Note that this will also be satisifed if BOTH \(X_5\) and \(X_6\) is 1 – there is nothing stopping her from taking both PG2a and PG2b!)
And finally let’s consider the constraint that she needs to finish a specialization. That is, she needs to take either ST3 (\(X_3\)) or PG3 (\(X_7\)) or DM3 (\(X_{10}\)). This means that at least one of them needs to be 1. So we can just write:
\[ X_3 + X_7 + X_{10} \geq 0 \]
Please make sure you understand how we got (or how we tested/verified) the above inequalities!
Now, we can finally write out our optimization problem in a large table:
Decision variables \(X_1\) = Take ST1, \(X_2\) = Take ST2, \(X_3\) = Take ST3, \(X_4\) = Take PG1, \(X_5\) = Take PG2a, \(X_6\) = Take PG2b, \(X_7\) = Take PG3, \(X_8\) = Take DM1, \(X_9\) = Take DM2, \(X_{10}\) = Take DM3 |
Maximize Interest = 8\(X_1\) + 4\(X_2\) + 6\(X_3\) + 7\(X_4\) + 5\(X_5\) + 6\(X_6\) + 3\(X_7\) + 4\(X_8\) + 6\(X_9\) + 7\(X_{10}\) |
---|---|
Subject to | |
Minimum Course Requirements (= for cost savings) | 10 \(X_1\) + 10 \(X_2\) + 10 \(X_3\) + 10 \(X_4\) + 10 \(X_5\) + 10 \(X_6\) + 10 \(X_7\) + 10 \(X_8\) + 10 \(X_9\) + 10 \(X_{10}\) \(=\) 40 |
Pre-Requisite for ST2 | \(X_1 - X_2 \geq 0\) or 1 \(X_1\) + (-1) \(X_2\) + 0 \(X_3\) + 0 \(X_4\) + 0 \(X_5\) + 0 \(X_6\) + 0 \(X_7\) + 0 \(X_8\) + 0 \(X_9\) + 0 \(X_{10}\) \(\geq\) 0 |
Pre-Requisite for ST3 | \(X_2 - X_3 \geq 0\) or 0 \(X_1\) + 1 \(X_2\) + -1 \(X_3\) + 0 \(X_4\) + 0 \(X_5\) + 0 \(X_6\) + 0 \(X_7\) + 0 \(X_8\) + 0 \(X_9\) + 0 \(X_{10}\) \(\geq\) 0 |
Pre-Requisite for PG2a | \(X_4 - X_5 \geq 0\) or 0 \(X_1\) + 0 \(X_2\) + 0 \(X_3\) + 1 \(X_4\) + -1 \(X_5\) + 0 \(X_6\) + 0 \(X_7\) + 0 \(X_8\) + 0 \(X_9\) + 0 \(X_{10}\) \(\geq\) 0 |
Pre-Requisite for PG2b | \(X_4 - X_6 \geq 0\) or 0 \(X_1\) + 0 \(X_2\) + 0 \(X_3\) + 1 \(X_4\) + 0 \(X_5\) + -1 \(X_6\) + 0 \(X_7\) + 0 \(X_8\) + 0 \(X_9\) + 0 \(X_{10}\) \(\geq\) 0 |
Pre-Requisite for PG3 (PG2a OR PG2b) |
\((X_5 + X_6) - X_7 \geq 0\) or 0 \(X_1\) + 0 \(X_2\) + 0 \(X_3\) + 0 \(X_4\) + 1 \(X_5\) + 1 \(X_6\) + -1 \(X_7\) + 0 \(X_8\) + 0 \(X_9\) + 0 \(X_{10}\) \(\geq\) 0 |
Pre-Requisite for DM2 | \(X_8 - X_9 \geq 0\) or 0 \(X_1\) + 0 \(X_2\) + 0 \(X_3\) + 0 \(X_4\) + 0 \(X_5\) + 0 \(X_6\) + 0 \(X_7\) + 1 \(X_8\) + -1 \(X_9\) + 0 \(X_{10}\) \(\geq\) 0 |
Pre-Requisite for DM3 | \(X_9 - X_{10} \geq 0\) or 0 \(X_1\) + 0 \(X_2\) + 0 \(X_3\) + 0 \(X_4\) + 0 \(X_5\) + 0 \(X_6\) + 0 \(X_7\) + 0 \(X_8\) + 1 \(X_9\) + -1 \(X_{10}\) \(\geq\) 0 |
Complete Specialization | \(X_3 + X_7 + X_{10} \geq 1\) or 0 \(X_1\) + 0 \(X_2\) + 1 \(X_3\) + 0 \(X_4\) + 0 \(X_5\) + 0 \(X_6\) + 1 \(X_7\) + 0 \(X_8\) + 0 \(X_9\) + 1 \(X_{10}\) \(\geq\) 1 |
Binary, Integer, Non-Negativity Constraints | \(X_1\), to \(X_{10}\) all binary, integers and \(\geq 0\). |
Notice that I’ve written out the inequalities into longer forms with explicit “0”s, so
\[X_1 - X_2 \geq 0\] becomes \[X_1 + (-1) X_2 + 0 X_3 + 0 X_4 + 0 X_5 + 0 X_6 + 0 X_7 + 0 X_8 + 0 X_9 + 0 X_{10} \geq 0\]
This makes it much easier to type this into code. This is the hardest part now, transfering this into code without making any mistakes!
#defining parameters
objective.fn <- c(8, 4, 6, 7, 5, 6, 3, 4, 6, 7)
const.mat <- matrix(c(rep(10,10),
rep(0,0), 1, -1, rep(0, 8), # ST2
rep(0,1), 1, -1, rep(0, 7), # ST3
rep(0,3), 1, -1, rep(0, 5), # PG2a
rep(0,3), 1, 0, -1, rep(0, 4), # PG2b
rep(0,4), 1, 1, -1, rep(0, 3), # PG3
rep(0,7), 1, -1, rep(0, 1), # DM2
rep(0,8), 1, -1, rep(0, 0), # DM3
rep(0,2), 1, rep(0,3), 1, rep(0,2), 1 # complete specialization
) , ncol=10 , byrow=TRUE)
const.dir <- c("=", rep(">=", 8))
const.rhs <- c(40, rep(0,7), 1)
#solving model
lp.solution <- lp("max", objective.fn, const.mat, const.dir, const.rhs,
binary.vec = c(1:10))
lp.solution$solution #decision variables values
## [1] 1 1 1 1 0 0 0 0 0 0
## Success: the objective function is 25
Thus, the final solution is \(X_1\), \(X_2\), \(X_3\), \(X_4\) = 1, with the rest = 0.
In order to maximize her interest while completing the degree, Natalie should choose to specialize in Statistics (taking ST1, ST2, ST3), and then also taking the first course in Programming (PG1).