Pyomo Interface

Pyomo is a Python based, open source optimization modeling language with a diverse set of optimization capabilities. It is used by researchers to solve complex real-world applications, see Who uses Pyomo? for more introduction. The following documentation explains how to use the Cardinal Optimizer.

Installation Guide

To use the Cardinal Optimizer in Pyomo, you should setup Pyomo and Cardinal Optimizer correctly first. Pyomo currently supports Python 2.7, 3.6-3.9, you can install Python from Anaconda Distribution of Python or from Official Python. We recommend install Python from Anaconda since it is much more friendly and convenient for fresh users.

Using conda

The recommended way to install Pyomo in Anaconda Distribution of Python is to use conda which is built-in supported. Just execute the following commands in command prompt on Windows or shell on Linux and MacOS:

conda install -c conda-forge pyomo

Pyomo also has conditional dependencies on a variety of third-party Python packages, they can be installed using conda with commands:

conda install -c conda-forge pyomo.extras

Using pip

The alternative way to install Pyomo is to use the standard pip utitility, just execute the following commands in command prompt on Windows or shell on Linux and MacOS:

pip install pyomo

If you encounter any problems when installing Pyomo, please refer to How to install Pyomo for details. To install Cardinal Optimizer and setup its license properly, please refer to How to install Cardinal Optimizer for details.

Example Usage

We are going to make a simple introduction on how to use the Cardinal Optimizer in Pyomo by solving the example described in AMPL Interface - Example Usage. Users who want to learn more information about Pyomo may refer to Pyomo documentation for details.

Abstract Model

Pyomo provides two major approaches to construct any supported model types, here we show the Abstract Model approach to solve the above problem.

The source code pydiet_abstract.py is shown below, see Listing 9:

Listing 9 pydiet_abstract.py
 1# The code is adopted from:
 2#
 3# https://github.com/Pyomo/pyomo/blob/master/examples/pyomo/amplbook2/diet.py
 4#
 5# with some modification by developer of the Cardinal Optimizer
 6
 7from pyomo.core import *
 8
 9model = AbstractModel()
10
11model.NUTR = Set()
12model.FOOD = Set()
13
14model.cost  = Param(model.FOOD, within=NonNegativeReals)
15model.f_min = Param(model.FOOD, within=NonNegativeReals)
16
17model.f_max = Param(model.FOOD)
18model.n_min = Param(model.NUTR, within=NonNegativeReals)
19model.n_max = Param(model.NUTR)
20model.amt   = Param(model.NUTR, model.FOOD, within=NonNegativeReals)
21
22def Buy_bounds(model, i):
23  return (model.f_min[i], model.f_max[i])
24model.Buy = Var(model.FOOD, bounds=Buy_bounds)
25
26def Objective_rule(model):
27  return sum_product(model.cost, model.Buy)
28model.totalcost = Objective(rule=Objective_rule, sense=minimize)
29
30def Diet_rule(model, i):
31  expr = 0
32
33  for j in model.FOOD:
34    expr = expr + model.amt[i, j] * model.Buy[j]
35
36  return (model.n_min[i], expr, model.n_max[i])
37model.Diet = Constraint(model.NUTR, rule=Diet_rule)

And the data file pydiet_abstract.dat in Listing 10:

Listing 10 pydiet_abstract.dat
 1# The data is adopted from:
 2# 
 3# https://github.com/Pyomo/pyomo/blob/master/examples/pyomo/amplbook2/diet.dat
 4#
 5# with some modification by developer of the Cardinal Optimizer
 6
 7data;
 8
 9set NUTR := A B1 B2 C ;
10set FOOD := BEEF CHK FISH HAM MCH MTL SPG TUR ;
11
12param:   cost  f_min  f_max :=
13  BEEF   3.19    0     100
14  CHK    2.59    0     100
15  FISH   2.29    0     100
16  HAM    2.89    0     100
17  MCH    1.89    0     100
18  MTL    1.99    0     100
19  SPG    1.99    0     100
20  TUR    2.49    0     100 ;
21
22param:   n_min  n_max :=
23   A      700   10000
24   C      700   10000
25   B1     700   10000
26   B2     700   10000 ;
27
28param amt (tr):
29           A    C   B1   B2 :=
30   BEEF   60   20   10   15
31   CHK     8    0   20   20
32   FISH    8   10   15   10
33   HAM    40   40   35   10
34   MCH    15   35   15   15
35   MTL    70   30   15   15
36   SPG    25   50   25   15
37   TUR    60   20   15   10 ;

