Exemple #1
0
    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
Exemple #2
0
    def _validate(self):
        """Validate my state.

        This validates that value and par are not None.

        Raises SrFitError if validation fails.

        """
        if self.value is None:
            raise SrFitError("value is None")
        if self.par is None:
            raise SrFitError("par is None")
        return
Exemple #3
0
    def _validate(self):
        """Validate my state.

        This validates that the phase is not None.
        This performs ProfileGenerator validations.

        Raises SrFitError if validation fails.

        """
        if self._calc is None:
            raise SrFitError("_calc is None")
        if self._phase is None:
            raise SrFitError("_phase is None")
        ProfileGenerator._validate(self)
        return
Exemple #4
0
    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, e:
            raise SrFitError(e)
    def _validate(self):
        """This evaluates the calculator.

        Raises SrFitError if validation fails.

        """
        from numpy import nan
        p = self.penalty()
        if p is None or p is nan:
            raise SrFitError("Cannot evaluate penalty")
        v = self._calc.value
        if len(v) > 1 and not v.any():
            emsg = ("Bond valence sums are all zero.  Check atom symbols in "
                    "the structure or define custom bond-valence parameters.")
            raise SrFitError(emsg)
        return
Exemple #6
0
    def savetxt(self, fname, **kwargs):
        """Call `numpy.savetxt` with x, ycalc, y, dy.

        Parameters
        ----------
        fname : filename or file handle
            This is passed to `numpy.savetxt`.
        **kwargs
            The keyword arguments that are passed to `numpy.savetxt`.
            We preset file header "x  ycalc  y  dy".  Use ``header=''``
            to save data without any header.

        Raises
        ------
        SrFitError
            When `self.ycalc` has not been set.

        See also
        --------
        numpy.savetxt
        """
        x = self.x
        ycalc = self.ycalc
        if ycalc is None:
            raise SrFitError("ycalc is None")
        y = self.y
        dy = self.dy
        kwargs.setdefault('header', 'x  ycalc  y  dy')
        data = numpy.transpose([x, ycalc, y, dy])
        numpy.savetxt(fname, data, **kwargs)
        return
Exemple #7
0
    def _validate(self):
        """Validate my state.

        This validates that x, y, dy, xobx, yobs and dyobs are not None.
        This validates that x, y, and dy are the same length.

        Raises SrFitError if validation fails.

        """
        datanotset = any(v is None for v in
                [self.x, self.y, self.dy, self.xobs, self.yobs, self.dyobs])
        if datanotset:
            raise SrFitError("Missing data")
        if len(self.x) != len(self.y) or len(self.x) != len(self.dy):
            raise SrFitError("Data are different lengths")
        return
Exemple #8
0
    def _validate(self):
        """Validate my state.

        This validates that value is not None.

        Raises SrFitError if validation fails.

        """
        if self.value is None:
            raise SrFitError("value of '%s' is None" % self.name)
        return
Exemple #9
0
    def setResidualEquation(self, eqstr):
        """Set the residual equation for the FitContribution.

        eqstr   --  A string representation of the residual. If eqstr is None
                    (default), then the previous residual equation will be
                    used, or the chi2 residual will be used if that does not
                    exist.

        Two residuals are preset for convenience, "chiv" and "resv".
        chiv is defined such that dot(chiv, chiv) = chi^2.
        resv is defined such that dot(resv, resv) = Rw^2.
        You can call on these in your residual equation. Note that the quantity
        that will be optimized is the summed square of the residual equation.
        Keep that in mind when defining a new residual or using the built-in
        ones.

        Raises SrFitError if the Profile is not yet defined.
        Raises ValueError if eqstr depends on a Parameter that is not part of
        the FitContribution.

        """
        if self.profile is None:
            raise SrFitError("Assign the Profile first")
        if self._eq is None:
            raise SrFitError("Assign the Equation first")

        chivstr = "(eq - %s)/%s" % (self._yname, self._dyname)
        resvstr = "(eq - %s)/sum(%s**2)**0.5" % (self._yname, self._yname)

        # Get the equation string if it is not defined
        if eqstr == "chiv":
            eqstr = chivstr
        elif eqstr == "resv":
            eqstr = resvstr

        reseq = equationFromString(eqstr, self._eqfactory)
        self._eqfactory.wipeout(self._reseq)
        self._reseq = reseq

        return
    def _validate(self):
        """Validate my state.

        This performs profile validations.
        This performs ParameterSet validations.
        This does not validate the operation, since this could be costly. The
        operation should be validated with a containing equation.

        Raises SrFitError if validation fails.

        """
        if self.profile is None:
            raise SrFitError("profile is None")
        self.profile._validate()
        ParameterSet._validate(self)
        return
