Modeling Nonlinear Problems

This section illustrates differences between SimpleModel and regular Pyomo models on a simple nonlinear problem. PuLP is omitted from this comparison because it cannot represent nonlinear problems.

Soda Can Problem

Finding the optimal dimensions of a soda can is a simple nonlinear optimization problem. We consider an idealized soda can that is represented as a cylinder with radius r and height h. The problem is to find the radius and height that minimizes the surface area of the cylinder while keeping a fixed volume. Here, the surface area of the cylinder approximates the amount of aluminum needed for a soda can, so this problem can be used to predict the miminum amount of aluminum needed to hold a given volume.

The surface area of a cylinder is

\[2 \pi r (r+h)\]

A standard soda can is 12 oz or 355 ml. Thus, we have the constraint

\[\pi r^2 h = 355\]

Thus, we have the following optimization representation for this problem:

\begin{equation*} \begin{array}{ll} \min & 2 \pi r (r+h)\\ \textrm{s.t. } & \pi r^2 h = 355\\ & r \geq 0\\ & h \geq 0 \end{array} \end{equation*}

This is a nonlinear problem, so it cannot be formulated with PuLP. The following sections illustrate how this optimization problem can be formulated and solved with SimpleModel and Pyomo.

SimpleModel Formulation

The following script executes the following steps to create and solve the soda can problem:

  1. Import pyomo_simplemodel
  2. Construct a SimpleModel class
  3. Declare variables, the objective and the constraint
  4. Perform optimization
  5. Summarize the optimal solution
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# sodacan.py

from pyomo_simplemodel import *
from math import pi

m = SimpleModel()

r = m.var('r', bounds=(0,None))
h = m.var('h', bounds=(0,None))

m += 2*pi*r*(r + h)
m += pi*h*r**2 == 355

status = m.solve("ipopt")

print("Status = %s" % status.solver.termination_condition)

print("%s = %f" % (r, value(r)))
print("%s = %f" % (h, value(h)))
print("Objective = %f" % value(m.objective()))

In this example, the model object m is used to manage the problem definition. Decision variables are declared with the var() method, the objective and constraint are added with the += operator, and the solve() method is used to perform optimization. After optimization, the solution is stored in the variable objects, and the objective value can be accessed with using the objective() method.

Pyomo Formulation

The following script executes the same steps as above to create and solve the soda can problem using Pyomo:

# sodacan-pyomo.py

from pyomo.environ import *
from math import pi

m = ConcreteModel()

m.r = Var(bounds=(0,None))
m.h = Var(bounds=(0,None))

m.o = Objective(expr=2*pi*m.r*(m.r + m.h))
m.c = Constraint(expr=pi*m.h*m.r**2 == 355)

solver = SolverFactory('ipopt')
status = solver.solve(m)

print("Status = %s" % status.solver.termination_condition)

print("%s = %f" % (m.r, value(m.r)))
print("%s = %f" % (m.h, value(m.h)))
print("Objective = %f" % value(m.o))

This script is similar to the SimpleModel script, but Pyomo models are created with an object-oriented design. Thus, elements of the optimization problem are declared with variable, objective and constraint components, which are Pyomo objects. As a consequence, the objective and constraint expressions reference variable components within the model (e.g. m.x) instead of variable objects directly (e.g. x).