Source code for pycif.plugins.minimizers.m1qn3.check
import numpy as np
from logging import info
[docs]
def check_options(self, chi, finit, **kwargs):
"""Validate M1QN3 parameters and initialise the working zone.
Reads convergence parameters from the plugin instance (falling back to
``maxiter`` when ``niter``/``nsim`` are absent), validates their ranges,
detects warm vs cold start from the ``mode`` flag, and writes the
resolved values back to ``self`` so that ``m1qn3`` can read them.
Args:
self (Plugin): M1QN3 minimizer plugin instance. The following
attributes are read (with defaults where noted):
* ``niter`` / ``nsim`` — explicit iteration/simulation limits;
if absent, both are derived from ``maxiter``.
* ``nupdates`` (int, default 5) — number of L-BFGS memory pairs.
* ``dxmin`` (float, default 1e-20) — absolute precision on *x*.
* ``epsg`` (float, default 1e-20) — relative precision on the gradient.
* ``mode`` (int, default 0) — scaling and restart mode.
* ``df1`` (float, default 0.01) — expected fractional decrease of *f*.
* ``iz`` (np.ndarray[int], default zeros(6)) — warm-start working zone.
chi (np.ndarray): initial iterate :math:`\chi_0`, used only to read
its dimension.
finit (float): initial cost value; used to scale ``df1``.
**kwargs: unused; accepted for interface consistency.
Returns:
Plugin: ``self`` with all resolved parameters written back as
attributes.
Raises:
ValueError: if any parameter is outside its valid range, or if a
warm restart is requested with an inconsistent ``iz`` vector.
"""
# Dimension of the control vector
dim = chi.size
# Number of simulations
# Can be explicitly given by the user, or will be determined from 'maxiter'
try:
niter = self.niter
nsim = self.nsim
except AttributeError:
maxiter = self.maxiter
niter = maxiter
nsim = 2 * maxiter
# Default parameters
m = getattr(self, "nupdates", 5)
dxmin = getattr(self, "dxmin", 1.0e-20)
epsg = getattr(self, "epsg", 1.0e-20)
mode = getattr(self, "mode", 0)
df1 = getattr(self, "df1", 0.01) * finit
iz = getattr(self, "iz", np.zeros(6, dtype=int))
# Init parameters
towrite = """
M1QN3 (Version 2.0b, December 1993):
entry point program translated in Python language
dimension of the problem (n): {}
number of updates: {}
absolute precision on x (dxmin) : {}
expected decrease for f (df1): {}
relative precision on g (epsg): {}
maximal number of iterations (niter): {}
maximal number of simulations (nsim): {}
""".format(
dim, m, dxmin, df1, epsg, niter, nsim
)
info(towrite)
# Checking for inconsistent definition of parameters
if (
dim <= 0.0
or niter <= 0.0
or nsim <= 0.0
or dxmin <= 0.0
or epsg <= 0.0
or epsg > 1.0
or mode < 0.0
or mode > 3
or m < 1
or m > 10
):
raise ValueError(
"Warning: inconsistent call in M1QN3:\n"
f"- dim = {dim} should be > 0\n"
f"- niter = {niter} should be > 0\n"
f"- nsim = {nsim} should be >0\n"
f"- dxmin = {dxmin} should be >0\n"
f"- epsg = {epsg} should be in ]0, 1[\n"
f"- mode = {mode} should be in ]0, 3[\n"
f"- m = {m} should be in ]1, 10[\n"
)
# Select mode
if mode - int(mode / 2.0) * 2.0 == 0:
info("M1QN3: Diagonal Initial Scaling mode")
sscale = False
else:
info("M1QN3: Scalar Initial Scaling mode")
sscale = True
# Cold start or warm restart?
# Check iz: iz(1)=n, iz(2)=(0 if DIS, 1 if SIS),
# iz(3)=m, iz(4)=jmin, iz(5)=jmax
if mode % 2 == 0:
info("M1QN3: cold start")
else:
if (
iz[1] != dim
or iz[2] != sscale
or iz[3] != m
or iz[4] < 1
or iz[5] < 1
or iz[4] > iz[3]
or iz[5] > iz[3]
):
info("Warning: M1QN3: inconsistent iz for a warm " "restart")
raise ValueError
info("M1QN3: warm restart")
iz[1] = dim
iz[2] = 0
if sscale:
iz[2] = 1
iz[3] = m
# Update options
self.mode = mode
self.iz = iz
self.m = iz[3]
self.jmin = iz[4]
self.jmax = iz[5]
self.niter = niter
self.nsim = nsim
self.dxmin = dxmin
self.df1 = df1
self.epsg = epsg
return self