Exemple #11
0
    def _validate(self):
        """Validate my state.

        This performs profile validations.
        This performs ProfileGenerator validations.
        This validates _eq.
        This validates _reseq and residual.

        Raises SrFitError if validation fails.

        """
        self.profile._validate()
        ParameterSet._validate(self)

        # Try to get the value of eq.
        from diffpy.srfit.equation.visitors import validate
        try:
            validate(self._eq)
        except ValueError, e:
            raise SrFitError(e)
Exemple #12
0
    def _validate(self):
        """Validate my state.

        This performs profile validations.
        This performs ProfileGenerator validations.
        This validates _eq.
        This validates _reseq and residual.

        Raises SrFitError if validation fails.

        """
        self.profile._validate()
        ParameterSet._validate(self)

        # Try to get the value of eq.
        from diffpy.srfit.equation.visitors import validate
        try:
            validate(self._eq)
        except ValueError as e:
            raise SrFitError(e)
        if self._eq is None:
            raise SrFitError("_eq is None")

        val = None
        try:
            val = self._eq()
        except TypeError as e:
            raise SrFitError("_eq cannot be evaluated: %s"%e)

        if val is None:
            raise SrFitError("_eq evaluates to None")

        # Try to get the value for residual
        try:
            validate(self._reseq)
        except ValueError as e:
            raise SrFitError(e)
        try:
            val = self.residual()
        except TypeError:
            raise SrFitError("residual cannot be evaluated")
        if val is None:
            raise SrFitError("residual evaluates to None")
        return
Exemple #13
0
class Constraint(Validatable):
    """Constraint class.

    Constraints are designed to be stored in only one place. (The holder of the
    constraint owns it).

    Attributes
    par     --  A Parameter that is the subject of the constraint.
    eq      --  An equation whose evaluation is used to set the value of the
                constraint.

    """
    def __init__(self):
        """Initialization. """
        self.par = None
        self.eq = None
        return

    def constrain(self, par, eq):
        """Constrain a Parameter according to an Equation.

        The parameter will be set constant once it is constrained. This will
        keep it from being constrained multiple times.

        Raises a ValueError if par is const.

        """

        if par.const:
            raise ValueError("The parameter '%s' is constant" % par)

        if par.constrained:
            raise ValueError("The parameter '%s' is already constrained" % par)

        par.constrained = True

        self.par = par
        self.eq = eq
        self.update()
        return

    def unconstrain(self):
        """Clear the constraint."""
        self.par.constrained = False
        self.par = None
        self.eq = None
        return

    def update(self):
        """Update the parameter according to the equation."""
        # This will be evaluated quickly thanks to the Equation class.
        val = self.eq()
        # This will only change the Parameter if val is different from the
        # currently stored value.
        self.par.setValue(val)
        return

    def _validate(self):
        """Validate my state.

        This validates that par is not None.
        This validates eq.

        Raises SrFitError if validation fails.

        """
        if self.par is None:
            raise SrFitError("par is None")
        if self.eq is None:
            raise SrFitError("eq is None")
        self.par._validate()
        from diffpy.srfit.equation.visitors import validate
        try:
            validate(self.eq)
        except ValueError, e:
            raise SrFitError(e)

        # Try to get the value of eq.
        try:
            val = self.eq()
            self.par.setValue(val)
        except TypeError, e:
            raise SrFitError("eq cannot be evaluated")
