emopt.optimizer

This Optimizer class provides a simple wrapper around scipy.minimize.optimize which allows you optimize an electromagnetic structure given an arbitrary (user-defined) set of design parameters. The Optimizer class minimizes a figure of merit defined in an emopt.adjoint_method.AdjointMethod object and takes advantage of the gradient computed by the supplied emopt.adjoint_method.AdjointMethod object.

Examples

The Optimizer is used approximately as follows:

# Setup the simulation object
sim = ...

# Define a custom adjoint method class and instantiate it
am = MyAdjointMethod(...)

# Define a callback function
def my_callback(params, ...):
    ...

callback_func = lambda params : my_callback(params, other_inputs)

# Specify initial guess for the design parameters
design_params = ....

# Create the optimizer object
opt = Optimizer(sim, am, design_params, callback=callback_func)

# run the optimization
opt.run()
class emopt.optimizer.Optimizer(am, p0, callback_func=None, opt_method='BFGS', Nmax=1000, tol=1e-05, bounds=None, scipy_verbose=True)

Bases: future.types.newobject.newobject

Handles the optimization of an electromagnetic structure.

Given a set of design variables, a figure of merit, and a gradient, any electromagnetic structure can be optimized regardless of the underlying implementations of the simulator. With these quantities defined, it is in theory quite easy to run an optimization.

Currently, the optimization code is based on scipy.optimize.minimize, which is not parallel/MPI-compatible. As is such, this class manages the interface between the sequential scipy calls and the parallel components of EMOpt (like running simulations).

Fully parallelizing the optimization code should be possible using petsc. However, parallelizing the gradient computation is quite tricky for the most general case of arbitrary design variables. The process of computing gradients in paralle is significantly simplified when the material in each grid cell is an independent design variable (i.e. grayscale topology optimization). This type of problem, however, is not the core goal of EMOpt. This functionality may be added in the future.

Parameters:
  • sim (emopt.fdfd.FDFD) – Simulation object
  • am (emopt.adjoint_method.AdjointMethod) – Object containing problem-specific implementation of AdjointMethod
  • p0 (numpy.ndarray or list) – Initial guess for design parameters of system
  • callback_func (function) – Function which accepts the current design variables as the only argument. This function is called after each iteration of the optimization. By default, no callback function is used.
  • opt_method (str) – Optimization method to use. The recommended options are: CG, BFGS, L-BFGS-B, TNC, SLSQP. (default=’BFGS’)
  • Nmax (int) – Maximum number of interations of optimization method before process is terminated. (default=1000)
  • tol (float) – Minimum change in figure of merit below which the optimization will complete. (default=1e-5)
  • bounds (list of tuples) – List of tuples containing two floats which specify the lower and upper bounds on each design variable. This is not compatible with all optimization methods. Consult the scipy.optimize.minimize documentation for details. (default=None)
am

The adjoint method object for calculating FOM and gradient

Type:emopt.adjoint_method.AdjointMethod
p0

The initial guess for design variables

Type:numpy.ndarray or list
callback

The callback function to call after each optimization iteration.

Type:function
Nmax

The maximum number of iterations

Type:int
tol

The minimum change in figure of merit under which optimization terminates

Type:float
bounds

The list of bounds to put on design variables in the formate (minv, maxv)

Type:list of 2-tuple
run(self)

Run the optimization.

run_sequence(self, sim, am)

Define the sequence of figure of merit and gradient calls for the optimization.

class RunCommands

Bases: future.types.newobject.newobject

Run command codes used during message passing.

We need a way to signal the non-master nodes to perform different operations during the optimization. We do this by sending integers from the master node to the other nodes containing a command code. The commands are specified using an enum-like class.

FOM

Tells worker nodes to compute the figure of merit

Type:int
GRAD

Tells the worker nodes to compute the gradient of the figure of merit

Type:int
EXIT

Tells the worker nodes to finish.

Type:int
EXIT = 2
FOM = 0
GRAD = 1
run()

Run the optimization.

Returns:
  • float – The final figure of merit
  • numpy.array – The optimized design parameters
run_sequence(am)

Sequential optimization code.

In general, the optimization itself is run in parallel. Instead, only the calculation of the figure of merit and gradient takes advantage of paralellism (which is where the bulk of the computational complexity comes in). This function defines the sequential optimization code and makes calls to the parallel components.

Notes

Override this method for custom functionality!

Parameters:am (emopt.adjoint_method.AdjointMethod) – The adjoint method object responsible for FOM and gradient calculations.
Returns:The optimized figure of merit and the corresponding set of optimal design parameters.
Return type:(float, numpy.ndarray)