Goal programming (GP) is an extension of Linear Programming (LP). Essentially GP is LP with multiple objectives. The value of goal programming comes from a need optimize conflicting objectives which do not have a feasible solution, and where information to devise an accurate objective function is absent. In both of these cases, goal programming can be used to satisfy objectives as best as possible. Goal programming was first used by Dr. Cooper in 1955.
Dr. Cooper was a busy man. It is hard to list all of the chapters he was a president of. For a bit of perspective, he received four honorary degrees: an M.A. from Harvard University in 1976, and honorary doctorates from Ohio State University in 1970, Carnegie Mellon in 1982, and the University of Alicante in 1995. Overall this man was a force to be reckoned with in the fields of Operations Research, Management Science, Business, and Accounting. With over 500 research articles and 27 books in his career, this man was the apotome of an expert.
Lets take a step back. The foundation of GP is Linear Programming. LP problems seek to optimize some utility function which is bound by constraints.
$$ \text{where } \quad \vec{x}_j \geq 0 \quad \text{for } j = 1, \dots, n $$
Problem statement: Maximize profit by choosing how many stools and chairs to make.
There are chairs and stools: \(n\) = 2. Let \( x_1\) be the number of chairs to build and \( x_2\) the number of stools to build.
There are rods and wooden sheets: \(m\) = 2. Let \(i = 1\) represent the wooden sheet, and \(i = 2\) represent the rods.
There are three dominant vertices to the 2D solution space. One of these three vertices is the optimal solution. Like in many problems, integer values are troublesome. For simplicity, we can just round down each of these points and calculate the profit from each of them. This gives near optimal solutions.
Great! If we had a bigger problem or our results were more important, we could use the branch and bound method for solving an integer programming (IP) problem. Here I have implemented it in code:
# JuliaLang is very nice for solving optimization problems.
using JuMP, HiGHS
# HiGHS is overkill for this but it works.
model = Model(HiGHS.Optimizer)
# We are initializing the variables as Integers.
# Technically this is not a Linear Programming problem.
@variable(model, x1 >= 0, Int)
@variable(model, x2 >= 0, Int)
# Setting constraint for the wooden sheet
@constraint(model, 4 * x1 + 2 * x2 <= 75)
# Setting the constraint for the rods
@constraint(model, 4 * x1 + 3 * x2 <= 100)
# Our objective function is to maximize this equation
# By changing x1 and x2, such that
# The constraints are still true.
@objective(model, Max, 50 * x1 + 35 * x2)
# Use the branch-and-bound technique to solve this IP
optimize!(model)
# Getting the saved values from the model
x1_opt = value(x1)
x2_opt = value(x2)
optimal_profit = objective_value(model)
println("Optimal x1: ", x1_opt) # x_1 = 4
println("Optimal x2: ", x2_opt) # x_2 = 28
println("Optimal profit: ", optimal_profit) # Profit = $1,180
The actual optimal solution is the closest integer point to the LP solution which also happens to be an intercept of the two constraints.
While there are two methodologies to Goal Programming, we will only be practicing one.
Preemptive: Also known as lexicographical, Goals are ranked by their priorities. Goals at a higher priority are infinitely more important than another goal at a lower priority. The model is solved top down, with the highest ranked goal solved first.
Non-preemptive: Also known as weighted, all objectives are reduced to some common unit and "mashed" together to be solved as one system.
We will be focusing on the preemptive methodology. Both methods must have a decision maker choose which goals have priority, but the preemptive methodology is more communicative of this, and there isn't an issue of finding common units between uncommon objectives.
Goal programming shines when there is no feasible solution satisfying all of the goals. Typically it is employed when objectives are working against each other. Goals and constraints can both be used on GP. Our example problem below will show how constraints and objectives come together to make an infeasible problem which goal programming will "solve".
Our sample problem is a mixture problem. Working with fluids is a nice way to not have to dance around integer values. And Factorio makes oil processing seem somehow fun.
A refinery blends two types of crude oil: Oil A and Oil B. The Refinery sells two types of Gasoline: Regular and Premium. The higher ups have decided that the priorities for oil production are ordered as follows:
Additionally, in order to sell the gas, the sulfur content must be below certain limits.
The following information is known:
As the decision maker, we want to say with some certainty what combination of Oil A and Oil B should be used to satisfy the objectives. Not all objectives will necessarily fit together neatly, some sacrifices will have to be made. While the solutions should try to attain all of the goals, there is no feasible solution which does so.
Note the deviation \(d_1^{+}\) communicates the positive deviation (how much the LHS exceeds the RHS) in the first constraint, and \(d_1^{-}\) communicates how much the RHS exceeds the LHS in the first constraint. When optimizing constraints with a \(\geq\) equality, we want to minimize the \(d_1^{-}\) so that the values are as close to the goal as possible. There is no value added in exceeding the goals. Sometimes analysts will include a penalty for not achieving goals, but it is not strictly necessary.
Download Goal Programming Example
We want to find values of \(x_1\) and \(x_2\) which satisfy our constraints and meet as many of the goals as possible. To do this, we will use the excel solver.
a) First we want to see if we can meet the first goal (regular gasoline) volume. We want to minimize deviations BELOW the target, as it is a \(\geq\) constraint.
b) Next, we want to see if we can meet the second goal (premium gasoline) volume while maintaining our deviation of 0 for goal 1. We want to minimize deviations BELOW the target, as it is a \(\geq\) constraint.
c) Finally, we want to see if we can meet the third goal (cost of oil) while maintaining our deviation of 0 for goal 1, and 500 for goal 2. We want to minimize deviations ABOVE the target, as it is a \(\leq\) constraint.
Now we have the optimal lexicographical solution the for the decision values relative to the problem statement. We should use 6250 units of oil A and 1250 units of oil B. We do not attain goals 2 or 3. The sulfur concentration for premium gasoline is a bounding constraint; it has no slack. The sulfur concentration for regular gasoline has .25% of slack.
What is good about this approach?
What is not good about this approach?
Should goals be revamped to become more feasible?
What if Oil A and Oil B had different rates or costs? Theoretically, how could we adjust them to be more feasible?
You work at an advertising agency. A customer has identified three primary target audiences they are trying to reach, and has an advertising budget of $600,000. They have expressed their targets in the form of three goals:
Type (per minute) | HIM | LIP | HIW | Cost |
---|---|---|---|---|
Football | 7 million | 10 million | 5 million | $100,000 |
Soap Opera | 3 million | 5 million | 4 million | $60,000 |
Formulate the Goals and Constraints for this problem
Formulate System with deviations
LINDO formulation
MIN d1M + d2M + d3M
ST
HIM) 7x1 + 3x2 + d1M - D1P = 40
LIP) 10x1 + 5x2 + d2M - d2P = 60
HIW) 5x1 + 4x2 + d3M - D1M = 35
Budget) 100x1 + 60x2 <= 600