Exemple #14
0
class FitContribution(ParameterSet):
    """FitContribution class.

    FitContributions organize an Equation that calculates the signal, and a
    Profile that holds the signal. ProfileGenerators and Calculators can be
    used as well.  Contraints and Restraints can be created as part of a
    FitContribution.

    Attributes
    name            --  A name for this FitContribution.
    profile         --  A Profile that holds the measured (and calculated)
                        signal.
    _calculators    --  A managed dictionary of Calculators, indexed by name.
    _constraints    --  A set of constrained Parameters. Constraints can be
                        added using the 'constrain' methods.
    _generators     --  A managed dictionary of ProfileGenerators.
    _parameters     --  A managed OrderedDict of parameters.
    _restraints     --  A set of Restraints. Restraints can be added using the
                        'restrain' method.
    _parsets        --  A managed dictionary of ParameterSets.
    _eqfactory      --  A diffpy.srfit.equation.builder.EquationFactory
                        instance that is used to create constraints and
                        restraints from string
    _eq             --  The FitContribution equation that will be optimized.
    _reseq          --  The residual equation.
    _xname          --  Name of the x-variable
    _yname          --  Name of the y-variable
    _dyname         --  Name of the dy-variable

    Properties
    names           --  Variable names (read only). See getNames.
    values          --  Variable values (read only). See getValues.

    """

    def __init__(self, name):
        """Initialization."""
        ParameterSet.__init__(self, name)
        self._eq = None
        self._reseq = None
        self.profile = None
        self._xname = None
        self._yname = None
        self._dyname = None

        self._generators = {}
        self._manage(self._generators)
        return

    def setProfile(self, profile, xname = None, yname = None, dyname = None):
        """Assign the Profile for this FitContribution.

        profile --  A Profile that specifies the calculation points and that
                    will store the calculated signal.
        xname   --  The name of the independent variable from the Profile. If
                    this is None (default), then the name specified by the
                    Profile for this parameter will be used.  This variable is
                    usable within string equations with the specified name.
        yname   --  The name of the observed Profile.  If this is None
                    (default), then the name specified by the Profile for this
                    parameter will be used.  This variable is usable within
                    string equations with the specified name.
        dyname  --  The name of the uncertainty in the observed Profile. If
                    this is None (default), then the name specified by the
                    Profile for this parameter will be used.  This variable is
                    usable within string equations with the specified name.

        """
        # Enforce type of the profile argument
        if not isinstance(profile, Profile):
            emsg = "Argument must be an instance of the Profile class."
            raise TypeError(emsg)

        # Set the Profile and add its parameters to this organizer.
        self.profile = profile

        if xname is None:
            xname = self.profile.xpar.name
        if yname is None:
            yname = self.profile.ypar.name
        if dyname is None:
            dyname = self.profile.dypar.name

        self._xname = xname
        self._yname = yname
        self._dyname = dyname

        xpar = ParameterProxy(xname, self.profile.xpar)
        ypar = ParameterProxy(yname, self.profile.ypar)
        dypar = ParameterProxy(dyname, self.profile.dypar)
        self.addParameter(xpar, check = False)
        self.addParameter(ypar, check = False)
        self.addParameter(dypar, check = False)

        # If we have ProfileGenerators, set their Profiles.
        for gen in self._generators.values():
            gen.setProfile(profile)

        # If we have _eq, but not _reseq, set the residual
        if self._eq is not None and self._reseq is None:
            self.setResidualEquation('chiv')

        return


    def addProfileGenerator(self, gen, name = None):
        """Add a ProfileGenerator to be used by this FitContribution.

        The ProfileGenerator is given a name so that it can be used as part of
        the profile equation (see setEquation). This can be different from the
        name of the ProfileGenerator used for attribute access.
        FitContributions should not share ProfileGenerator instances. Different
        ProfileGenerators can share Parameters and ParameterSets, however.

        Calling addProfileGenerator sets the profile equation to call the
        calculator and if there is not a profile equation already.

        gen     --  A ProfileGenerator instance
        name    --  A name for the calculator. If name is None (default), then
                    the ProfileGenerator's name attribute will be used.

        Raises ValueError if the ProfileGenerator has no name.
        Raises ValueError if the ProfileGenerator has the same name as some
        other managed object.

        """
        if name is None:
            name = gen.name

        # Register the generator with the equation factory and add it as a
        # managed object.
        self._eqfactory.registerOperator(name, gen)
        self._addObject(gen, self._generators, True)

        # If we have a profile, set the profile of the generator.
        if self.profile is not None:
            gen.setProfile(self.profile)

        # Make this our equation if we don't have one. This will set the
        # residual equation if necessary.
        if self._eq is None:
            self.setEquation(name)

        return

    def setEquation(self, eqstr, ns = {}):
        """Set the profile equation for the FitContribution.

        This sets the equation that will be used when generating the residual
        for this FitContribution.  The equation will be usable within
        setResidualEquation as "eq", and it takes no arguments.

        eqstr   --  A string representation of the equation. Any Parameter
                    registered by addParameter or setProfile, or function
                    registered by setCalculator, registerFunction or
                    registerStringFunction can be can be used in the equation
                    by name. Other names will be turned into Parameters of this
                    FitContribution.
        ns      --  A dictionary of Parameters, indexed by name, that are used
                    in the eqstr, but not registered (default {}).

        Raises ValueError if ns uses a name that is already used for a
        variable.

        """
        # Build the equation instance.
        eq = equationFromString(eqstr, self._eqfactory,
                                buildargs=True, ns=ns)
        eq.name = "eq"

        # Register any new Parameters.
        for par in self._eqfactory.newargs:
            self._addParameter(par)

        # Register eq as an operator
        self._eqfactory.registerOperator("eq", eq)
        self._eqfactory.wipeout(self._eq)
        self._eq = eq

        # Set the residual if we need to
        if self.profile is not None and self._reseq is None:
            self.setResidualEquation('chiv')

        return


    def getEquation(self):
        """Get math expression string for the active profile equation.

        Return normalized math expression or an empty string if profile
        equation has not been set yet.
        """
        from diffpy.srfit.equation.visitors import getExpression
        rv = ""
        if self._eq is not None:
            rv = getExpression(self._eq)
        return rv


    def setResidualEquation(self, eqstr):
        """Set the residual equation for the FitContribution.

        eqstr   --  A string representation of the residual. If eqstr is None
                    (default), then the previous residual equation will be
                    used, or the chi2 residual will be used if that does not
                    exist.

        Two residuals are preset for convenience, "chiv" and "resv".
        chiv is defined such that dot(chiv, chiv) = chi^2.
        resv is defined such that dot(resv, resv) = Rw^2.
        You can call on these in your residual equation. Note that the quantity
        that will be optimized is the summed square of the residual equation.
        Keep that in mind when defining a new residual or using the built-in
        ones.

        Raises SrFitError if the Profile is not yet defined.
        Raises ValueError if eqstr depends on a Parameter that is not part of
        the FitContribution.

        """
        if self.profile is None:
            raise SrFitError("Assign the Profile first")
        if self._eq is None:
            raise SrFitError("Assign the Equation first")

        chivstr = "(eq - %s)/%s" % (self._yname, self._dyname)
        resvstr = "(eq - %s)/sum(%s**2)**0.5" % (self._yname, self._yname)

        # Get the equation string if it is not defined
        if eqstr == "chiv":
            eqstr = chivstr
        elif eqstr == "resv":
            eqstr = resvstr

        reseq = equationFromString(eqstr, self._eqfactory)
        self._eqfactory.wipeout(self._reseq)
        self._reseq = reseq

        return


    def getResidualEquation(self):
        """Get math expression string for the active residual equation.

        Return normalized math formula or an empty string if residual
        equation has not been configured yet.
        """
        from diffpy.srfit.equation.visitors import getExpression
        rv = ""
        if self._reseq is not None:
            rv = getExpression(self._reseq, eqskip='eq$')
        return rv


    def residual(self):
        """Calculate the residual for this fitcontribution.

        When this method is called, it is assumed that all parameters have been
        assigned their most current values by the FitRecipe. This will be the
        case when being called as part of a FitRecipe refinement.

        The residual is by default an array chiv:
        chiv = (eq() - self.profile.y) / self.profile.dy
        The value that is optimized is dot(chiv, chiv).

        The residual equation can be changed with the setResidualEquation
        method.

        """
        # Assign the calculated profile
        self.profile.ycalc = self._eq()
        # Note that equations only recompute when their inputs are modified, so
        # the following will not recompute the equation.
        return self._reseq()


    def evaluate(self):
        """Evaluate the contribution equation and update profile.ycalc.
        """
        yc = self._eq()
        if self.profile is not None:
            self.profile.ycalc = yc
        return yc


    def _validate(self):
        """Validate my state.

        This performs profile validations.
        This performs ProfileGenerator validations.
        This validates _eq.
        This validates _reseq and residual.

        Raises SrFitError if validation fails.

        """
        self.profile._validate()
        ParameterSet._validate(self)

        # Try to get the value of eq.
        from diffpy.srfit.equation.visitors import validate
        try:
            validate(self._eq)
        except ValueError, e:
            raise SrFitError(e)
        if self._eq is None:
            raise SrFitError("_eq is None")

        val = None
        try:
            val = self._eq()
        except TypeError, e:
            raise SrFitError("_eq cannot be evaluated: %s"%e)