To solve the problem using Pyomo and the Cardinal Optimizer, just type commands below in command prompt on Windows or Bash shell on Linux and MacOS.

pyomo solve --solver=coptampl pydiet_abstract.py pydiet_abstract.dat

When solving the problem, Pyomo write log information to the screen:

[    0.00] Setting up Pyomo environment
[    0.00] Applying Pyomo preprocessing actions
[    0.00] Creating model
[    0.01] Applying solver
[    0.05] Processing results
    Number of solutions: 1
    Solution Information
      Gap: None
      Status: optimal
      Function Value: 88.19999999999999
    Solver results file: results.yml
[    0.05] Applying Pyomo postprocessing actions
[    0.05] Pyomo Finished

Upon completion, you can check the solution summary in results.yml:

# ==========================================================
# = Solver Results                                         =
# ==========================================================
# ----------------------------------------------------------
#   Problem Information
# ----------------------------------------------------------
Problem: 
- Lower bound: -inf
  Upper bound: inf
  Number of objectives: 1
  Number of constraints: 4
  Number of variables: 8
  Sense: unknown
# ----------------------------------------------------------
#   Solver Information
# ----------------------------------------------------------
Solver: 
- Status: ok
  Message: COPT-AMPL\x3a optimal solution; objective 88.2, iterations 1
  Termination condition: optimal
  Id: 0
  Error rc: 0
  Time: 0.03171110153198242
# ----------------------------------------------------------
#   Solution Information
# ----------------------------------------------------------
Solution: 
- number of solutions: 1
  number of solutions displayed: 1
- Gap: None
  Status: optimal
  Message: COPT-AMPL\x3a optimal solution; objective 88.2, iterations 1
  Objective:
    totalcost:
      Value: 88.19999999999999
  Variable:
    Buy[MCH]:
      Value: 46.666666666666664
  Constraint: No values

So the minimal total cost is about 88.2 units when buying 46.67 units of MCH.

Concrete Model

The other approach to construct model in Pyomo is to use Concrete Model, we will show how to model and solve the above problem in this way.

Concrete models can be solved using the "Direct" and "Persistent" interface methods. This method relies on the Pyomo plugin file "copt_pyomo.py" of COPT, which is located in the "lib/pyomo" subfolder of the installation package.

To use this plugin, you need to copy the "copt_pyomo.py" file to the same directory of your program, and have correctly installed the corresponding version of the Python interface of COPT(coptpy).

The source code pydiet_concrete.py is shown in Listing 11:

Listing 11 pydiet_concrete.py
  1# The code is adopted from:
  2#
  3# https://github.com/Pyomo/pyomo/blob/master/examples/pyomo/amplbook2/diet.py
  4#
  5# with some modification by developer of the Cardinal Optimizer
  6
  7from __future__ import print_function, division
  8
  9import pyomo.environ as pyo
 10import pyomo.opt as pyopt
 11
 12from copt_pyomo import *
 13
 14# Nutrition set
 15NUTR = ["A", "C", "B1", "B2"]
 16# Food set
 17FOOD = ["BEEF", "CHK", "FISH", "HAM", "MCH", "MTL", "SPG", "TUR"]
 18
 19# Price of foods
 20cost = {"BEEF": 3.19, "CHK": 2.59, "FISH": 2.29, "HAM": 2.89, "MCH": 1.89,
 21        "MTL":  1.99, "SPG": 1.99, "TUR":  2.49}
 22# Nutrition of foods
 23amt = {"BEEF": {"A": 60, "C": 20, "B1": 10, "B2": 15},
 24       "CHK":  {"A": 8,  "C": 0,  "B1": 20, "B2": 20},
 25       "FISH": {"A": 8,  "C": 10, "B1": 15, "B2": 10},
 26       "HAM":  {"A": 40, "C": 40, "B1": 35, "B2": 10},
 27       "MCH":  {"A": 15, "C": 35, "B1": 15, "B2": 15},
 28       "MTL":  {"A": 70, "C": 30, "B1": 15, "B2": 15},
 29       "SPG":  {"A": 25, "C": 50, "B1": 25, "B2": 15},
 30       "TUR":  {"A": 60, "C": 20, "B1": 15, "B2": 10}}
 31
 32# The "diet problem" using ConcreteModel
 33model = pyo.ConcreteModel()
 34
 35model.NUTR = pyo.Set(initialize=NUTR)
 36model.FOOD = pyo.Set(initialize=FOOD)
 37
 38model.cost = pyo.Param(model.FOOD, initialize=cost)
 39
 40def amt_rule(model, i, j):
 41  return amt[i][j]
 42model.amt  = pyo.Param(model.FOOD, model.NUTR, initialize=amt_rule)
 43
 44model.f_min = pyo.Param(model.FOOD, default=0)
 45model.f_max = pyo.Param(model.FOOD, default=100)
 46
 47model.n_min = pyo.Param(model.NUTR, default=700)
 48model.n_max = pyo.Param(model.NUTR, default=10000)
 49
 50def Buy_bounds(model, i):
 51  return (model.f_min[i], model.f_max[i])
 52model.buy = pyo.Var(model.FOOD, bounds=Buy_bounds)
 53
 54def Objective_rule(model):
 55  return pyo.sum_product(model.cost, model.buy)
 56model.totalcost = pyo.Objective(rule=Objective_rule, sense=pyo.minimize)
 57
 58def Diet_rule(model, j):
 59  expr = 0
 60
 61  for i in model.FOOD:
 62    expr = expr + model.amt[i, j] * model.buy[i]
 63
 64  return (model.n_min[j], expr, model.n_max[j])
 65model.Diet = pyo.Constraint(model.NUTR, rule=Diet_rule)
 66
 67# Reduced costs of variables
 68model.rc = pyo.Suffix(direction=pyo.Suffix.IMPORT)
 69
 70# Activities and duals of constraints
 71model.slack = pyo.Suffix(direction=pyo.Suffix.IMPORT)
 72model.dual = pyo.Suffix(direction=pyo.Suffix.IMPORT)
 73
 74# Use 'copt_direct' solver to solve the problem
 75solver = pyopt.SolverFactory('copt_direct')
 76
 77# Use 'copt_persistent' solver to solve the problem
 78# solver = pyopt.SolverFactory('copt_persistent')
 79# solver.set_instance(model)
 80
 81results = solver.solve(model, tee=True)
 82
 83# Check result
 84print("")
 85if results.solver.status == pyopt.SolverStatus.ok and \
 86   results.solver.termination_condition == pyopt.TerminationCondition.optimal:
 87  print("Optimal solution found")
 88else:
 89  print("Something unexpected happened: ", str(results.solver))
 90
 91print("")
 92print("Optimal objective value:")
 93print("  totalcost: {0:6f}".format(pyo.value(model.totalcost)))
 94
 95print("")
 96print("Variables solution:")
 97for i in FOOD:
 98  print("  buy[{0:4s}] = {1:9.6f} (rc: {2:9.6f})".format(i, \
 99                                                  pyo.value(model.buy[i]), \
100                                                  model.rc[model.buy[i]]))
101
102print("")
103print("Constraint solution:")
104for i in NUTR:
105  print("  diet[{0:2s}] = {1:12.6f} (dual: {2:9.6f})".format(i, \
106                                                      model.slack[model.Diet[i]], \
107                                                      model.dual[model.Diet[i]]))

To solve the problem using Pyomo and the Cardinal Optimizer, just execute commands below:

python pydiet_concrete.py

Up completion, you should see solution summary on screen as below:

Optimal solution found
Objective:
  totalcost: 88.200000
Variables:
  buy[BEEF] = 0.000000
  buy[CHK ] = 0.000000
  buy[FISH] = 0.000000
  buy[HAM ] = 0.000000
  buy[MCH ] = 46.666667
  buy[MTL ] = 0.000000
  buy[SPG ] = 0.000000
  buy[TUR ] = 0.000000

So the Cardinal Optimizer found the optimal solution, which is about 88.2 units when buying about 46.67 units of MCH.