Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Mathematical Programming (Python)

Maintained by the JuliaQUBO organization
SECQUOIA  ·  PSR Energy

Open In Colab

Setup

Google Colab

Click the badge above to open this notebook in Colab. The notebook installs or activates dependencies in the setup cells below.

Local installation

Run the following from the repository root before opening this notebook locally:

uv sync --locked --group docs --group mathprog
uv run --locked --group docs --group mathprog idaes get-extensions --release 3.4.2 --to .nbverify/mathprog-solvers
export PATH="$PWD/.nbverify/mathprog-solvers:$PATH"
make verify-mathprog-python-local

Install GLPK separately as described below; the Ubuntu command also installs the IDAES runtime libraries. CBC, IPOPT, BONMIN, and Couenne come from the IDAES solver bundle. All five solvers are required: a missing executable or failed solve stops verification. The local target executes the complete notebook under warnings-as-errors and writes fresh results to .nbverify/; no credentials are needed.

See local-setup.md for the full local workflow, including optional commercial solver licences.

GLPK (required for ILP sections):

  • macOS: brew install glpk

  • Ubuntu/Debian: sudo apt-get install glpk-utils libgfortran5 libgomp1 liblapack3 libblas3

  • Windows: download from http://winglpk.sourceforge.net/

Learning objectives

By the end of this notebook you will be able to:

  1. Formulate linear, integer, convex nonlinear, and nonconvex nonlinear programs from a word problem.

  2. Implement the same optimization model family in Pyomo with continuous and integer decision variables.

  3. Select appropriate LP, MILP, NLP, and MINLP solvers and interpret their reported solutions.

  4. Explain how integrality and nonconvexity change the difficulty and reliability of optimization workflows.

Prerequisites

Mathematical background: Basic algebra, inequalities, objective functions, and familiarity with continuous and integer variables.
Prior notebooks: None; this is the first Python notebook in the sequence.
Accounts required: None. Every solver this notebook uses is open source.
Python version: Python 3.9+ with the Pyomo stack used by this repository.

Introduction to Mathematical Programming

Modeling

The solution of optimization problems requires the development of a mathematical model. Here we will model an example given in the lecture and see how an integer program can be solved practically. This example will use as modeling language Pyomo, an open-source Python package, which provides a flexible access to different solvers and a general modeling framework for linear and nonlinear integer programs. The examples solved here will make use of open-source solvers GLPK and CLP/CBC for linear and mixed-integer linear programming, IPOPT for interior point (non)linear programming, BONMIN for convex integer nonlinear programming and COUENNE for nonconvex (global) integer nonlinear programming.

Notebook Cell

Problem statement

Suppose there is a company that produces two different products, A and B, which can be sold at different values, $5.5\$5.5 and $2.1\$2.1 per unit, respectively. The company has a single machine with electricity use capped at 17 kW/day. Producing each unit of A and B consumes 8kW/day8\text{kW}/\text{day} and 2kW/day2\text{kW}/\text{day}, respectively. Besides, the company can only produce at most 2 more units of B than A per day.

Linear Programming

This is a valid model, but it would be easier to solve if we had a mathematical representation. Assuming the units produced of A are x1x_1 and of B are x2x_2 we have

max⁡x1,x25.5x1+2.1x2s.t.x2≤x1+28x1+2x2≤17x1,x2≥0\begin{array}{rl} \displaystyle% \max_{x_1, x_2} & 5.5x_1 + 2.1x_2 \\ \textrm{s.t.} & x_2 \le x_1 + 2 \\ & 8x_1 + 2x_2 \le 17 \\ & x_1, x_2 \ge 0 \end{array}
Image produced in Jupyter
1 Var Declarations
    x : Size=2, Index={1, 2}
        Key : Lower : Value : Upper : Fixed : Stale : Domain
          1 :     0 :  None :  None : False :  True : NonNegativeReals
          2 :     0 :  None :  None : False :  True : NonNegativeReals