Exemple #15
0
    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, e:
            raise SrFitError(e)

        # Try to get the value of eq.
        try:
            val = self.eq()
        except TypeError, e:
            raise SrFitError("eq cannot be evaluated")
        finally:
            if val is None:
                raise SrFitError("eq evaluates to None")

        return

# End class Restraint

# End of file
Exemple #16
0
        from diffpy.srfit.equation.visitors import validate
        try:
            validate(self._eq)
        except ValueError, e:
            raise SrFitError(e)
        if self._eq is None:
            raise SrFitError("_eq is None")

        val = None
        try:
            val = self._eq()
        except TypeError, e:
            raise SrFitError("_eq cannot be evaluated: %s"%e)

        if val is None:
            raise SrFitError("_eq evaluates to None")

        # Try to get the value for residual
        try:
            validate(self._reseq)
        except ValueError, e:
            raise SrFitError(e)
        try:
            val = self.residual()
        except TypeError, e:
            raise SrFitError("residual cannot be evaluated")
        if val is None:
            raise SrFitError("residual evaluates to None")
        return

# End of file
Exemple #17
0
class Restraint(Validatable):
    """Restraint class.

    Attributes
    eq      --  An equation whose evaluation is compared against the restraint
                bounds.
    lb      --  The lower bound on the restraint evaluation (default -inf).
    ub      --  The lower bound on the restraint evaluation (default inf).
    sig     --  The uncertainty on the bounds (default 1).
    scaled  --  A flag indicating if the restraint is scaled (multiplied) by
                the unrestrained point-average chi^2 (chi^2/numpoints)
                (default False).

    The penalty is calculated as
    (max(0, lb - val, val - ub)/sig)**2
    and val is the value of the calculated equation.  This is multipled by the
    average chi^2 if scaled is True.

    """

    def __init__(self, eq, lb = -inf, ub = inf, sig = 1, scaled = False):
        """Restrain an equation to specified bounds.

        eq      --  An equation whose evaluation is compared against the
                    restraint bounds.
        lb      --  The lower bound on the restraint evaluation (float, default
                    -inf).
        ub      --  The lower bound on the restraint evaluation (float, default
                    inf).
        sig     --  The uncertainty on the bounds (default 1).
        scaled  --  A flag indicating if the restraint is scaled (multiplied)
                    by the unrestrained point-average chi^2 (chi^2/numpoints)
                    (bool, default False).

        """
        self.eq = eq
        self.lb = float(lb)
        self.ub = float(ub)
        self.sig = float(sig)
        self.scaled = bool(scaled)
        return

    def penalty(self, w = 1.0):
        """Calculate the penalty of the restraint.

        w   --  The point-average chi^2 which is optionally used to scale the
                penalty (default 1.0).

        Returns the penalty as a float

        """
        val = self.eq()
        penalty = (max(0, self.lb - val, val - self.ub) / 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, e:
            raise SrFitError(e)

        # Try to get the value of eq.
        try:
            val = self.eq()
        except TypeError, e:
            raise SrFitError("eq cannot be evaluated")