modOptDriver#
modOptDriver wraps the optimizers available through the modOpt optimization framework. modOpt provides access to a variety of gradient-based and gradient-free optimization algorithms, including SLSQP, IPOPT, SNOPT, COBYLA, and others. Some of these are available in the package, some need to be separately installed, and some are commercial products that must be obtained from their respective authors (e.g. SNOPT). The modOptDriver supports sparse specification of constraint Jacobians, but not all optimizers can utilize the sparsity.
In this example, we use the SLSQP optimizer to minimize the objective of the Paraboloid problem.
from openmdao.utils.notebook_utils import get_code
from myst_nb import glue
glue("code_src020", get_code("openmdao.test_suite.components.paraboloid.Paraboloid"), display=False)
Paraboloid class definition
class Paraboloid(om.ExplicitComponent):
"""
Evaluates the equation f(x,y) = (x-3)^2 + xy + (y+4)^2 - 3.
"""
def setup(self):
self.add_input('x', val=0.0)
self.add_input('y', val=0.0)
self.add_output('f_xy', val=0.0)
def setup_partials(self):
self.declare_partials('*', '*')
def compute(self, inputs, outputs):
"""
f(x,y) = (x-3)^2 + xy + (y+4)^2 - 3
Optimal solution (minimum): x = 6.6667; y = -7.3333
"""
x = inputs['x']
y = inputs['y']
outputs['f_xy'] = (x-3.0)**2 + x*y + (y+4.0)**2 - 3.0
def compute_partials(self, inputs, partials):
"""
Jacobian for our paraboloid.
"""
x = inputs['x']
y = inputs['y']
partials['f_xy', 'x'] = 2.0*x - 6.0 + y
partials['f_xy', 'y'] = 2.0*y + 8.0 + x
import openmdao.api as om
from openmdao.test_suite.components.paraboloid import Paraboloid
prob = om.Problem()
model = prob.model
model.add_subsystem('comp', Paraboloid(), promotes=['*'])
prob.driver = om.modOptDriver()
prob.driver.options['optimizer'] = 'SLSQP'
prob.driver.options['maxiter'] = 200
prob.driver.options['disp'] = True
model.add_design_var('x', lower=-50.0, upper=50.0)
model.add_design_var('y', lower=-50.0, upper=50.0)
model.add_objective('f_xy')
prob.setup()
prob.set_val('x', 50.0)
prob.set_val('y', 50.0)
prob.run_driver()
Setting objective name as "f_xy".
Optimization terminated successfully (Exit mode 0)
Current function value: -27.33333333333333
Iterations: 3
Function evaluations: 4
Gradient evaluations: 3
Optimization Complete
-----------------------------------
Problem: problem
Driver: modOptDriver
success : True
iterations : 6
runtime : 3.6305E-02 s
model_evals : 6
model_time : 4.9838E-04 s
deriv_evals : 0
deriv_time : 0.0000E+00 s
exit_status : SUCCESS
The “optimizer” option lets you choose which optimizer to use. modOptDriver provides access to a wide range of optimizers through the modOpt framework. The supported optimizers are (case sensitive):
Gradient-Based:
“SLSQP” - Sequential Least Squares Programming (default optimizer)
“PySLSQP” - Pure Python implementation of SLSQP with expanded features
“BFGS” - Broyden-Fletcher-Goldfarb-Shanno (unconstrained only)
“LBFGSB” - Limited-memory BFGS with bounds
“TrustConstr” - Trust-region constrained algorithm
“IPOPT” - Interior Point Optimizer
“SNOPT” - Sparse Nonlinear Optimizer (requires license)
“OpenSQP” - Sequential Quadratic Programming built into modOpt
Gradient-Free:
“COBYLA” - Constrained Optimization BY Linear Approximation
“COBYQA” - Constrained Optimization BY Quadratic Approximation (improved COBYLA)
“NelderMead” - Nelder-Mead simplex algorithm (unconstrained only)
Note
OpenMDAO installs only the base modOpt package. Some optimizers listed above require additional packages that must be installed manually in your environment. Most of the optimizers (SLSQP, BFGS, LBFGSB, TrustConstr, COBYLA, COBYQA, NelderMead) are available through SciPy and are included with the base modOpt installation.
print(prob.get_val('x'))
[6.66666667]
print(prob.get_val('y'))
[-7.33333333]
modOptDriver Options#
om.show_options_table("openmdao.drivers.modopt_driver.modOptDriver")
| Option | Default | Acceptable Values | Acceptable Types | Description |
|---|---|---|---|---|
| debug_print | [] | ['desvars', 'nl_cons', 'ln_cons', 'objs', 'totals'] | ['list'] | List of what type of Driver variables to print at each iteration. |
| disp | True | N/A | ['int', 'bool'] | Controls optimizer output verbosity. Can be bool (True/False) or int for fine control (0=quiet, higher=more verbose). Automatically maps to optimizer-specific settings. If optimizer specific verbosity settings are provided in the "opt_settings" attribute, then this option will be ignored and those settings will be used instead. |
| invalid_desvar_behavior | warn | ['warn', 'raise', 'ignore'] | N/A | Behavior of driver if the initial value of a design variable exceeds its bounds. The default value may beset using the `OPENMDAO_INVALID_DESVAR_BEHAVIOR` environment variable to one of the valid options. |
| maxiter | 200 | N/A | N/A | Maximum number of iterations. |
| optimizer | SLSQP | ['SLSQP', 'IPOPT', 'BFGS', 'COBYQA', 'TrustConstr', 'SNOPT', 'LBFGSB', 'NelderMead', 'OpenSQP', 'PySLSQP', 'CVXOPT', 'COBYLA', 'ConvexQPSolvers'] | N/A | Name of optimizer to use |
| output_dir | DEFAULT_REPORTS_DIR | N/A | ['str', '_ReprClass'] | The directory to store all the output files generated from the optimization. |
| singular_jac_behavior | warn | ['error', 'warn', 'ignore'] | N/A | Defines behavior of a zero row/col check after first call to compute_totals: error - raise an error. warn - raise a warning. ignore - don't perform check. |
| singular_jac_tol | 1e-16 | N/A | N/A | Tolerance for zero row/column check. |
| turn_off_outputs | False | [True, False] | ['bool'] | If True, prevents modOpt from generating any output files. |
modOptDriver Constructor#
The call signature for the modOptDriver constructor is:
- modOptDriver.__init__(**kwargs)[source]
Initialize the modOptDriver.
- Parameters:
- **kwargsdict of keyword arguments
Keyword arguments that will be mapped into the Driver options.
Using modOptDriver#
modOptDriver has a small number of unified options that can be specified as keyword arguments when it is instantiated or by using the “options” dictionary. We have already shown how to set the optimizer option. Next we will see how maxiter and disp can be used to control the maxinum number of iterations and what the optimizer displays. The “disp” option controls optimizer output verbosity. You can set it to True/False or an integer for fine control (0=quiet, higher=more verbose). This setting is automatically mapped to the optimizer’s output settings.
import openmdao.api as om
from openmdao.test_suite.components.paraboloid import Paraboloid
prob = om.Problem()
model = prob.model
model.add_subsystem('comp', Paraboloid(), promotes=['*'])
prob.driver = om.modOptDriver()
prob.driver.options['maxiter'] = 20
prob.driver.options['disp'] = False
model.add_design_var('x', lower=-50.0, upper=50.0)
model.add_design_var('y', lower=-50.0, upper=50.0)
model.add_objective('f_xy')
prob.setup()
prob.set_val('x', 50.0)
prob.set_val('y', 50.0)
prob.run_driver()
Setting objective name as "f_xy".
Problem: problem2
Driver: modOptDriver
success : True
iterations : 6
runtime : 6.8236E-03 s
model_evals : 6
model_time : 4.5012E-04 s
deriv_evals : 0
deriv_time : 0.0000E+00 s
exit_status : SUCCESS
print(prob.get_val('x'))
[6.66666667]
print(prob.get_val('y'))
[-7.33333333]
modOptDriver Optimizer-Specific Options#
Each optimizer in modOpt has its own set of specific options that control its behavior. You can set these optimizer-specific options through the opt_settings dictionary. See the modOpt documentation for detailed information about available settings for each optimizer.
For example, here’s code setting some specific options for the SLSQP optimizer:
import openmdao.api as om
from openmdao.test_suite.components.paraboloid import Paraboloid
prob = om.Problem()
model = prob.model
model.add_subsystem('comp', Paraboloid(), promotes=['*'])
prob.driver = driver = om.modOptDriver()
driver.options['optimizer'] = 'SLSQP'
driver.options['maxiter'] = 100
driver.options['disp'] = False
# Set optimizer-specific options through opt_settings. Here we set the precision for
# the final solution with the "ftol" setting specific to SLSQP
driver.opt_settings['ftol'] = 1e-8
model.add_design_var('x', lower=-50.0, upper=50.0)
model.add_design_var('y', lower=-50.0, upper=50.0)
model.add_objective('f_xy')
prob.setup()
prob.set_val('x', 50.0)
prob.set_val('y', 50.0)
prob.run_driver()
Setting objective name as "f_xy".
Problem: problem3
Driver: modOptDriver
success : True
iterations : 6
runtime : 5.8466E-03 s
model_evals : 6
model_time : 4.6127E-04 s
deriv_evals : 0
deriv_time : 0.0000E+00 s
exit_status : SUCCESS