#!/usr/bin/env python
##############################################################################
#
# diffpy.srfit by DANSE Diffraction group
# Simon J. L. Billinge
# (c) 2008 The Trustees of Columbia University
# in the City of New York. All rights reserved.
#
# File coded by: Chris Farrow
#
# See AUTHORS.txt for a list of people who contributed.
# See LICENSE_DANSE.txt for license information.
#
##############################################################################
"""Restraints class.
Restraints are used by RecipeOrganizers to organize restraint equations.
Restraints store an Equation, bounds on its value, and the form of the
penalty function for breaking a restraint. This penalty is added to the
residual equation calculated by a FitRecipe.
"""
__all__ = ["Restraint"]
from numpy import inf
from diffpy.srfit.exceptions import SrFitError
from diffpy.srfit.fitbase.validatable import Validatable
[docs]
class Restraint(Validatable):
"""Restrain an equation to specified bounds.
The penalty for breaking the restraint is calculated as
``(max(0, lower_bound - val, val - upper_bound) / sig) ** 2``, where
``val`` is the value of the calculated equation. This is multiplied
by the average chi^2 if ``scaled`` is True.
Attributes
----------
eq : Equation
The equation whose evaluation is compared against the restraint
bounds.
lower_bound : float
The lower bound on the restraint evaluation (default -inf).
upper_bound : float
The upper bound on the restraint evaluation (default inf).
sig : float
The uncertainty on the bounds (default 1).
scaled : bool
A flag indicating if the restraint is scaled (multiplied) by
the unrestrained point-average chi^2 (chi^2/numpoints)
(default False).
"""
def __init__(
self, eq, lower_bound=-inf, upper_bound=inf, sig=1, scaled=False
):
"""Restrain an equation to specified bounds.
Parameters
----------
eq : Equation
The equation whose evaluation is compared against the
restraint bounds.
lower_bound : float, optional
The lower bound on the restraint evaluation (default -inf).
upper_bound : float, optional
The upper bound on the restraint evaluation (default inf).
sig : float, optional
The uncertainty on the bounds (default 1).
scaled : bool, optional
The flag indicating if the restraint is scaled (multiplied)
by the unrestrained point-average chi^2 (chi^2/numpoints)
(default False).
"""
self.eq = eq
self.lower_bound = float(lower_bound)
self.upper_bound = float(upper_bound)
self.sig = float(sig)
self.scaled = bool(scaled)
return
[docs]
def penalty(self, w=1.0):
"""Calculate the penalty of the restraint.
Parameters
----------
w : float, optional
The point-average chi^2 which is optionally used to scale
the penalty (default 1.0).
Returns
-------
float
The penalty for breaking the restraint.
"""
val = self.eq()
penalty = (
max(0, self.lower_bound - val, val - self.upper_bound) / self.sig
) ** 2
if self.scaled:
penalty *= w
return penalty
def _validate(self):
"""Validate my state.
This validates ``eq``.
Raises
------
SrFitError
If validation fails.
"""
if self.eq is None:
raise SrFitError("eq is None")
from diffpy.srfit.equation.visitors import validate
try:
validate(self.eq)
except ValueError as e:
raise SrFitError(e)
# Try to get the value of eq.
try:
val = self.eq()
except TypeError:
raise SrFitError("eq cannot be evaluated")
finally:
if val is None:
raise SrFitError("eq evaluates to None")
return
# End class Restraint
# End of file