Simplex Method
A technique for maximizing linear expressions subject to linear constraints.
This post is part of the book Introduction to Algorithms and Machine Learning: from Sorting to Strategic Agents. Suggested citation: Skycak, J. (2021). Simplex Method. In Introduction to Algorithms and Machine Learning: from Sorting to Strategic Agents. https://justinmath.com/simplex-method/
Want to get notified about new posts? Join the mailing list and follow on X/Twitter.
The simplex method is a technique for maximizing linear expressions subject to linear constraints. Many applied problems can be framed like this – for example, a business may have a variety of raw materials and need to determine the most profitable inventory of products that they can produce from those materials. This would ultimately amount to maximizing a linear expression for profit, subject to a linear inequality for each type of raw material (the total amount of raw material used in the products produced must be less than or equal to the amount of raw material available).
At its core, the simplex method is just algebra that can be implemented elegantly using array manipulations. Let’s work through the algebra on an example:
Step 1: Introduce Slack Variables
Math is generally easier when we work with equations (rather than inequalities). We can convert most of the system above into equations if we move everything to the RHS and then assign those RHS expressions to new variables called slack variables.
The slack variables that we created are $x_4, x_5, x_6.$ Note that the original system is entirely equivalent to the definition of the slack variables. The system involving slack variables is exactly the same as the original system, but written a little differently. (This is analogous to change of variables in integration.)
Step 2: Evaluate the Objective Quantity
Each system in the above form is associated with a “guess” that comes from setting all the basic variables equal to $0.$ The basic variables are the variables that appear in the expressions for other non-basic variables.
In our case, the basic variables are $x_1, x_2, x_3$ because they appear in the quantity we’re trying to maximize and also in the expressions for the non-basic variables $x_4, x_5, x_6.$ You can also remember that the basic variables are the RHS and the non-basic variables are the LHS.
When we set the basic variables equal to $0,$ we plug
into the quantity that we’re trying to maximize (known as the objective quantity) and get
So currently, we have a guess $x_1 = x_2 = x_3 = 0$ and this brings the objective quantity to $0.$ This is a pretty bad guess, but we’re going to repeatedly improve it until it’s optimal.
Step 3: Identify the Basic Variable with the Greatest Slope
We’re going to improve upon our guess by repeatedly increasing the basic variable that will most quickly improve our guess.
To improve our guess, we need to increase the objective quantity $x_1 + 2x_2 + x_3,$ which we write as a function below:
The basic variable that will most quickly improve our guess is the one that will make the objective quantity increase the fastest (when we increase that variable). In other words, it is the one with the greatest slope.
Here, the basic variable with the largest slope is $x_2.$ So this what we will increase.
Step 4: Identify which Non-Basic Variable to Trade for that Basic Variable
The basic variable with the largest slope is $x_2.$ We will now trade this for one of the non-basic variables ($x_4, x_5, x_6$).
For our next guess, we want to increase $x_2$ as much as possible (since doing so will increase our objective quantity the fastest). But we can’t increase it too much – remember that the system has constraints, and we can’t invalidate those constraints.
Each of the constraints of the original system was converted into a non-basic variable. So, we need to identify the non-basic variable that places the strictest constraint on $x_2,$ and then trade it with $x_2$ (i.e. make that variable basic in exchange for making $x_2$ non-basic).
Here is where we are so far:
Since $0 \leq x_4, x_5, x_6,$ we have the following constraints:
Let’s simplify a bit:
Now, let’s set move $x_2$ to the LHS of each constraint and set the other basic variables ($x_1,x_3$) equal to zero so that we can see which constraint is strictest:
The strictest constraint is the constraint that places the lowest non-negative upper bound on $x_2.$ Here, the third constraint is the strictest, and if you look at the work backwards, you’ll see that it came from the non-basic variable $x_6{:}$
So, we need to trade the non-basic variable $x_6$ for the basic variable $x_2.$
Step 5: Execute the Trade
This step will feel more familiar than the earlier steps. We have an equation relating $x_6$ and $x_2{:}$
All we need to do is solve this equation for $x_2$ and then substitute that into our system.
Solving for $x_2,$ we get
Then, we substitute into our system. The only catch is that we need to fully replace the $x_6 = $ equation with the $x_2 = $ equation. (This equation is supposed to capture the relationship between $x_2$ and $x_6$, and if we just substituted $x_2$ in the RHS, then it would simplify to a useless equation $x_6 = x_6$).
Just to keep things tidy, we’ll re-order the equations so that the LHS variables are sorted:
Step 6: Repeat Steps 2-5 Until No Slopes Are Positive
Evaluate the objective quantity. We will evaluate the objective quantity for our new guess. Remember that our guess is obtained by setting the basic variables equal to $0.$ The basic variables are now $x_1, x_3, x_6.$ Setting these variables equal to $0,$ our objective quantity becomes
So our guess has improved! Our previous guess gave us an objective quantity of $0,$ and our new guess gave us an objective quantity of $12.$
Sanity Checks
In this example, if you ever get a new guess that does not appear to increase the objective quantity, then something went wrong. The new guess should always increase the objective quantity. (In rare cases, it’s possible for the simplex algorithm to “cycle” around suboptimal minima that yield the same value of the objective function, but this example is not one of those cases.)
Additionally, you should always verify that your guess satisfies the constraints of the original problem statement. Below is an explanation of how to do that. As a reminder, the original problem statement was to maximize $x_1 + 2x_2 + x_3$ subject to the following constraints:
The original problem statement is framed in terms of variables $x_1,x_2,x_3.$ The slack variables $x_4,x_5,x_6$ are artificial things that we made up. Here, our guess is $x_1 = x_3 = x_6 = 0.$ Although $x_2$ is not explicit in our guess, we can find it by substituting our guess into our system. Doing so, we find
So in terms of the original variables of our system, our guess is
Indeed, if we evaluate the objective quantity shown in the problem statement, we get
Additionally, the guess $x_1=0, \, x_2 = 6, \, x_3 = 0$ satisfies all constraints in the original problem statement.
Another Iteration
Identify the basic variable with the largest slope. Our objective function is now
and the basic variable with the largest slope is $x_1.$
Identify which non-basic variable to trade for that basic variable. With a bit of algebra work (which you should do on your own), we have the following constraints:
The second and third constraints are the strictest. They are equally strict, so we can choose to proceed with either one. Referring back to our system (shown below), these two constraints come from $x_4$ and $x_5.$
Let’s proceed with the $x_4$ constraint since it’s simpler (it contains fewer variables).
Execute the trade. Solving for $x_1$ in terms of $x_4$ (which you should do on your own), we get
I’ll leave it to you to finish the trade execution by substituting into your system.
In the next round, you should find that your guess has improved, but that no slopes are positive, which means you’re done and have found the optimal solution.
Array Manipulations
The algebra above can be implemented more elegantly as array manipulations. Let’s start by taking the system that we obtained after introducing slack variables, and converting it to an array:
From the array, we can tell that $x_2$ has the largest slope since it has the largest entry in the top row. To find the row that places the strictest constraint on $x_2,$ we just divide the constant column by the $x_2$ column:
The bottom row places the strictest constraint. To execute the trade, we perform elementary row operations using the bottom row to kill off the rest of the $x_2$ column. (That is, we divide the bottom row so that the entry in the $x_2$ column becomes $1,$ and then we subtract multiples of the bottom row from the other rows.)
This matches up with the system that we got after the first iteration when we worked out the algebra (shown below). The only catch is that the $-12$ in the “constant” column really means that we’ve reached a maximum of positive $12.$ (This happens because the constant column usually represents the constant once it’s been moved to the other side of the equality sign, but there is no equality sign in the expression that we’re trying to maximize.)
Remember that the array manipulations are just implementing algebra that we’ve already worked through, so if you get confused or stuck when implementing the simplex method, it will help to go back to the algebra and make sure that every array manipulation you do matches up against the algebra.
Exercises
- Work out the example that was covered, using algebra, on paper.
- Write all the relevant array manipulations next to the algebra.
- Implement the simplex method in code. The input should be the first array that you write down, and the output should be the last array that you wrote down.
- Work out the array manipulations manually for the new system shown below, and then ensure that your implementation gives the same result. Note that when you are identifying the strictest constraint, if you ever get a constraint of that involves a comparison to a negative number, then you can discard it (since we already know the variables are non-negative). This should only require you to work out $3$ iterations (i.e. your $4$th guess will be the final one).
$\begin{align*} \textrm{maximize } 20x_1 + 10x_2 + 15x_3 & \textrm{ such that} \\ 3x_1 + 2x_2 + 5x_3 &\leq 55 \\ 2x_1 + x_2 + x_3 &\leq 26 \\ x_1 + x_2 + 3x_3 &\leq 30 \\ 5x_1 + 2x_2 + 4x_3 &\leq 57 \\ x_1, \, x_2, \, x_3 &\geq 0. \end{align*}$
This post is part of the book Introduction to Algorithms and Machine Learning: from Sorting to Strategic Agents. Suggested citation: Skycak, J. (2021). Simplex Method. In Introduction to Algorithms and Machine Learning: from Sorting to Strategic Agents. https://justinmath.com/simplex-method/
Want to get notified about new posts? Join the mailing list and follow on X/Twitter.