1 Objective Declarations
    obj : Size=1, Index=None, Active=True
        Key  : Active : Sense    : Expression
        None :   True : maximize : 5.5*x[1] + 2.1*x[2]

2 Constraint Declarations
    Constraint1 : Size=1, Index=None, Active=True
        Key  : Lower : Body              : Upper : Active
        None :  -Inf : x[2] - (x[1] + 2) :   0.0 :   True
    Constraint2 : Size=1, Index=None, Active=True
        Key  : Lower : Body            : Upper : Active
        None :  -Inf : 8*x[1] + 2*x[2] :  17.0 :   True

4 Declarations: x obj Constraint1 Constraint2
Notebook Cell
GLPK: OK
CBC: OK
GLPSOL--GLPK LP/MIP Solver 5.0
Parameter(s) specified in the command line:
 --write <TEMPORARY_PATH> --wglp <TEMPORARY_PATH> --cpxlp
 <TEMPORARY_PATH>
Reading problem data from '<TEMPORARY_PATH>'...
2 rows, 2 columns, 4 non-zeros
23 lines were read
Writing problem data to '<TEMPORARY_PATH>'...
15 lines were written
GLPK Simplex Optimizer 5.0
2 rows, 2 columns, 4 non-zeros
Preprocessing...
2 rows, 2 columns, 4 non-zeros
Scaling...
 A: min|aij| =  1.000e+00  max|aij| =  8.000e+00  ratio =  8.000e+00
Problem data seem to be well scaled
Constructing initial basis...
Size of triangular part is 2
*     0: obj =  -0.000000000e+00 inf =   0.000e+00 (2)
*     2: obj =   1.408000000e+01 inf =   0.000e+00 (0)
OPTIMAL LP SOLUTION FOUND
Time used:   0.0 secs
Memory used: 0.0 Mb (39693 bytes)
Writing basic solution to '<TEMPORARY_PATH>'...
13 lines were written
Model Simple example LP

  Variables:
    x : Size=2, Index={1, 2}
        Key : Lower : Value : Upper : Fixed : Stale : Domain
          1 :     0 :   1.3 :  None : False : False : NonNegativeReals
          2 :     0 :   3.3 :  None : False : False : NonNegativeReals

  Objectives:
    obj : Size=1, Index=None, Active=True
        Key  : Active : Value
        None :   True : 14.08

  Constraints:
    Constraint1 : Size=1
        Key  : Lower : Body : Upper
        None :  None :  0.0 :   0.0
    Constraint2 : Size=1
        Key  : Lower : Body : Upper
        None :  None : 17.0 :  17.0
Image produced in Jupyter

We observe that the optimal solution of this problem is x1=1.3x_1 = 1.3, x2=3.3x_2 = 3.3, leading to a profit of 14.08.

Model Simple example LP

  Variables:
    x : Size=2, Index={1, 2}
        Key : Lower : Value : Upper : Fixed : Stale : Domain
          1 :     0 :   1.3 :  None : False : False : NonNegativeReals
          2 :     0 :   3.3 :  None : False : False : NonNegativeReals

  Objectives:
    obj : Size=1, Index=None, Active=True
        Key  : Active : Value
        None :   True : 14.08

  Constraints:
    Constraint1 : Size=1
        Key  : Lower : Body : Upper
        None :  None :  0.0 :   0.0
    Constraint2 : Size=1
        Key  : Lower : Body : Upper
        None :  None : 17.0 :  17.0

The solvers GLPK and CLP implement the simplex method (with many improvements) by default, but we can also use the interior-point solver IPOPT. IPOPT can solve both linear and nonlinear problems.

Notebook Cell
Ipopt 3.13.2: 

******************************************************************************
This program contains Ipopt, a library for large-scale nonlinear optimization.
 Ipopt is released as open source code under the Eclipse Public License (EPL).
         For more information visit http://projects.coin-or.org/Ipopt

