Optimization
optimize finds the parameters of a simulation you have already set up that give the best value of a quantity you choose, within bounds and constraints (EMode3D only). The parameters are named with the keys of sweep: a setting ('wavelength'), one profile’s setting ('setting, <profile>, <name>'), a shape property ('shape, <name>, <property>'), a section property ('section, <name>, <property>'), or an argument of a monitor_coupling beam ('beam, <name>').
Every evaluation starts from the simulation as declared when optimize is called. It sets each parameter, solves what the objectives need (a mode solve for a mode quantity, EME for an S-matrix or a monitor coupling) and records the result, so an evaluation depends only on its parameters, whatever ran before it. A cross-section that an earlier evaluation solved is not solved again: optimizing a section length solves the modes once, and optimizing a beam’s angle on a solved chain solves nothing. At the end, the simulation is left solved at the best point, so get, plot and EME read it.
The objectives and constraints are declared as data with the classes below, imported from emodeconnection:
from emodeconnection import Parameter, Objective, Constraint
res = em.optimize(
parameters=[Parameter('section, coupler, length', bounds=(5e3, 40e3), x0=15e3)],
objective=Objective('S_power', out='coupler.out_right', into='coupler.in_left'),
constraints=[Constraint('section, coupler, length', '<=', 30e3)],
)
print(res.parameters, res.objective, res.status)
- optimize
Find the parameters that give the best objective, within bounds and constraints.
- Parameters:
parameters – what to vary, a
Parameteror a list of themobjective – what to improve, an
Objectiveor a list of them, whose weighted sum is improvedconstraints – limits on a parameter or on an objective’s quantity, a
Constraintor a list of them, default: Nonemethod (str) – ‘cobyqa’ (bounds and constraints, no derivatives), ‘nelder-mead’ or ‘powell’ (bounds, constraints by a penalty), ‘differential_evolution’ (a global search over finite bounds, constraints too), ‘l-bfgs-b’ (bounds, with a gradient) or ‘slsqp’ (bounds and constraints, with a gradient), default: ‘cobyqa’
gradient (str or None) – how ‘l-bfgs-b’ and ‘slsqp’ get a gradient: ‘finite_difference’ evaluates the point and one step in each parameter, default: None (the pulsed adjoint for a poling design with pulse objectives, finite differences otherwise)
max_evaluations (int) – the most evaluations to make, default: 100
max_time (float or None) – [s] the most time to run, default: None (no limit)
tolerance (float) – the convergence tolerance, relative to each parameter’s range, default: 1e-4
seed (int) – the random seed of ‘differential_evolution’, so a run can be repeated, default: 0
apply_best (bool) – leave the simulation solved at the best point, default: True
record (str or None) – a file to write each evaluation to as one line of JSON as it completes, so an interrupted run keeps its history, default: None
snapshot (str or None) – also write the starting point to this
.ephfile, whichevaluatecan evaluate from in another session, default: Nonesimulation_name (str) – simulation name, default: ‘emode’
Returns an
OptimizationResult.A constraint on a parameter (
'<='or'>=') narrows that parameter’s bounds, so it is met exactly. A constraint on an objective’s quantity is met to the method’s tolerance. Each method spends evaluations differently: ‘cobyqa’ needs a few evaluations per parameter per step, finite differences one more evaluation than there are parameters per gradient, and ‘differential_evolution’ fifteen per parameter per generation. The result of a mode solve reproduces to about 1e-9 on the same grid, so settoleranceabove that for objectives read from a mode solve.
- class Parameter(key, bounds=None, x0=None, shape=(), step=None)
A quantity to vary.
- Parameters:
key (str) – the parameter, as a
sweepkey,'beam, <name>'(one of ‘angle’, ‘mode_field_diameter’, ‘center’, ‘index’ and ‘polarization’), or'poling, <section>, domain_lengths', the length [nm] of every domain of a section with aPulsedProcess('poling, <section>, domain_lengths[k]'for domain k alone)bounds – (min, max), either end None for no limit, default: None
x0 (float or None) – the starting value, default: None (the value the simulation holds now, or for a beam argument the value in the objective’s
beam)step (float or None) – the finite-difference step as a fraction of the bounded range (or of the value when unbounded), default: None (1e-6)
shape (tuple) – the shape of a list of values, such as
(n,), for'poling, <section>, domain_lengths', default: () (one value, or for the domain lengths the number of domains the section holds now)
A shape’s
'position'given one value moves the shape in x and keeps its height.boundsof a list of values is one(min, max)for every element, or a list of them, one per element.
Poling design. Every domain of a poled section can be a free variable. Parameter('poling, <section>, domain_lengths', bounds=(1500, 6000)) varies the length of each domain within a fabrication minimum and maximum, starting from the section’s declared poling as domains (Poling.to_domain_lengths). 'poling, <section>, total_length', the sum of the domain lengths, can be constrained (Constraint('poling, ppln, total_length', '<=', 5e6)), which costs no evaluation. Each evaluation propagates the pulse through the chain the last EME prepared, with new domain walls, and solves no mode. With ‘l-bfgs-b’ or ‘slsqp’ and 'band_energy' or 'band_average' objectives, the gradient with respect to every domain comes from one adjoint propagation, so one evaluation with its gradient costs one and a half to two and a half propagations, whatever the number of domains. With gradient='finite_difference' a gradient costs one propagation per domain, which suits tens of domains but not thousands.
- class Objective(kind, goal='maximize', target=None, weight=1.0, ...)
A quantity to improve.
kindand the arguments that select it:'S_power'and'S_phase': \(|S|^2\) and the phase of one S-matrix element.outandintoname the ports, by name (its mode 0), as'<port>_<mode>', or as a mode index of the whole matrix, andwavelengthpicks one wavelength of a multi-wavelength circuit.'monitor_coupling':monitor_couplingof the monitormonitorfor the beambeam, a dict of that function’s arguments.'effective_index','group_index'and'loss'(dB/m): of modemode(default 0) ofprofile(default the default profile).'band_energy'(pJ) and'band_average'(average power, W): of a pulse after pulsed propagation, between the wavelengthsband = (min, max)in nm, for tracked modemode(default all of them).'parameter': the value of the parameterkeyitself, to make a device as short or as narrow as its constraints allow.
- Parameters:
goal (str) – ‘maximize’, ‘minimize’, or ‘target’, which minimizes the squared distance to
target, default: ‘maximize’target (float or None) – the value to reach with
goal='target', default: Noneweight (float) – the objective’s weight in a list of them, default: 1.0
value(results)returns the objective’s quantity from an evaluation’s results.
- class Constraint(quantity, op, value)
A limit.
- Parameters:
quantity (str or Objective) – a parameter key, or an
Objectivewhose quantity is limitedop (str) – ‘<=’, ‘>=’ or ‘==’
value (float) – the limit
- class OptimizationResult
What
optimizereturns.parametersis the best point by key andxthe same as a list,objectiveandconstraintsits values, andgoalsays whether the objective was maximized or minimized. With several objectives that are not all maximized,objectiveis the minimized sum of each one’s value (minimized), negative value (maximized) or squared distance from its target, each times its weight.statusis ‘converged’, ‘budget’ (max_evaluationsran out), ‘time’ (max_timeran out) or ‘failed’, andmessagesays more.historylists every evaluation as anEvaluationwith itsindex,parameters,objective,constraints, theresultsthe objectives read,seconds,solves(the mode solves it made),cache_hits(the solves and junctions it reused) andcached(a point the run had already evaluated, which costs nothing).bestis the best evaluation,evaluationsthe number actually solved andsecondsthe run’s time.to_json()gives the whole result as plain JSON-ready data.
- evaluate
Evaluate the simulation at one point. It starts from the simulation’s snapshot (
snapshot, or the simulation as it was at the first evaluation), sets every parameter, solves what the results need and returns a record. The same parameters give the same results whatever ran before. Available in every session.- Parameters:
parameters (dict) – {key: value} for every parameter, with the keys of
optimizeresults (str or list) – the quantities to return, any
getkey, ‘S_matrix’ or ‘monitor_coupling’, default: ‘effective_index’snapshot (str or None) – a snapshot file to evaluate from instead of this simulation’s snapshot, default: None
monitor (str or None) – the monitor of a ‘monitor_coupling’ result, default: None
beam (dict or None) – the arguments of
monitor_couplingfor a ‘monitor_coupling’ result, default: Noneprofile (str or None) – the profile a mode result is read from, default: None (the default profile)
simulation_name (str) – simulation name, default: ‘emode’
Returns a dict with
parameters,results,seconds,solves,cache_hitsandseeded.
- snapshot
Take the snapshot
evaluatestarts from: the simulation as declared now (profiles, shapes, sections, ports and settings), without anything solved. Available in every session.- Parameters:
file_name (str or None) – also write the snapshot to this
.ephfile, which another session canevaluatefrom, default: Nonesimulation_name (str) – simulation name, default: ‘emode’
Returns the file’s path when
file_nameis given.