Two previous posts considered using SciPy and CVXPY to solve a simple optimisation problem.
Pyomo is a flexible Open Source optimisation modelling language for Python. It can be used to define, solve, and analyse a wide range of optimisation problems, including Linear Programming (LP) and Mixed-Integer Programming (MIP), nonlinear programming (NLP), and differential equations.
Install Pyomo
Use pip
to install the pyomo
package. I suggest doing this in a virtual environment.
pip install pyomo
You can also go all in and install it with all conditional dependencies, which might save some fiddling later on.
pip install 'pyomo[optional]'
Check that you are able to import pyomo
into a Python session. For repeatability you should record all installed package versions at this point using pip freeze
.
Before you can do anything meaningful with the pyomo
package you’ll also need to install one or more solvers.
Install a Solver
Pyomo supports a selection of Open Source and commercial optimisers. I’ll be focusing on the former.
GLPK
GLPK (GNU Linear Programming Kit) is a powerful Open Source package for solving LP and MIP optimisation problems. GLPK comprises a library (a collection of C functions that can be integrated into other projects) and glpsol
, a command-line executable that can be used to solve optimisation problems specified in a few different formats.
It’s easy to install GLPK via the system package manager.
sudo apt update
sudo apt install -y glpk-utils libglpk-dev glpk-doc
🚧 Unfortunately the GLPK solver will not work for the water tank problem though because it contains non-linear terms. We need an optimiser that’s able to handle non-linearity.
The Ipopt solver is a good alternative with a wider range of capabilities. It’s slightly more challenging to install. Ipopt is a project developed by COIN-OR (Computational Infrastructure for Operations Research). To use it with Pyomo you’ll need to first install a few other dependencies.
ASL
ASL (AMPL Solver Library) is a library developed to facilitate communication between solvers and the AMPL modelling language. It acts as a bridge, allowing solvers to interpret and solve optimisation problems defined in the AMPL format.
Install ASL using the third-party builder maintained by COIN-OR.
git clone git@github.com:coin-or-tools/ThirdParty-ASL.git
cd ThirdParty-ASL
Run the get.ASL
script to download the ASL source code.
bash get.ASL
Some tools will be required, specifically a C compiler.
sudo apt install -y build-essential
Then configure, build and install.
./configure
make
sudo make install
Confirm that libcoinasl.so
is installed under /usr/local/lib/
.
HSL
HSL (Harwell Subroutine Library) is a collection of packages for large-scale scientific computation. It was developed at the Rutherford Appleton Laboratory. HSL is available under either academic or commercial licenses. A subset of HSL, the HSL Archive, is also available free for personal use.
Go to the Coin-HSL Archive and request a license. When your request is approved (should be immediate) you’ll receive a link to a download page. Follow the link and download the archive, which will be named something like coinhsl-archive-2023.11.17.tar.gz
(possibly the date will differ).
Install HSL using the third-party builder maintained by COIN-OR.
git clone git@github.com:coin-or-tools/ThirdParty-HSL.git
cd ThirdParty-HSL
Unpack the HSL archive into that directory and create a symbolic link.
tar -zxvf coinhsl-archive-2023.11.17.tar.gz
ln -s coinhsl-archive-2023.11.17/ coinhsl
You’ll need a few libraries.
sudo apt install -y libblas-dev liblapack-dev libmetis-dev
Then configure, build and install.
./configure
make
sudo make install
Check to see that libcoinhsl.so
is installed under /usr/local/lib/
.
Ipopt
Finally we’re ready to install Ipopt.
git clone https://github.com/coin-or/Ipopt
cd Ipopt
But first a few more dependencies.
sudo apt install -y g++ pkgconf libmumps-seq-dev
Make sure that it can find the ASL and HSL libraries installed earlier. You might want to add this to your .bashrc
.
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
Then configure, build and test.
./configure
make
make test
Assuming that all of the tests pass, you can now install.
sudo make install
Check that you can run the ipopt
executable.
ipopt -v
Ipopt 3.14.17 (x86_64-pc-linux-gnu), ASL(20241111)
CPLEX
See installing CPLEX post.
Optimisation with Pyomo
I set up the reference problem for Pyomo in a Jupter notebook. Here are the key components:
- Import a selection of packages.
- Specify the problem via a set of constants.
- Create a Pyomo concrete model object, along with objects for each of the problem variables. Each problem variable is assigned a type and bounds.
- Set up constraints on the model object.
- Define the objective function and link to the model object.
- Instantiate a solver (specifying the
ipopt
solver). - Run the solver on the model object.
📢 In contrast to the objective functions used in the CVXPY and SciPy, here I used the L2 rather than the L1 norm. The impact on the results is apparent in smoother changes in the inflow rate.
Ipopt
I unpacked the optimised problem variables into a data frame and generated the plots below. For reference, these results were obtained with Ipopt
version 3.14.17 using the MA27
solver from the HSL library.
MOSEK
For comparison with the Ipopt results above, here are the corresponding results from the MOSEK solver. These can also be compared with the MOSEK results obtained via the CVXPY package.
As mentioned above, the decrease in inflow rate after the first phase is smoother due to the objective function using the L2 rather than L1 norm.
Summary
Optimiser | Objective | Time (s) | Timestamp |
---|---|---|---|
Compared with the results from the CVXPY package the optimised inflow rate is similar: high initial flow rate, then decreasing to a lower flow rate after the first demand cycle. The inflow here is, however, a smooth function of time. This is objectively a better solution since it corresponds to a lower value of the objective function. It’s also more aesthetically pleasing. Whereas with CVXPY the smoothing term in the objective function seems to have had no effect on the input flow, here it has achieved its goal. The optimized outflow rate closely matches the demand and the tank level is a periodic sawtooth as before.
Performance
The optimisation itself took around 0.055 seconds. For comparison purposes I extended the problem to 10 days and dropped the sampling interval to 10 minutes, for which the optimisation took 0.272 seconds. All else being equal, comparing these times with those from CVXPY suggests that Pyomo is significantly quicker.