The goal of linear programming is to optimize a linear function
subject to linear constraints. In its most general form,
a linear program is an optimization problem
Maximize
|
Σ ci xi |
---|
subject to |
lhsi ≤ Aix
≤
rhsi |
|
li ≤
xi ≤
ui |
A typical linear program can have many thousands of variables and several thousand constraints.
Linear programs use a specialized kind of decision variable,
LinearProgramVariable.
Since the objective function is linear, it is specified fully by the coefficient of each variable,
usually called the cost. It can be set or retrieved through the
Cost property.
By convention, linear program variables have a lower bound of zero by default.
Linear programs may have variables that are restricted to be integers. Integer variables can be
created using the model's AddIntegerVariable
method. If the variable can only take on the values 0 or 1, the
AddBinaryVariable
method provides a shortcut. Existing variables can be restricted to integer values by setting the
IsInteger
property to true.
The LinearConstraint class is
used to represent constraints in a linear program. Every constraint must have a unique name, which can be accessed
through the Name
property.
A constraint is a linear combination of variables that is required to lie between specified boundaries. The
LowerBound and
UpperBound
properties define these boundaries. When a constraint is not bounded from above, its upper bound is DoublePositiveInfinity. When a constraint is not bounded from below, its lower
bound is DoubleNegativeInfinity.
Variables and constraints can be accessed through the linear program's
Variables and
Constraints collections.
These collections are indexed by position and by name.
A linear program is represented by the LinearProgram class. There are three ways
to construct LinearProgram
objects.
Defining linear programs using constructors
The quickest way to define a linear program is by passing the vectors and matrices that appear in the definition
above to a constructor. There are three constructors that can be used in this way.
The first constructor takes three arguments. It defines a linear program in standard form. This is a linear
program where variables are constrained to be positive, and all constraints are inequalities with an upper bound. The
first parameter is a VectorT containing the coefficients
of the objective function. It corresponds to the vector c in the definition. The second argument is a
MatrixT that contains the coefficients of the
inequalities. It corresponds to the matrix A in the definition. The third argument is a VectorT containing the right-hand-sides of the inequalities. The
code below creates the following a linear program:
|
s.t. | x1 + 2x2 ≤ 5
|
|
5x1 + 3x2 ≤ 11
|
Vector<double> c = Vector.Create(1.0, 1.0);
Matrix<double> A = Matrix.Create(2, 2,
new double[] { 1.0, 5.0, 2.0, 3.0 },
MatrixElementOrder.RowMajor);
Vector<double> b = Vector.Create(5.0, 11.0);
LinearProgram lp1 = new LinearProgram(c, A, b);
Dim c = Vector.Create(1.0, 1.0)
Dim A = Matrix.Create(2, 2,
New Double() {1.0, 5.0, 2.0, 3.0},
MatrixElementOrder.RowMajor)
Dim b = Vector.Create(5.0, 11.0)
Dim lp1 As LinearProgram = New LinearProgram(c, A, b)
No code example is currently available or this language may not be supported.
let c = Vector.Create(1.0, 1.0)
let A = Matrix.Create(2, 2, [| 1.0; 5.0; 2.0; 3.0 |], MatrixElementOrder.RowMajor)
let b = Vector.Create(5.0, 11.0)
let lp1 = LinearProgram(c, A, b)
The second constructor adds a fourth parameter. If present, this parameter indicates the number of equality
constraints. The equality constraints appear above the inequality constraints.
The third constructor is the most flexible. It has six parameters in all. The first two are once again the cost
coefficients and the coefficient matrix. The third and fourth parameters are vectors that contain the lower and upper
bounds of the constraints. Each type of constraint can be expressed using a lower and upper bound, as shown in the
table below.
Types of Constraints and their bounds
Constraint | Lower bound | Upper bound |
---|
c ≤ rhs | -infinity | rhs |
c ≥ rhs | rhs | +infinity |
c = rhs | rhs | rhs |
lhs ≤ c ≤ rhs | lhs | rhs |
The fifth and sixth parameters are vectors that contain the lower and upper bound for the variables. The different
types of constraints on variables can be expressed the same way as for constraints. The following code sample builds
the following linear program:
|
s.t. |
0.5 ≤ x1 + x2 ≤ 1.5
|
|
0 ≤ x1 ≤ 1
|
|
0 ≤ x2 ≤ 1
|
Vector<double> c = Vector.Create(-1.0, -3.0);
Matrix<double> A = Matrix.Create(1, 2, new double[] { 1.0, 1.0 },
MatrixElementOrder.RowMajor);
Vector<double> lc = Vector.Create(0.5);
Vector<double> uc = Vector.Create(1.5);
Vector<double> lx = Vector.Create(0.0, 0.0);
Vector<double> ux = Vector.Create(1.0, 1.0);
LinearProgram lp2 = new LinearProgram(c, A, lc, uc, lx, ux);
Dim c As Vector(Of Double) = Vector.Create(-1.0, -3.0)
Dim A As Matrix(Of Double) = Matrix.Create(1, 2, New Double() {1.0, 1.0},
MatrixElementOrder.RowMajor)
Dim lc As Vector(Of Double) = Vector.Create(0.5)
Dim uc As Vector(Of Double) = Vector.Create(1.5)
Dim lx As Vector(Of Double) = Vector.Create(0.0, 0.0)
Dim ux As Vector(Of Double) = Vector.Create(1.0, 1.0)
Dim lp2 As LinearProgram = New LinearProgram(c, A, lc, uc, lx, ux)
No code example is currently available or this language may not be supported.
let c = Vector.Create(-1.0, -3.0)
let A = Matrix.Create(1, 2, [| 1.0; 1.0 |], MatrixElementOrder.RowMajor)
let lc = Vector.Create(0.5)
let uc = Vector.Create(1.5)
let lx = Vector.Create(0.0, 0.0)
let ux = Vector.Create(1.0, 1.0)
let lp2 = LinearProgram(c, A, lc, uc, lx, ux)
Building linear programs from scratch
The LinearProgram class also
has a default constructor that creates an empty linear program. You can then call the linear program's AddVariable and AddLinearConstraint methods.
The AddVariable
method has two overloads. The first takes two arguments. The first is the name of the variable. The second is the cost
associated with the variable. The second overload has two additional parameters that specify the lower and upper
bound of the variable. If no values are specified, a lower bound of zero and an upper bound of infinity are
assumed.
The AddLinearConstraint method has
four overloads. The first takes four parameters. The first is the name of the constraint. The second is a VectorT that contains the coefficients of the variables. The third
parameter is a ConstraintType
value that specifies the type of constraint. The possible values are listed in the table below. The last parameter is
the right-hand side of the constraint.
ConstraintType values
Value | Description |
---|
Equal | c = rhs |
GreaterThanOrEqual | c ≥ rhs |
LessThanOrEqual | c ≤ rhs |
The second overload also takes four parameters. The first two are the same as before. The third and fourth
parameters are the lower and upper bound of the constraint. The different types of constraints can be specified
according to the examples in Table 1.
The third and fourth overloads are similar to the first two. The only difference is that the coefficients are
specified using a Double array instead of a vector.
The following example creates a linear program that is identical to the last example:
LinearProgram lp3 = new LinearProgram(c, A, lc, uc, lx, ux);
lp3.AddVariable("X1", -1, 0.0, 1.0);
lp3.AddVariable("X2", -3, 0.0, 1.0);
lp3.AddLinearConstraint("C1", new double[] { 1.0, 1.0 }, 0.5, 1.5);
Dim lp3 As LinearProgram = New LinearProgram(c, A, lc, uc, lx, ux)
lp3.AddVariable("X1", -1, 0.0, 1.0)
lp3.AddVariable("X2", -3, 0.0, 1.0)
lp3.AddLinearConstraint("C1", New Double() {1.0, 1.0}, 0.5, 1.5)
No code example is currently available or this language may not be supported.
let lp3 = LinearProgram(c, A, lc, uc, lx, ux)
lp3.AddVariable("X1", -1.0, 0.0, 1.0) |> ignore
lp3.AddVariable("X2", -3.0, 0.0, 1.0) |> ignore
lp3.AddLinearConstraint("C1", [| 1.0; 1.0 |], 0.5, 1.5) |> ignore
Although not strictly necessary, it is advisable to create the variables first, followed by the constraints. If
the vector containing the coefficients for a constraint is longer than the number of variables that have been
defined, additional variables are added automatically. The kth variable is given the name
x<g>.
Whenever possible, constraints on variables should be preferred over explicit linear program constraints. The time
to solve a linear program depends most heavily on the number of constraints.
Importing linear programs: the MPS format
Use the MpsReader class to read a
LinearProgram from a file in MPS
format. The format was named after an early linear programming system from IBM. It has since become a de facto
standard for defining linear programs in text format in commercial linear programming systems. There are many
online resources
that describe the format in detail.
The MpsReader class has one
method, Read, which is overloaded. The first
method takes a string containing the path to an MPS file and returns a LinearProgram object that represents the
linear program in the file. The second method does the same but gets its input from a System.IOStreamReader.
Several extensions of the MPS format have been defined over the years. The current implementation of MpsReader doesn't support these extensions. A
SystemFormatException is thrown when an unrecognized extension is found.
Once a linear program has been defined, solving it is very straightforward. The Solve method does all the work.
A variation of the revised dual simplex method is used. Depending on the size of the linear program, this method may
take a very long time to complete. Most problems with of the order of hundreds or a few thousand variables and constraints are
typically solved in a fraction of a second. Very large problems, with thousands of variables and constraints, may
take much longer to solve.
The Solve method
returns a VectorT containing the optimal solution. You
should inspect the Status
property to make sure an optimal solution was indeed found. This property is of type
OptimizationModelStatus and can take on
the following values:
Value | Description |
---|
Unknown |
Nothing is known about the solution. This is the status returned before the
SolveModel method is called.
|
Optimal | The linear program was solved successfully and the solution that was returned is the optimal solution. |
Infeasible | The linear program does not have a solution because some of the constraints conflict with each other. |
Unbounded | The linear program does not have a finite solution. The cost function can be made arbitrarily small. |
Two other status values, Feasible and DualFeasible are used while solving the linear
program. Finally, the OptimalValue property returns the
value of the cost function at the solution. The GetDualSolution method
returns a VectorT containing the solution to the dual
problem.
The code below solves the linear program defined earlier:
Vector<double> x = lp1.Solve();
Vector<double> y = lp1.GetDualSolution();
Console.WriteLine("Status: {0}", lp1.Status);
Console.WriteLine("Primal: {0:F1}", x);
Console.WriteLine("Dual: {0:F1}", y);
Console.WriteLine("Optimal value: {0:F1}", lp1.OptimalValue);
Dim x As Vector(Of Double) = lp1.Solve()
Dim y As Vector(Of Double) = lp1.GetDualSolution()
Console.WriteLine("Status: {0}", lp1.Status)
Console.WriteLine("Primal: 0:F1", x)
Console.WriteLine("Dual: 0:F1", y)
Console.WriteLine("Optimal value: 0:F1", lp1.OptimalValue)
No code example is currently available or this language may not be supported.
let x = lp1.Solve()
let y = lp1.GetDualSolution()
Console.WriteLine("Status: 0", lp1.Status)
Console.WriteLine("Primal: 0:F1", x)
Console.WriteLine("Dual: 0:F1", y)
Console.WriteLine("Optimal value: 0:F1", lp1.OptimalValue)