This version of Ipopt was compiled from source code available at
    https://github.com/IDAES/Ipopt as part of the Institute for the Design of
    Advanced Energy Systems Process Systems Engineering Framework (IDAES PSE
    Framework) Copyright (c) 2018-2019. See https://github.com/IDAES/idaes-pse.

This version of Ipopt was compiled using HSL, a collection of Fortran codes
    for large-scale scientific computation.  All technical papers, sales and
    publicity material resulting from use of the HSL codes within IPOPT must
    contain the following acknowledgement:
        HSL, a collection of Fortran codes for large-scale scientific
        computation. See http://www.hsl.rl.ac.uk.
******************************************************************************

This is Ipopt version 3.13.2, running with linear solver ma27.

Number of nonzeros in equality constraint Jacobian...:        0
Number of nonzeros in inequality constraint Jacobian.:        4
Number of nonzeros in Lagrangian Hessian.............:        0

Total number of variables............................:        2
                     variables with only lower bounds:        2
                variables with lower and upper bounds:        0
                     variables with only upper bounds:        0
Total number of equality constraints.................:        0
Total number of inequality constraints...............:        2
        inequality constraints with only lower bounds:        0
   inequality constraints with lower and upper bounds:        0
        inequality constraints with only upper bounds:        2

iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
   0 -1.4080000e+01 0.00e+00 1.09e-01  -1.0 0.00e+00    -  0.00e+00 0.00e+00   0
   1 -1.3912219e+01 0.00e+00 1.00e-06  -1.0 1.00e-01    -  1.00e+00 1.00e+00h  1
   2 -1.4069173e+01 0.00e+00 1.12e-03  -2.5 1.33e-01    -  9.84e-01 1.00e+00f  1
   3 -1.4079704e+01 0.00e+00 1.50e-09  -3.8 1.06e-02    -  1.00e+00 1.00e+00f  1
   4 -1.4079996e+01 0.00e+00 1.84e-11  -5.7 2.45e-04    -  1.00e+00 1.00e+00f  1
   5 -1.4080000e+01 0.00e+00 2.57e-14  -8.6 3.18e-06    -  1.00e+00 1.00e+00f  1

Number of Iterations....: 5

                                   (scaled)                 (unscaled)
Objective...............:  -1.4080000135787204e+01   -1.4080000135787204e+01
Dual infeasibility......:   2.5678265217212132e-14    2.5678265217212132e-14
Constraint violation....:   0.0000000000000000e+00    0.0000000000000000e+00
Complementarity.........:   2.5064622693731291e-09    2.5064622693731291e-09
Overall NLP error.......:   2.5064622693731291e-09    2.5064622693731291e-09


Number of objective function evaluations             = 6
Number of objective gradient evaluations             = 6
Number of equality constraint evaluations            = 0
Number of inequality constraint evaluations          = 6
Number of equality constraint Jacobian evaluations   = 0
Number of inequality constraint Jacobian evaluations = 6
Number of Lagrangian Hessian evaluations             = 5
Total CPU secs in IPOPT (w/o function evaluations)   =      0.001
Total CPU secs in NLP function evaluations           =      0.000

EXIT: Optimal Solution Found.
Model Simple example LP

  Variables:
    x : Size=2, Index={1, 2}
        Key : Lower : Value              : Upper : Fixed : Stale : Domain
          1 :     0 : 1.3000000135344933 :  None : False : False : NonNegativeReals
          2 :     0 : 3.3000000292130904 :  None : False : False : NonNegativeReals

  Objectives:
    obj : Size=1, Index=None, Active=True
        Key  : Active : Value
        None :   True : 14.080000135787204

  Constraints:
    Constraint1 : Size=1
        Key  : Lower : Body                  : Upper
        None :  None : 1.567859708728747e-08 :   0.0
    Constraint2 : Size=1
        Key  : Lower : Body              : Upper
        None :  None : 17.00000016670213 :  17.0

