at.latticetools.optimizers#

Optimizers for observable matching.

The Jacobian solver is adapted from the JacobianSolver of Xsuite’s xdeps optimizer (xsuite/xdeps, Apache License 2.0).

_JacobianSolver is generic. _MeritFunction adapts it to AT’s VariableList / ObservableList, using ResponseMatrix to build the Jacobian and solve each Newton step.

Functions

jacobian_match(variables, constraints, *[, ...])

Observable matching using a damped Gauss-Newton (Jacobian) solver.

least_squares_match(variables, constraints, *)

Observable matching using least_squares().

jacobian_match(variables, constraints, *, verbose=0, ftol=1e-08, xtol=1e-08, gtol=1e-08, tol=None, max_nfev=None, n_bisections=3, n_steps_max=20, rcond=1e-06, sing_val_cutoff=None, broyden=False, max_step=None, max_rel_penalty_increase=10.0, err=1000000.0, use_mp=False, pool_size=None, start_method=None, one_sided=True, **eval_kw)[source]#

Observable matching using a damped Gauss-Newton (Jacobian) solver.

An alternative to the scipy-backed methods of match(), better suited to problems where Jacobian evaluation dominates the cost, or where explicit control over the singular-value cutoff or Jacobian reuse (broyden=True) is wanted.

Parameters:
  • variables (VariableList) – Variable parameters. The delta attribute of each variable is the step used to compute the Jacobian by finite differences.

  • constraints (ObservableList) – Constraints to fulfill.

  • verbose (int) – 0 for silent, 1 for a summary, >=2 for per-step detail.

  • ftol (float | None) – Tolerance on the relative decrease of the cost 0.5 * sum(weighted_deviations**2), as in scipy.optimize.least_squares(). None disables it.

  • xtol (float | None) – Tolerance on the relative change of the variables, as in scipy.optimize.least_squares(). None disables it.

  • gtol (float | None) – Tolerance on the infinity norm of the cost gradient, as in scipy.optimize.least_squares(). None disables it.

  • tol (float | None) – Stop when the penalty (norm of the weighted deviations) drops below this value. None disables it.

  • max_nfev (int | None) – Maximum number of function evaluations, checked after each Newton step. None means no limit.

  • n_bisections (int) – Maximum number of step-halvings in the line search.

  • n_steps_max (int) – Maximum number of outer (Newton) steps.

  • rcond (float) – Relative singular-value cutoff for the Newton step: singular values smaller than rcond times the largest one are discarded. Used only when sing_val_cutoff is not given.

  • sing_val_cutoff (int | None) – Number of (largest) singular values to keep for the Newton step; takes precedence over rcond.

  • broyden (bool) – If True, rank-1 (Broyden) update the Jacobian across outer steps instead of recomputing it by finite differences.

  • max_step (float | ndarray | None) – Maximum change of each variable in a single step. The whole step is scaled down if needed, keeping its direction.

  • max_rel_penalty_increase (float | None) – Once n_bisections step-halvings have been tried, keep halving the step while the trial penalty is at least this factor times the current one (for instance when the evaluation fails). None disables these extra halvings.

  • err (float) – Value substituted for a constraint’s deviation when it cannot be evaluated.

  • use_mp (bool) – Compute the Jacobian with multiprocessing, as in ResponseMatrix.build().

  • pool_size (int | None) – Compute the Jacobian with multiprocessing, as in ResponseMatrix.build().

  • start_method (str | None) – Compute the Jacobian with multiprocessing, as in ResponseMatrix.build().

  • one_sided (bool) – Compute the Jacobian using positive delta only (default) instead of +/-delta.

Keyword Arguments:

**eval_kw – Evaluation keywords passed to VariableList.set()/get() and ObservableList.evaluate() (e.g. ring, dp, dct, df…). ring is needed if the variables or the constraints require a lattice.

Returns:

result – scipy.optimize.OptimizeResult with x, success, status, message, nfev and fun (the raw, unweighted deviations at x). status follows scipy.optimize.least_squares() (0: maximum number of steps/evaluations, 1: gtol, 2: ftol, 3: xtol, 4: both ftol and xtol), with in addition 5: tol and 6: no decrease found in the line search. success is status > 0.

Note

  • use_mp only parallelises the finite-difference Jacobian; the line search and the SVD step remain serial. Each match also pays a fixed cost to start the worker pool. use_mp pays off only when building a Jacobian is expensive; on small problems, it can be slower than serial.

  • With broyden=True the Jacobian is computed by finite differences only at the first step, then updated from each accepted step. This saves most of the Jacobian evaluations, however on strongly nonlinear problems the solver may take poorer steps or stop early.

  • With broyden=True only one Jacobian is computed, so use_mp=True rarely helps.

  • The variable bounds are respected at every step: a step that would cross a bound is scaled down to stop on it, and a variable on a bound is held fixed as long as the gradient pushes it outwards.

  • The delta must be small enough that observables are not strongly affected or the lattice becomes unstable: the default delta (1.0) is usually far too large. A step that is too large gives an inaccurate Jacobian and a poor or slow convergence; if it makes the Jacobian computation fail, the error message lists the variables whose step exceeds 10% of their value.

least_squares_match(variables, constraints, *, verbose=0, lstsq_method=None, jac='2-point', ftol=1e-08, xtol=1e-08, gtol=1e-08, x_scale=None, loss='linear', f_scale=1.0, diff_step=None, tr_solver=None, tr_options=None, jac_sparsity=None, max_nfev=None, err=1000000.0, **eval_kw)[source]#

Observable matching using least_squares().

Parameters:
Keyword Arguments:

**eval_kw – Evaluation keywords provided to VariableList.set() and ObservableList.evaluate() (e.g. ring, dp, dct, orbit, twiss_in…).

Returns:

result – scipy.optimize.OptimizeResult returned by least_squares().