OptMethod

class sherpa.optmethods.OptMethod(name: str, optfunc: Callable[[Concatenate[Callable[[Sequence[float] | ndarray], tuple[float, ndarray]], Sequence[float] | ndarray, Sequence[float] | ndarray, Sequence[float] | ndarray, P]], tuple[bool, ndarray, float, str, dict[str, Any]]], **kwargs: Any)[source] [edit on github]

Bases: NoNewAttributesAfterInit

Base class for the optimisers.

A new optimizer can be created by directly instantiating this class or by subclassing it which provides the opportunity to set default values for name and optimization function.

Parameters:
  • name (str) – The name of the optimiser; this is just a string used to identify the optimiser in output messages.

  • optfunc (function) – The function which optimises the model: its arguments are a function which evaluates the statistic given a list of parameter values, the starting parameters, minima, and maxima, followed by keyword arguments matching the configuration data.

Notes

The optfunc is the main input to the optimizer. It must be a callable with the following signature - see the example below for a full implementation:

>>> def optfunc(fcn: StatFunc,
...             x0: np.ndarray,
...             xmin: np.ndarray,
...             xmax: np.ndarray,
...             **kwargs) -> OptReturn:
...     '''Do something clever here'''
...     success = True # or False
...     best_pars = x0 # best parameter values found
...     best_stat = fcn(x0)[0] # best statistic value found
...     msg = 'Did something clever' # message for user
...     imsg = {'additional info': 'if any'} # dict with additional info
...     return (success, best_pars, best_stat, msg, imsg)

fcn is a callable that has only a single argument (a sequence of parameter values) and returns a tuple of the statistic value and per-bin statistic values for the given values. (Most optimizers will use the total value of the statistics, but some my profit from the per-bin information.) fcn is prepared by Sherpa and does not need any additional information from the optimizer, in particular it internally already knows about that data and model to be used.

x0 is a sequence of starting values for the parameters. It only contains the free parameters that the optimizer will vary. xmin and xmax are the minimum and maximum values for the parameters. In sherpa, all parameters are bounded in some way either because there is a physical limit (e.g. some X-ray models are only available for a certain range of plasma temperatures) or because there is a mathematical limit (e.g. the width of a Gaussian cannot be negative). For numerical reasons, Sherpa does not allow limits of 0 or inf, but uses sherpa.models.parameter.tinyval and sherpa.models.parameter.hugeval in its build-in models. For example, the width of a Gaussian is bounded +tinyval to +hugeval.

The x0, xmin, and xmax sequences will always have the same length and they are typically of type np.array, but lists or tuples may also appear depending on how exactly the optimizer is called; the optimizing function shall accept any of these types.

Usually, an optfunc will call the function fcn multiple times with different values of the parameters until it find a minimum. It must then return a tuple of the following form:

OptReturn = tuple[bool,        # did the optimiser succeed
              np.ndarray,      # final parameter values
              float,           # final statistic value
              str,             # message
              dict[str, Any]]  # information to pass back

Example

For this example, we first define an optimizer function. To demonstrate the interface, we use a function that is stupidly simple and should not be used for any real fitting problem: We will simply draw random numbers for the parameters a few times and return the set of parameters that gives the lowest value of the statistic.

>>> import numpy as np
>>> from sherpa.utils.types import StatFunc, OptReturn
>>> rng = np.random.default_rng(42)  # fixed seed for reproducibility
>>> def stupid_optfunc(fcn: StatFunc,
...                    x0: np.ndarray,
...                    xmin: np.ndarray,
...                    xmax: np.ndarray,
...                    n_iter: int = 20) -> OptReturn:
...     '''Do NOT use this in real life!'''
...     best_pars = x0
...     best_stat = fcn(x0)[0]
...     for i in range(n_iter):
...         try_x = rng.uniform(xmin, xmax)
...         try_stat = fcn(try_x)[0]
...         if try_stat < best_stat:
...             best_stat = try_stat
...             best_pars = try_x
...     return (True, best_pars, best_stat,
...             'Is this really a success?',
...             {'additional info': 'not really'})

Now, we can create an instance of the OptMethod class:

>>> from sherpa.optmethods import OptMethod
>>> opt = OptMethod(name='myopt', optfunc=stupid_optfunc)

We can change the default of the parameters on the instance:

>>> opt.n_iter = 15

And finally let’s some make some very simply data and try to fit it:

>>> from sherpa.data import Data1D
>>> from sherpa.models import Const1D
>>> from sherpa.stats import Chi2
>>> from sherpa.fit import Fit
>>> data = Data1D('data1', x=np.arange(5),
...               y=[3.4, 3.6, 3.3, 3.5, 3.4],
...               staterror=[0.1, 0.2, 0.14, 0.09, 0.1])
>>> model = Const1D('model')
>>> model.c0.min = 0
>>> model.c0.max = 10
>>> fitter = Fit(data, model, stat=Chi2(), method=opt)
>>> result = fitter.fit()
>>> print(result)
datasets       = None
itermethodname = none
methodname     = optmethod
statname       = chi2
succeeded      = True
parnames       = ('model.c0',)
parvals        = (3.7079802423258124,)
statval        = 33.094317831919184
istatval       = 2362.5028974552783
dstatval       = 2329.408579623359
numpoints      = 5
dof            = 4
qval           = 1.142528391357815e-06
rstat          = 8.273579457979796
message        = Is this really a success?
nfev           = None

Attributes Summary

default_config

The default settings for the optimiser.

Methods Summary

fit(statfunc, pars, parmins, parmaxes[, ...])

Run the optimiser.

Attributes Documentation

default_config

The default settings for the optimiser.

Methods Documentation

fit(statfunc: Callable[[Sequence[float] | ndarray], tuple[float, ndarray]], pars: Sequence[float] | ndarray, parmins: Sequence[float] | ndarray, parmaxes: Sequence[float] | ndarray, statargs: Any | None = None, statkwargs: Any | None = None) tuple[bool, ndarray, float, str, dict[str, Any]][source] [edit on github]

Run the optimiser.

Changed in version 4.18.0: The statargs and statkwargs arguments are now ignored.

Changed in version 4.16.0: The statkwargs argument now defaults to None rather than {}.

Parameters:
  • statfunc (function) – Given a list of parameter values as the first argument and, as the remaining positional arguments, statargs and statkwargs as keyword arguments, return the statistic value.

  • pars (sequence) – The start position of the model parameter values.

  • parmins (sequence) – The minimum allowed values for each model parameter. This must match the length of pars.

  • parmaxes (sequence) – The maximum allowed values for each model parameter. This must match the length of pars.

  • statargs (optional) – This is currently unused.

  • statkwargs (dict, optional) – This is currently unused.

Returns:

newpars – The tuple contains: boolean indicating whether the optimization succeeded or not, the best fit parameters as a NumPy array, the statistic value at the best-fit location, a string message indicating the status, and a dictionary containing information about the optimisation (this depends on the optimiser).

Return type:

tuple