We obtain the same result as previously, but notice that the interior point method reports some solution subject to certain tolerance, given by its convergence properties when it can get infinitesimally close (but not directly at) the boundary of the feasible region.

From Linear Programming to Integer Programming

The LP above allows fractional decisions, which is useful for production rates but not for yes/no or count decisions. When the variables must be integers, the feasible region becomes a set of discrete points rather than a filled polygon. Solvers such as GLPK and CBC handle this by solving LP relaxations inside a branch-and-bound search. This usually makes the model harder, but it also lets the formulation represent decisions such as building 0, 1, or 2 units.

Integer Programming

Now let’s consider that only integer units of each product can be produced, namely

max⁡x1,x25.5x1+2.1x2s.t.x2≤x1+28x1+2x2≤17x1,x2≥0x1,x2∈Z\max_{x_1, x_2} 5.5x_1 + 2.1x_2 \\ s.t. x_2 \leq x_1 + 2 \\ 8x_1 + 2x_2 \leq 17 \\ x_1, x_2 \geq 0 \\ x_1, x_2 \in \mathbb{Z}
Image produced in Jupyter
1 Var Declarations
    x : Size=2, Index={1, 2}
        Key : Lower : Value : Upper : Fixed : Stale : Domain
          1 :  None :  None :  None : False :  True : Integers
          2 :  None :  None :  None : False :  True : Integers

1 Objective Declarations
    obj : Size=1, Index=None, Active=True
        Key  : Active : Sense    : Expression
        None :   True : maximize : 5.5*x[1] + 2.1*x[2]

2 Constraint Declarations
    Constraint1 : Size=1, Index=None, Active=True
        Key  : Lower : Body              : Upper : Active
        None :  -Inf : x[2] - (x[1] + 2) :   0.0 :   True
    Constraint2 : Size=1, Index=None, Active=True
        Key  : Lower : Body            : Upper : Active
        None :  -Inf : 8*x[1] + 2*x[2] :  17.0 :   True

4 Declarations: x obj Constraint1 Constraint2
Model 'Simple example IP, 47-779 QuIP'

  Variables:
    x : Size=2, Index={1, 2}
        Key : Lower : Value : Upper : Fixed : Stale : Domain
          1 :  None :   1.0 :  None : False : False : Integers
          2 :  None :   3.0 :  None : False : False : Integers

  Objectives:
    obj : Size=1, Index=None, Active=True
        Key  : Active : Value
        None :   True :  11.8

  Constraints:
    Constraint1 : Size=1
        Key  : Lower : Body : Upper
        None :  None :  0.0 :   0.0
    Constraint2 : Size=1
        Key  : Lower : Body : Upper
        None :  None : 14.0 :  17.0
Image produced in Jupyter

Here the solution becomes x1=1,x2=3x_1 = 1, x_2 = 3 with an objective of 11.8.

Enumeration

Enumeration is practical for this small problem: the plot shows only 8 feasible integer points. If both nonnegative integer variables instead had upper bounds of 4, there would be 52=255^2 = 25 candidate pairs before checking feasibility. With more variables, enumeration quickly becomes impractical. For nn binary variables (integer variables can be encoded in binary), the number of possible assignments is 2n2^n.

In many other applications, the possible solutions come from permutations of the integer variables (e.g. assignment problems), which grow as n!n! with the size of the input.

This combinatorial growth makes exhaustive enumeration impractical very quickly.

Image produced in Jupyter

From Integer Linear Programming to Integer Nonlinear Programming

Integer variables can also appear inside nonlinear relationships. The next model keeps the same integer decision logic but adds a convex nonlinear constraint, so the feasible points are still discrete while the relaxation is curved. This changes both the geometry and the solver requirements: MILP solvers are no longer enough, and nonlinear branch-and-bound methods need problem-specific assumptions to give reliable answers.

Integer convex nonlinear programming

The following constraint “the production of B minus 1 squared can only be smaller than 2 minus the production of A” can be incorporated in the following convex integer nonlinear program

