[docs]class Optimizer(object):
"""
This is a top-level optimization controller. It is intended to be the
primary means by which a user interacts with Kona.
Attributes
----------
_memory : KonaMemory
All-knowing Kona memory controller.
_algorithm : OptimizationAlgorithm
Optimization algorithm object.
Parameters
----------
solver : UserSolver
algorithm : OptimizationAlgorithm
optns : dict, optional
"""
def __init__(self, solver, algorithm, optns=None):
# initialize optimization memory
self._memory = KonaMemory(solver)
# set default file handles
self._optns = {
'info_file' : 'kona_info.dat',
'hist_file' : 'kona_hist.dat',
'krylov' : {
'out_file' : 'kona_krylov.dat',
},
'verify' : {
'out_file' : 'kona_verify.dat',
}
}
# process the final options
if optns is None:
optns = {}
if not isinstance(optns, dict):
raise TypeError('Kona.Optimizer >> Options must be a dictionary!')
self._process_options(optns)
# get vector factories
primal_factory = self._memory.primal_factory
state_factory = self._memory.state_factory
if self._memory.neq > 0:
eq_factory = self._memory.eq_factory
else:
eq_factory = None
if self._memory.nineq > 0:
ineq_factory = self._memory.ineq_factory
else:
ineq_factory = None
# initialize the optimization algorithm
self._algorithm = algorithm(
primal_factory, state_factory, eq_factory, ineq_factory,
self._optns)
def _process_options(self, optns):
# this is a recursive dictionary merge function
def update(d, u):
for k, v in u.iteritems():
if isinstance(v, collections.Mapping):
r = update(d.get(k, {}), v)
d[k] = r
else:
d[k] = u[k]
return d
# merge user dictionary with default file names
self._optns = update(self._optns, optns)
# open the files on the master (zero) rank
self._optns['info_file'] = \
self._memory.open_file(self._optns['info_file'])
self._optns['hist_file'] = \
self._memory.open_file(self._optns['hist_file'])
self._optns['krylov']['out_file'] = \
self._memory.open_file(self._optns['krylov']['out_file'])
self._optns['verify']['out_file'] = \
self._memory.open_file(self._optns['verify']['out_file'])
[docs] def set_design_bounds(self, lower=None, upper=None):
"""
Define lower and upper design bounds.
Parameters
----------
lower : int
Lower bound for design variables.
upper : int
Upper bound for design variables.
"""
# make sure user provided some value
if lower is not None and upper is None:
raise ValueError("Must specify at least one bound!")
# assign the bounds
if lower is not None:
assert isinstance(lower, (np.float, np.int))
self._memory.design_lb = lower
if upper is not None:
assert isinstance(upper, (np.float, np.int))
self._memory.design_ub = upper
[docs] def solve(self, print_opts=False):
# print options
if print_opts:
self._optns['info_file'].write('\n')
self._optns['info_file'].write('Kona Options\n')
self._optns['info_file'].write('===========================================\n')
print_dict(self._optns, out_file=self._optns['info_file'])
self._optns['info_file'].write('\n')
# allocate memory and run the optimization
self._memory.allocate_memory()
self._algorithm.solve()
# package imports at the bottom to prevent circular import errors
import collections
import numpy as np
from kona.options import print_dict
from kona.user import UserSolver
from kona.linalg.memory import KonaMemory