Source code for openmdao.lib.drivers.cobyladriver

"""
cobyladriver.py - Contains a driver that wraps the cobyla
optimizer as used in pyOpt:

Minimize a function using the Constrained Optimization BY Linear
Approximation (COBYLA) method

COBYLA is gradient-free and can handle inequality constraints.
"""

import logging
from math import isnan

try:
    from numpy import zeros, array
except ImportError as err:
    logging.warn("In %s: %r" % (__file__, err))
    # to keep class decl from barfing before being stubbed out
    zeros = lambda *args, **kwargs: None 
    
from cobyla.cobyla import cobyla, closeunit

from openmdao.main.datatypes.api import Enum, Float, Int, Str
from openmdao.main.driver import Driver
from openmdao.main.hasparameters import HasParameters
from openmdao.main.hasconstraints import HasIneqConstraints
from openmdao.main.hasobjective import HasObjective
from openmdao.main.interfaces import IHasParameters, IHasIneqConstraints, \
                                     IHasObjective, implements, IOptimizer
from openmdao.util.decorators import add_delegate, stub_if_missing_deps

    
@stub_if_missing_deps('numpy')
@add_delegate(HasParameters, HasIneqConstraints, HasObjective)
[docs]class COBYLAdriver(Driver): """Minimize a function using the Constrained Optimization BY Linear Approximation (COBYLA) method. COBYLA is gradient-free and can handle inequality constraints. Note: constraints should be added using the OpenMDAO convention (positive = violated) """ implements(IHasParameters, IHasIneqConstraints, IHasObjective, IOptimizer) # pylint: disable-msg=E1101 rhobeg = Float(1.0, iotype='in', desc = 'Reasonable initial changes to the variables.') rhoend = Float(1e-4, iotype='in', desc = 'Final accuracy in the optimization (not precisely guaranteed).') iprint = Enum(1, [0, 1, 2, 3], iotype='in', desc = 'Controls the frequency of output: 0 (no output),1,2,3') maxfun = Int(1000, iotype='in', desc = 'Maximum number of function evaluations.') iout = Int(6, iotype='in', desc = 'Fortran output unit. Leave this at 6 for STDOUT') output_filename = Str('cobyla.out', iotype='in', desc = 'Name of output file (if iout not 6).') error_code = Int(0, iotype='out', desc = 'Error code returned from COBYLA.') def __init__(self, *args, **kwargs): super(COBYLAdriver, self).__init__(*args, **kwargs) self.error_messages = { 1 : 'Max. number of function evaluations reached', 2 : 'Rounding errors are becoming damaging' } self.x = zeros(0,'d') self.work_vector = zeros(0,'d') self.gg = zeros(0,'d') self.iact = zeros(0,'d') self.ff = 0 self.nfvals = 0
[docs] def start_iteration(self): """Perform initial setup before iteration loop begins.""" self.nparam = len(self.get_parameters().values()) self.ncon = len(self.get_ineq_constraints()) self.ncon += 2*self.nparam self.x = zeros(self.nparam,'d') self.g = zeros(self.ncon,'d') self.work_vector = zeros(self.ncon,'d') # get the initial values of the parameters params = self.get_parameters().values() for i, val in enumerate(params): self.x[i] = val.evaluate(self.parent) n = self.nparam m = self.ncon self.work_vector = zeros([n*(3*n+2*m+11)+4*m+6],'d') self.iact = zeros([m+1],'i') self.gg = zeros([m],'d') self._continue = True
[docs] def run_iteration(self): """ Note: cobyla controls the looping.""" try: self.iact, self.error_code, self.nfvals = \ cobyla(self._func, self.nparam, self.ncon, self.x, \ self.rhobeg, self.rhoend, self.iprint, self.maxfun, \ self.work_vector, self.iact, self.error_code, self.nfvals, \ self.iout, self.output_filename, self.ff, self.gg) except Exception, err: self._logger.error(str(err)) raise if self.iprint > 0 : closeunit(self.iout) # Log any errors if self.error_code != 0 : self._logger.warning(self.error_messages[self.error_code]) # Iteration is complete self._continue = False
def _func(self, n, m, xnew, f, g): """ Return ndarrays containing the function and constraint evaluations. Note: n, m, f, and g are unused inputs.""" self.set_parameters(xnew) super(COBYLAdriver, self).run_iteration() f = self.eval_objective() if isnan(f): msg = "Numerical overflow in the objective" self.raise_exception(msg, RuntimeError) # Constraints (COBYLA defines positive as satisfied) con_list = [] for v in self.get_ineq_constraints().values(): val = v.evaluate(self.parent) if '>' in val[2]: con_list.append(val[0]-val[1]) else: con_list.append(val[1]-val[0]) # Side Constraints for param in self.get_parameters().values(): val = param.evaluate(self.parent) con_list.append(val - param.low) con_list.append(param.high - val) g = array(con_list) # Write out some relevant information to the recorder self.record_case() return f, g
OpenMDAO Home