max⁡x1,x25.5x1+2.1x2s.t.x2≤x1+28x1+2x2≤17(x2−1)2≤2−x1x1,x2≥0x1,x2∈Z\max_{x_1, x_2} 5.5x_1 + 2.1x_2 \\ s.t. x_2 \leq x_1 + 2 \\ 8x_1 + 2x_2 \leq 17 \\ (x_2-1)^2 \leq 2-x_1\\ x_1, x_2 \geq 0 \\ x_1, x_2 \in \mathbb{Z}
Image produced in Jupyter
1 Var Declarations
    x : Size=2, Index={1, 2}
        Key : Lower : Value : Upper : Fixed : Stale : Domain
          1 :  None :  None :  None : False :  True : Integers
          2 :  None :  None :  None : False :  True : Integers

1 Objective Declarations
    obj : Size=1, Index=None, Active=True
        Key  : Active : Sense    : Expression
        None :   True : maximize : 5.5*x[1] + 2.1*x[2]

3 Constraint Declarations
    Constraint1 : Size=1, Index=None, Active=True
        Key  : Lower : Body              : Upper : Active
        None :  -Inf : x[2] - (x[1] + 2) :   0.0 :   True
    Constraint2 : Size=1, Index=None, Active=True
        Key  : Lower : Body            : Upper : Active
        None :  -Inf : 8*x[1] + 2*x[2] :  17.0 :   True
    Constraint3 : Size=1, Index=None, Active=True
        Key  : Lower : Body                       : Upper : Active
        None :  -Inf : (x[2] - 1)**2 - (2 - x[1]) :   0.0 :   True

5 Declarations: x obj Constraint1 Constraint2 Constraint3
Model 'Simple example convex INLP, 47-779 QuIP'

  Variables:
    x : Size=2, Index={1, 2}
        Key : Lower : Value : Upper : Fixed : Stale : Domain
          1 :  None :   1.0 :  None : False : False : Integers
          2 :  None :   2.0 :  None : False : False : Integers

  Objectives:
    obj : Size=1, Index=None, Active=True
        Key  : Active : Value
        None :   True :   9.7

  Constraints:
    Constraint1 : Size=1
        Key  : Lower : Body : Upper
        None :  None : -1.0 :   0.0
    Constraint2 : Size=1
        Key  : Lower : Body : Upper
        None :  None : 12.0 :  17.0
    Constraint3 : Size=1
        Key  : Lower : Body : Upper
        None :  None :  0.0 :   0.0
Image produced in Jupyter

In this case the optimal solution becomes x1=1,x2=2x_1 = 1, x_2 = 2 with an objective of 9.7.

Integer non-convex programming

The last constraint “the production of B minus 1 squared can only be greater than the production of A plus one half” can be incorporated in the following convex integer nonlinear program

max⁡x1,x25.5x1+2.1x2s.t.x2≤x1+28x1+2x2≤17(x2−1)2≤2−x1(x2−1)2≥1/2+x1x1,x2≥0x1,x2∈Z\max_{x_1, x_2} 5.5x_1 + 2.1x_2 \\ s.t. x_2 \leq x_1 + 2 \\ 8x_1 + 2x_2 \leq 17 \\ (x_2-1)^2 \leq 2-x_1\\ (x_2-1)^2 \geq 1/2+x_1\\ x_1, x_2 \geq 0 \\ x_1, x_2 \in \mathbb{Z}
Image produced in Jupyter
1 Var Declarations
    x : Size=2, Index={1, 2}
        Key : Lower : Value : Upper : Fixed : Stale : Domain
          1 :  None :  None :  None : False :  True : Integers
          2 :  None :  None :  None : False :  True : Integers

1 Objective Declarations
    obj : Size=1, Index=None, Active=True
        Key  : Active : Sense    : Expression
        None :   True : maximize : 5.5*x[1] + 2.1*x[2]

