LINEAR_PROG
Overview
The LINEAR_PROG function solves linear programming (LP) problems, which involve minimizing a linear objective function subject to linear equality and inequality constraints. Linear programming is one of the most widely used optimization techniques in operations research, economics, logistics, and engineering for resource allocation, production planning, and scheduling problems.
This implementation uses SciPy’s linprog function from the scipy.optimize module. The standard form of a linear programming problem is:
\begin{aligned}
\min_{x} \quad & c^T x \\
\text{subject to} \quad & A_{ub} x \leq b_{ub} \\
& A_{eq} x = b_{eq} \\
& l \leq x \leq u
\end{aligned}
where x is the vector of decision variables, c contains the objective function coefficients, A_{ub} and b_{ub} define inequality constraints, A_{eq} and b_{eq} define equality constraints, and l and u specify variable bounds.
The function supports multiple solver algorithms. The default highs method automatically selects between HiGHS dual simplex (highs-ds) and interior-point (highs-ipm) solvers—these are high-performance C++ implementations that are particularly efficient for large, sparse problems. Legacy methods including interior-point, revised simplex, and simplex (Dantzig’s original tableau method) are also available.
The simplex algorithm, introduced by George Dantzig in 1947, traverses vertices of the feasible polytope to find the optimal solution. Interior-point methods take a different approach, moving through the interior of the feasible region using techniques derived from barrier functions. For most practical applications, the HiGHS solvers provide the best performance and numerical stability.
The function returns the optimal values of decision variables along with the minimized objective function value. For more details on the underlying algorithms and parameters, see the SciPy linprog documentation and the HiGHS project.
This example function is provided as-is without any representation of accuracy.
Excel Usage
=LINEAR_PROG(c, A_ub, b_ub, A_eq, b_eq, bounds, linear_prog_method)
c(list[list], required): Objective function coefficients as a 2D list (row or column vector).A_ub(list[list], optional, default: null): Coefficient matrix for inequality constraints (A_ub @ x <= b_ub).b_ub(list[list], optional, default: null): Right-hand side vector for inequality constraints.A_eq(list[list], optional, default: null): Coefficient matrix for equality constraints (A_eq @ x = b_eq).b_eq(list[list], optional, default: null): Right-hand side vector for equality constraints.bounds(list[list], optional, default: null): Variable bounds as [lower, upper] pairs per variable. Use null for unbounded.linear_prog_method(str, optional, default: “highs”): Solver algorithm to use.
Returns (list[list]): 2D list [[x1, x2, …, objective]], or error message string.
Examples
Example 1: Maximization with inequality constraints and bounds
Inputs:
| c | A_ub | b_ub | bounds | linear_prog_method | |||
|---|---|---|---|---|---|---|---|
| 3 | 5 | -1 | -2 | -8 | 0 | 100 | highs-ipm |
| -2 | -1 | -8 | 0 | 100 |
Excel formula:
=LINEAR_PROG({3,5}, {-1,-2;-2,-1}, {-8;-8}, {0,100;0,100}, "highs-ipm")
Expected output:
| Result | ||
|---|---|---|
| 2.666666666666667 | 2.6666666666666665 | 21.333333333333332 |
Example 2: Minimization with single inequality constraint
Inputs:
| c | A_ub | b_ub | ||
|---|---|---|---|---|
| -1 | -1 | 1 | 2 | 10 |
Excel formula:
=LINEAR_PROG({-1,-1}, {1,2}, 10)
Expected output:
| Result | ||
|---|---|---|
| 10 | 0 | -10 |
Example 3: Single variable linear program
Inputs:
| c | A_ub | b_ub |
|---|---|---|
| 2 | -1 | -4 |
Excel formula:
=LINEAR_PROG(2, -1, -4)
Expected output:
| Result | |
|---|---|
| 4 | 8 |
Example 4: Two independent inequality constraints
Inputs:
| c | A_ub | b_ub | ||
|---|---|---|---|---|
| 1 | 1 | 1 | 0 | 5 |
| 0 | 1 | 3 |
Excel formula:
=LINEAR_PROG({1,1}, {1,0;0,1}, {5;3})
Expected output:
| Result | ||
|---|---|---|
| 0 | 0 | 0 |
Python Code
import numpy as np
from scipy.optimize import linprog as scipy_linprog
def linear_prog(c, A_ub=None, b_ub=None, A_eq=None, b_eq=None, bounds=None, linear_prog_method='highs'):
"""
Solve a linear programming problem using SciPy's `linprog` function.
See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.linprog.html
This example function is provided as-is without any representation of accuracy.
Args:
c (list[list]): Objective function coefficients as a 2D list (row or column vector).
A_ub (list[list], optional): Coefficient matrix for inequality constraints (A_ub @ x <= b_ub). Default is None.
b_ub (list[list], optional): Right-hand side vector for inequality constraints. Default is None.
A_eq (list[list], optional): Coefficient matrix for equality constraints (A_eq @ x = b_eq). Default is None.
b_eq (list[list], optional): Right-hand side vector for equality constraints. Default is None.
bounds (list[list], optional): Variable bounds as [lower, upper] pairs per variable. Use null for unbounded. Default is None.
linear_prog_method (str, optional): Solver algorithm to use. Valid options: HiGHS, HiGHS Dual Simplex, HiGHS Interior Point, Interior Point, Revised Simplex, Simplex. Default is 'highs'.
Returns:
list[list]: 2D list [[x1, x2, ..., objective]], or error message string.
"""
def to2d(x):
return [[x]] if not isinstance(x, list) else x
def conv(mat, name, cols=None):
if mat is None:
return None
if not isinstance(mat, list) or len(mat) == 0:
return f"Invalid input: {name} must be a 2D list with at least one row."
out = []
for ri, row in enumerate(mat):
if not isinstance(row, list) or len(row) == 0:
return f"Invalid input: each row in {name} must be a non-empty list."
if cols is not None and len(row) != cols:
return f"Invalid input: {name} must have {cols} columns to match the number of variables."
r = []
for ci, v in enumerate(row):
try:
n = float(v)
except Exception:
return f"Invalid input: {name} must be numeric. Problem at row {ri+1}, column {ci+1}."
if not np.isfinite(n):
return f"Invalid input: {name} must contain finite values. Problem at row {ri+1}, column {ci+1}."
r.append(n)
out.append(r)
return np.asarray(out, dtype=float)
c = to2d(c)
A_ub = to2d(A_ub) if A_ub is not None else None
b_ub = to2d(b_ub) if b_ub is not None else None
A_eq = to2d(A_eq) if A_eq is not None else None
b_eq = to2d(b_eq) if b_eq is not None else None
cv = conv(c, "c")
if isinstance(cv, str):
return cv
c_vec = cv.flatten()
if c_vec.size == 0:
return "Invalid input: c must contain at least one coefficient."
if A_ub is not None and b_ub is None:
return "Invalid input: b_ub must be provided when A_ub is specified."
if b_ub is not None and A_ub is None:
return "Invalid input: A_ub must be provided when b_ub is specified."
A_ub_m = conv(A_ub, "A_ub", cols=c_vec.size)
if isinstance(A_ub_m, str):
return A_ub_m
if b_ub is not None:
bv = conv(b_ub, "b_ub")
if isinstance(bv, str):
return bv
if A_ub_m is not None and A_ub_m.shape[0] != bv.flatten().size:
return "Invalid input: Number of rows in A_ub must equal the length of b_ub."
b_ub_v = bv.flatten()
else:
b_ub_v = None
if A_eq is not None and b_eq is None:
return "Invalid input: b_eq must be provided when A_eq is specified."
if b_eq is not None and A_eq is None:
return "Invalid input: A_eq must be provided when b_eq is specified."
A_eq_m = conv(A_eq, "A_eq", cols=c_vec.size)
if isinstance(A_eq_m, str):
return A_eq_m
if b_eq is not None:
bev = conv(b_eq, "b_eq")
if isinstance(bev, str):
return bev
if A_eq_m is not None and A_eq_m.shape[0] != bev.flatten().size:
return "Invalid input: Number of rows in A_eq must equal the length of b_eq."
b_eq_v = bev.flatten()
else:
b_eq_v = None
bounds_seq = None
if bounds is not None:
if not isinstance(bounds, list) or len(bounds) != c_vec.size:
return "Invalid input: bounds must supply one [min, max] pair per variable."
bb = []
for i, p in enumerate(bounds):
if not isinstance(p, list) or len(p) != 2:
return "Invalid input: each bounds entry must be a [min, max] pair."
lo = float(p[0]) if p[0] is not None else None
hi = float(p[1]) if p[1] is not None else None
if lo is not None and not np.isfinite(lo):
return f"Invalid input: bounds lower value at index {i+1} must be finite or None."
if hi is not None and not np.isfinite(hi):
return f"Invalid input: bounds upper value at index {i+1} must be finite or None."
if lo is not None and hi is not None and lo > hi:
return f"Invalid input: lower bound must not exceed upper bound for variable {i+1}."
bb.append((lo, hi))
bounds_seq = tuple(bb)
solver = linear_prog_method.lower() if isinstance(linear_prog_method, str) else "highs"
valid = {"highs", "highs-ds", "highs-ipm", "interior-point", "revised simplex", "simplex"}
if solver not in valid:
return ("Invalid input: linear_prog_method must be one of 'highs', 'highs-ds', 'highs-ipm', "
"'interior-point', 'revised simplex', or 'simplex'.")
try:
res = scipy_linprog(c=c_vec, A_ub=A_ub_m, b_ub=b_ub_v, A_eq=A_eq_m, b_eq=b_eq_v, bounds=bounds_seq, method=solver)
except Exception as e:
return f"linear_prog error: {e}"
if not getattr(res, "success", False) or res.x is None:
msg = getattr(res, "message", "Solver failed.")
return f"linear_prog failed: {msg}"
return [[float(x) for x in res.x] + [float(res.fun)]]