Source code for pycif.plugins.minimizers.scipy.minimize
import numpy as np
import scipy
from logging import info
[docs]
def minimize(self, finit, gradinit, chi0, **kwargs):
"""Run :func:`scipy.optimize.minimize` and return the optimal iterate.
Wraps the simulator into a ``(f, g)`` callable compatible with scipy's
``jac=True`` interface and hooks a gradient-norm callback for the
``epsg`` convergence criterion.
Args:
self (Plugin): scipy minimizer plugin instance.
finit (float): initial cost function value (unused; kept for
interface consistency with other minimizers).
gradinit (np.ndarray): initial gradient, shape ``(n,)``.
chi0 (np.ndarray): initial iterate, shape ``(n,)``.
**kwargs: forwarded to the simulator.
Returns:
np.ndarray: optimal iterate :math:`\chi_\mathrm{opt}`, shape ``(n,)``.
"""
# Initialize iteration number and initial norm of the gradient
self.iter_id = 0
self.grad_norm_0 = np.sqrt(np.dot(gradinit, gradinit))
# Wrap the simulator into a function compatible with scipy
def fun(x):
f, g = self.simulator.simul(x, run_id=self.iter_id, **kwargs)
self.iter_id += 1
self.current_function = f
self.current_gradient = g[:]
return f, g
# Define Call Back to check convergence with regards to the norm of the gradient
def callback(xk):
if hasattr(self, "epsg"):
gradopt = self.current_gradient
if np.sqrt(np.dot(gradopt, gradopt)) < self.epsg * self.grad_norm_0:
self.xopt = xk
raise StopIteration
return False
# Running scipy minimizer
try:
results = scipy.optimize.minimize(
fun, chi0,
jac=True,
method=self.method,
bounds=self.bounds,
options=self.options,
callback=callback
)
# Final verbose and output
info(results)
# Outputs
xopt = results.x
gradopt = results.jac
fopt = results.fun
except StopIteration:
info("The convergence criterion on epsg has been reached")
xopt = self.xopt
gradopt = self.current_gradient
fopt = self.current_function
r1 = np.sqrt(np.dot(xopt, xopt))
r2 = np.sqrt(np.dot(gradopt, gradopt))
info("norm of x = " + str(r1))
info("f = " + str(fopt))
info("norm of g = " + str(r2))
return xopt