4 Constraint Declarations
    Constraint1 : Size=1, Index=None, Active=True
        Key  : Lower : Body              : Upper : Active
        None :  -Inf : x[2] - (x[1] + 2) :   0.0 :   True
    Constraint2 : Size=1, Index=None, Active=True
        Key  : Lower : Body            : Upper : Active
        None :  -Inf : 8*x[1] + 2*x[2] :  17.0 :   True
    Constraint3 : Size=1, Index=None, Active=True
        Key  : Lower : Body                       : Upper : Active
        None :  -Inf : (x[2] - 1)**2 - (2 - x[1]) :   0.0 :   True
    Constraint4 : Size=1, Index=None, Active=True
        Key  : Lower : Body                       : Upper : Active
        None :  -Inf : 0.5 + x[1] - (x[2] - 1)**2 :   0.0 :   True

6 Declarations: x obj Constraint1 Constraint2 Constraint3 Constraint4
Model 'Simple example non-convex INP, 47-779 QuIP'

  Variables:
    x : Size=2, Index={1, 2}
        Key : Lower : Value : Upper : Fixed : Stale : Domain
          1 :  None :   0.0 :  None : False : False : Integers
          2 :  None :   2.0 :  None : False : False : Integers

  Objectives:
    obj : Size=1, Index=None, Active=True
        Key  : Active : Value
        None :   True :   4.2

  Constraints:
    Constraint1 : Size=1
        Key  : Lower : Body : Upper
        None :  None :  0.0 :   0.0
    Constraint2 : Size=1
        Key  : Lower : Body : Upper
        None :  None :  4.0 :  17.0
    Constraint3 : Size=1
        Key  : Lower : Body : Upper
        None :  None : -1.0 :   0.0
    Constraint4 : Size=1
        Key  : Lower : Body : Upper
        None :  None : -0.5 :   0.0
Model 'Simple example non-convex INP, 47-779 QuIP'

  Variables:
    x : Size=2, Index={1, 2}
        Key : Lower : Value : Upper : Fixed : Stale : Domain
          1 :  None :   0.0 :  None : False : False : Integers
          2 :  None :   2.0 :  None : False : False : Integers

  Objectives:
    obj : Size=1, Index=None, Active=True
        Key  : Active : Value
        None :   True :   4.2

  Constraints:
    Constraint1 : Size=1
        Key  : Lower : Body : Upper
        None :  None :  0.0 :   0.0
    Constraint2 : Size=1
        Key  : Lower : Body : Upper
        None :  None :  4.0 :  17.0
    Constraint3 : Size=1
        Key  : Lower : Body : Upper
        None :  None : -1.0 :   0.0
    Constraint4 : Size=1
        Key  : Lower : Body : Upper
        None :  None : -0.5 :   0.0
Image produced in Jupyter

We can solve nonconvex MINLP problems, but their complexity creates substantial computational challenges.

Practice checkpoints

Use these checkpoints during the workshop to test the main ideas before moving on.

Notebook Cell
minimizer = (0.0, 0.0); maximizer = (4.0, 0.0)
Notebook Cell
continuous = (2.4, 1.6); best_integer = (3, 2)
Notebook Cell
remaining_feasible_points = [(0, 0), (4, 0)]

Summary

In this notebook we:

  • Built optimization models for a production-planning example across LP, ILP, convex INLP, and nonconvex INLP forms.

  • Used Pyomo to declare variables, objectives, and constraints, then called solvers suited to each model class.

  • Compared how relaxations, integer restrictions, nonlinear constraints, and nonconvexity affect solution quality and solver behavior.

Learning objectives met: You practiced formulating optimization models, implementing them in Pyomo, choosing solver classes, and interpreting solver output across increasingly difficult model families.

Next steps: Proceed to Notebook 2: QUBO and Ising Models to learn how constrained binary models can be rewritten as unconstrained quadratic objectives.

Further reading:

Acknowledgments

This notebook was developed by: