From 3b9b2573a88d6a5495bd1bffcbb05a43bbe55aee Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 9 Mar 2026 14:17:36 -0400 Subject: [PATCH 01/21] constrain and unconstrain deprecation --- docs/examples/coreshellnp.py | 10 +-- docs/examples/crystalpdfall.py | 14 ++-- docs/examples/crystalpdftwodata.py | 4 +- docs/examples/crystalpdftwophase.py | 8 +-- docs/examples/debyemodelII.py | 4 +- docs/examples/npintensity.py | 6 +- docs/examples/npintensityII.py | 6 +- docs/examples/nppdfobjcryst.py | 4 +- docs/examples/nppdfsas.py | 8 +-- docs/examples/simplepdftwophase.py | 4 +- docs/examples/threedoublepeaks.py | 18 ++--- src/diffpy/srfit/fitbase/constraint.py | 60 ++++++++++++++-- src/diffpy/srfit/fitbase/fitrecipe.py | 52 +++++++++++--- src/diffpy/srfit/fitbase/recipeorganizer.py | 79 ++++++++++++++++++--- src/diffpy/srfit/pdf/pdfcontribution.py | 4 +- src/diffpy/srfit/structure/sgconstraints.py | 30 ++++---- tests/conftest.py | 6 +- tests/test_constraint.py | 39 ++++++++++ tests/test_fitrecipe.py | 10 +-- tests/test_recipeorganizer.py | 16 ++--- tests/test_sgconstraints.py | 6 +- 21 files changed, 286 insertions(+), 102 deletions(-) diff --git a/docs/examples/coreshellnp.py b/docs/examples/coreshellnp.py index 430726b1..4a413a70 100644 --- a/docs/examples/coreshellnp.py +++ b/docs/examples/coreshellnp.py @@ -91,14 +91,14 @@ def makeRecipe(stru1, stru2, datname): # diameter to twice the shell radius. recipe.add_variable(contribution.radius, 15) recipe.add_variable(contribution.thickness, 11) - recipe.constrain(contribution.psize, "2 * radius") + recipe.constrain_parameter(contribution.psize, "2 * radius") # Configure the fit variables # Start by configuring the scale factor and resolution factors. # We want the sum of the phase scale factors to be 1. recipe.create_new_variable("scale_CdS", 0.7) - recipe.constrain(generator_cds.scale, "scale_CdS") - recipe.constrain(generator_zns.scale, "1 - scale_CdS") + recipe.constrain_parameter(generator_cds.scale, "scale_CdS") + recipe.constrain_parameter(generator_zns.scale, "1 - scale_CdS") # We also want the resolution factor to be the same on each. # Vary the global scale as well. @@ -117,7 +117,7 @@ def makeRecipe(stru1, stru2, datname): ) # Since we know these have stacking disorder, constrain the B33 adps for # each atom type. - recipe.constrain("B33_1_cds", "B33_0_cds") + recipe.constrain_parameter("B33_1_cds", "B33_0_cds") recipe.add_variable(generator_cds.delta2, name="delta2_cds", value=5) phase_zns = generator_zns.phase @@ -128,7 +128,7 @@ def makeRecipe(stru1, stru2, datname): recipe.add_variable( phase_zns.sgpars.xyzpars.z_1, name="z_1_zns", tag="xyz" ) - recipe.constrain("B33_1_zns", "B33_0_zns") + recipe.constrain_parameter("B33_1_zns", "B33_0_zns") recipe.add_variable(generator_zns.delta2, name="delta2_zns", value=2.5) # Give the recipe away so it can be used! diff --git a/docs/examples/crystalpdfall.py b/docs/examples/crystalpdfall.py index db5fefa3..32e573f0 100644 --- a/docs/examples/crystalpdfall.py +++ b/docs/examples/crystalpdfall.py @@ -113,15 +113,15 @@ def makeRecipe( for par in phase_ni.sgpars: recipe.add_variable(par, name=par.name + "_ni") delta2_ni = recipe.create_new_variable("delta2_ni", 2.5) - recipe.constrain(xgenerator_ni.delta2, delta2_ni) - recipe.constrain(ngenerator_ni.delta2, delta2_ni) - recipe.constrain(xgenerator_sini_ni.delta2, delta2_ni) + recipe.constrain_parameter(xgenerator_ni.delta2, delta2_ni) + recipe.constrain_parameter(ngenerator_ni.delta2, delta2_ni) + recipe.constrain_parameter(xgenerator_sini_ni.delta2, delta2_ni) for par in phase_si.sgpars: recipe.add_variable(par, name=par.name + "_si") delta2_si = recipe.create_new_variable("delta2_si", 2.5) - recipe.constrain(xgenerator_si.delta2, delta2_si) - recipe.constrain(xgenerator_sini_si.delta2, delta2_si) + recipe.constrain_parameter(xgenerator_si.delta2, delta2_si) + recipe.constrain_parameter(xgenerator_sini_si.delta2, delta2_si) # Now the experimental parameters recipe.add_variable(xgenerator_ni.scale, name="xscale_ni") @@ -129,8 +129,8 @@ def makeRecipe( recipe.add_variable(ngenerator_ni.scale, name="nscale_ni") recipe.add_variable(xcontribution_sini.scale, 1.0, "xscale_sini") recipe.create_new_variable("pscale_sini_ni", 0.8) - recipe.constrain(xgenerator_sini_ni.scale, "pscale_sini_ni") - recipe.constrain(xgenerator_sini_si.scale, "1 - pscale_sini_ni") + recipe.constrain_parameter(xgenerator_sini_ni.scale, "pscale_sini_ni") + recipe.constrain_parameter(xgenerator_sini_si.scale, "1 - pscale_sini_ni") # The qdamp parameters are too correlated to vary so we fix them based on # previous measurements. diff --git a/docs/examples/crystalpdftwodata.py b/docs/examples/crystalpdftwodata.py index 601cb01b..e82374b2 100644 --- a/docs/examples/crystalpdftwodata.py +++ b/docs/examples/crystalpdftwodata.py @@ -121,8 +121,8 @@ def makeRecipe(ciffile, xdatname, ndatname): # delta2 is a non-structual material property. Thus, we constrain together # delta2 Parameter from each PDFGenerator. delta2 = recipe.create_new_variable("delta2", 2) - recipe.constrain(xgenerator.delta2, delta2) - recipe.constrain(ngenerator.delta2, delta2) + recipe.constrain_parameter(xgenerator.delta2, delta2) + recipe.constrain_parameter(ngenerator.delta2, delta2) # We only need to constrain phase properties once since there is a single # ObjCrystCrystalParSet for the Crystal. diff --git a/docs/examples/crystalpdftwophase.py b/docs/examples/crystalpdftwophase.py index de9cd965..37feaad5 100644 --- a/docs/examples/crystalpdftwophase.py +++ b/docs/examples/crystalpdftwophase.py @@ -90,12 +90,12 @@ def makeRecipe(niciffile, siciffile, datname): # Start by configuring the scale factor and resolution factors. # We want the sum of the phase scale factors to be 1. recipe.create_new_variable("scale_ni", 0.1) - recipe.constrain(generator_ni.scale, "scale_ni") - recipe.constrain(generator_si.scale, "1 - scale_ni") + recipe.constrain_parameter(generator_ni.scale, "scale_ni") + recipe.constrain_parameter(generator_si.scale, "1 - scale_ni") # We also want the resolution factor to be the same on each. recipe.create_new_variable("qdamp", 0.03) - recipe.constrain(generator_ni.qdamp, "qdamp") - recipe.constrain(generator_si.qdamp, "qdamp") + recipe.constrain_parameter(generator_ni.qdamp, "qdamp") + recipe.constrain_parameter(generator_si.qdamp, "qdamp") # Vary the global scale as well. recipe.add_variable(contribution.scale, 1) diff --git a/docs/examples/debyemodelII.py b/docs/examples/debyemodelII.py index 4817d11d..4115179f 100644 --- a/docs/examples/debyemodelII.py +++ b/docs/examples/debyemodelII.py @@ -89,8 +89,8 @@ def makeRecipeII(): # We create a new Variable and use the recipe's "constrain" method to # associate the Debye temperature parameters with that variable. recipe.create_new_variable("thetaD", 100) - recipe.constrain(recipe.lowT.thetaD, "thetaD") - recipe.constrain(recipe.highT.thetaD, "thetaD") + recipe.constrain_parameter(recipe.lowT.thetaD, "thetaD") + recipe.constrain_parameter(recipe.highT.thetaD, "thetaD") return recipe diff --git a/docs/examples/npintensity.py b/docs/examples/npintensity.py index b9d33075..54843d9f 100644 --- a/docs/examples/npintensity.py +++ b/docs/examples/npintensity.py @@ -290,15 +290,15 @@ def gaussian(q, q0, width): lattice = phase.getLattice() a = lattice.a recipe.add_variable(a) - recipe.constrain(lattice.b, a) - recipe.constrain(lattice.c, a) + recipe.constrain_parameter(lattice.b, a) + recipe.constrain_parameter(lattice.c, a) # We want to refine the thermal parameters as well. We will add a new # Variable that we call "Uiso" and constrain the atomic Uiso values to # this. Note that we don't give Uiso an initial value. The initial value # will be inferred from the following constraints. Uiso = recipe.create_new_variable("Uiso") for atom in phase.getScatterers(): - recipe.constrain(atom.Uiso, Uiso) + recipe.constrain_parameter(atom.Uiso, Uiso) # Give the recipe away so it can be used! return recipe diff --git a/docs/examples/npintensityII.py b/docs/examples/npintensityII.py index 92e5e899..69b54efc 100644 --- a/docs/examples/npintensityII.py +++ b/docs/examples/npintensityII.py @@ -173,15 +173,15 @@ def gaussian(q, q0, width): a = recipe.add_variable(lattice.a) # We want to allow for isotropic expansion, so we'll make constraints for # that. - recipe.constrain(lattice.b, a) - recipe.constrain(lattice.c, a) + recipe.constrain_parameter(lattice.b, a) + recipe.constrain_parameter(lattice.c, a) # We want to refine the thermal parameters as well. We will add a new # variable that we call "Uiso" and constrain the atomic Uiso values to # this. Note that we don't give Uiso an initial value. The initial value # will be inferred from the subsequent constraints. Uiso = recipe.create_new_variable("Uiso") for atom in phase.getScatterers(): - recipe.constrain(atom.Uiso, Uiso) + recipe.constrain_parameter(atom.Uiso, Uiso) # Give the recipe away so it can be used! return recipe diff --git a/docs/examples/nppdfobjcryst.py b/docs/examples/nppdfobjcryst.py index cce39962..33a387ab 100644 --- a/docs/examples/nppdfobjcryst.py +++ b/docs/examples/nppdfobjcryst.py @@ -73,7 +73,7 @@ def makeRecipe(molecule, datname): # has no scattering power. It is only used as a reference point for # our bond length. We don't want to constrain it. if not atom.isDummy(): - recipe.constrain(atom.Biso, Biso) + recipe.constrain_parameter(atom.Biso, Biso) # We need to let the molecule expand. If we were modeling it as a crystal, # we could let the unit cell expand. For instruction purposes, we use a @@ -97,7 +97,7 @@ def makeRecipe(molecule, datname): # This creates a Parameter that moves the second atom according to the # bond length. Note that each Parameter needs a unique name. par = c60.addBondLengthParameter("rad%i" % i, center, atom) - recipe.constrain(par, radius) + recipe.constrain_parameter(par, radius) # Add the correlation term, scale. The scale is too short to effectively # determine qdamp. diff --git a/docs/examples/nppdfsas.py b/docs/examples/nppdfsas.py index 69386a8b..adaae5bc 100644 --- a/docs/examples/nppdfsas.py +++ b/docs/examples/nppdfsas.py @@ -113,8 +113,8 @@ def makeRecipe(ciffile, grdata, iqdata): # Even though the cfcalculator and sasgenerator depend on the same sas # model, we must still constrain the cfcalculator Parameters so that it is # informed of changes in the refined parameters. - recipe.constrain(cfcalculator.radius_a, "radius_a") - recipe.constrain(cfcalculator.radius_b, "radius_b") + recipe.constrain_parameter(cfcalculator.radius_a, "radius_a") + recipe.constrain_parameter(cfcalculator.radius_b, "radius_b") return recipe @@ -126,9 +126,9 @@ def fitRecipe(recipe): recipe.set_weight(recipe.pdf, 0) recipe.fix("all") recipe.free("radius_a", "radius_b", iqscale=1e8) - recipe.constrain("radius_b", "radius_a") + recipe.constrain_parameter("radius_b", "radius_a") scipyOptimize(recipe) - recipe.unconstrain("radius_b") + recipe.unconstrain_parameter("radius_b") # Tune PDF recipe.set_weight(recipe.pdf, 1) diff --git a/docs/examples/simplepdftwophase.py b/docs/examples/simplepdftwophase.py index 7b252baf..209abcf0 100644 --- a/docs/examples/simplepdftwophase.py +++ b/docs/examples/simplepdftwophase.py @@ -47,8 +47,8 @@ def makeRecipe(niciffile, siciffile, datname): # Start by configuring the scale factor and resolution factors. # We want the sum of the phase scale factors to be 1. recipe.create_new_variable("scale_ni", 0.1) - recipe.constrain(contribution.ni.scale, "scale_ni") - recipe.constrain(contribution.si.scale, "1 - scale_ni") + recipe.constrain_parameter(contribution.ni.scale, "scale_ni") + recipe.constrain_parameter(contribution.si.scale, "1 - scale_ni") # We also want the resolution factor to be the same on each. This is done # for free by the PDFContribution. We simply need to add it to the recipe. recipe.add_variable(contribution.qdamp, 0.03) diff --git a/docs/examples/threedoublepeaks.py b/docs/examples/threedoublepeaks.py index 4a3d7eb6..17d23ed1 100644 --- a/docs/examples/threedoublepeaks.py +++ b/docs/examples/threedoublepeaks.py @@ -121,9 +121,9 @@ def peakloc(mu): return 180 / pi * arcsin(pi / 180 * l2 * sin(mu) / l1) recipe.register_function(peakloc) - recipe.constrain(contribution.mu12, "peakloc(mu11)") - recipe.constrain(contribution.mu22, "peakloc(mu21)") - recipe.constrain(contribution.mu32, "peakloc(mu31)") + recipe.constrain_parameter(contribution.mu12, "peakloc(mu11)") + recipe.constrain_parameter(contribution.mu22, "peakloc(mu21)") + recipe.constrain_parameter(contribution.mu32, "peakloc(mu31)") # Vary the width of the peaks. We know the functional form of the peak # broadening. @@ -139,20 +139,20 @@ def sig(sig0, dsig, mu): # Now constrain the peak widths to this recipe.sig0.value = 0.001 recipe.dsig.value = 4.0 - recipe.constrain(contribution.sig11, "sig(sig0, dsig, mu11)") - recipe.constrain( + recipe.constrain_parameter(contribution.sig11, "sig(sig0, dsig, mu11)") + recipe.constrain_parameter( contribution.sig12, "sig(sig0, dsig, mu12)", ns={"mu12": contribution.mu12}, ) - recipe.constrain(contribution.sig21, "sig(sig0, dsig, mu21)") - recipe.constrain( + recipe.constrain_parameter(contribution.sig21, "sig(sig0, dsig, mu21)") + recipe.constrain_parameter( contribution.sig22, "sig(sig0, dsig, mu22)", ns={"mu22": contribution.mu22}, ) - recipe.constrain(contribution.sig31, "sig(sig0, dsig, mu31)") - recipe.constrain( + recipe.constrain_parameter(contribution.sig31, "sig(sig0, dsig, mu31)") + recipe.constrain_parameter( contribution.sig32, "sig(sig0, dsig, mu32)", ns={"mu32": contribution.mu32}, diff --git a/src/diffpy/srfit/fitbase/constraint.py b/src/diffpy/srfit/fitbase/constraint.py index b0a89d19..c24eaf35 100644 --- a/src/diffpy/srfit/fitbase/constraint.py +++ b/src/diffpy/srfit/fitbase/constraint.py @@ -24,6 +24,24 @@ from diffpy.srfit.exceptions import SrFitError from diffpy.srfit.fitbase.validatable import Validatable +from diffpy.utils._deprecator import build_deprecation_message, deprecated + +base = "diffpy.srfit.fitbase.constraint.Constraint" +removal_version = "4.0.0" + +constrain_deprecation_msg = build_deprecation_message( + base, + "constrain", + "constrain_parameter", + removal_version, +) + +unconstrain_deprecation_msg = build_deprecation_message( + base, + "unconstrain", + "unconstrain_parameter", + removal_version, +) class Constraint(Validatable): @@ -47,13 +65,23 @@ def __init__(self): self.eq = None return - def constrain(self, par, eq): + def constrain_parameter(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. + Parameters + ---------- + par : Parameter + The Parameter to constrain. + eq : Equation + The Equation to use to constrain the Parameter. + + Raises + ------ + ValueError + If par is constant or already constrained. """ if par.const: @@ -69,13 +97,37 @@ def constrain(self, par, eq): self.update() return - def unconstrain(self): - """Clear the constraint.""" + @deprecated(constrain_deprecation_msg) + def constrain(self, par, eq): + """This function has been deprecated and will be removed in + version 4.0.0. + + Please use + diffpy.srfit.fitbase.constraint.Constraint.constrain_parameter + instead. + """ + self.constrain_parameter(par, eq) + return + + def unconstrain_parameter(self): + """Clear the constraint from a Parameter.""" self.par.constrained = False self.par = None self.eq = None return + @deprecated(unconstrain_deprecation_msg) + def unconstrain(self): + """This function has been deprecated and will be removed in + version 4.0.0. + + Please use + diffpy.srfit.fitbase.constraint.Constraint.unconstrain_parameter + instead. + """ + self.unconstrain_parameter() + return + def update(self): """Update the parameter according to the equation.""" # This will be evaluated quickly thanks to the Equation class. diff --git a/src/diffpy/srfit/fitbase/fitrecipe.py b/src/diffpy/srfit/fitbase/fitrecipe.py index 95021384..7cc56b21 100644 --- a/src/diffpy/srfit/fitbase/fitrecipe.py +++ b/src/diffpy/srfit/fitbase/fitrecipe.py @@ -125,6 +125,14 @@ base, "boundsToRestraints", "convert_bounds_to_restraints", removal_version ) +constrain_dep_msg = build_deprecation_message( + base, "constrain", "constrain_parameter", removal_version +) + +unconstrain_dep_msg = build_deprecation_message( + base, "unconstrain", "unconstrain_parameter", removal_version +) + class FitRecipe(_fitrecipe_interface, RecipeOrganizer): """FitRecipe class. @@ -472,6 +480,11 @@ def removeParameterSet(self, parset): def residual(self, p=[]): """Calculate the vector residual to be optimized. + The residual is by default the weighted concatenation of each + FitContribution's residual, plus the value of each restraint. The array + returned, denoted chiv, is such that + dot(chiv, chiv) = chi^2 + restraints. + Parameters ---------- p : list or numpy.ndarray @@ -481,10 +494,11 @@ def residual(self, p=[]): updated in some other way, and the explicit update within this function is skipped. - The residual is by default the weighted concatenation of each - FitContribution's residual, plus the value of each restraint. The array - returned, denoted chiv, is such that - dot(chiv, chiv) = chi^2 + restraints. + Return + ------ + chiv : numpy.ndarray + The array of residuals to be optimized. The array is such that + dot(chiv, chiv) = chi^2 + restraints. """ # Prepare, if necessary @@ -1093,7 +1107,7 @@ def isFree(self, var): """ return self.is_free(var) - def unconstrain(self, *pars): + def unconstrain_parameter(self, *pars): """Unconstrain a Parameter. This removes any constraints on a Parameter. If the Parameter is also a @@ -1119,7 +1133,7 @@ def unconstrain(self, *pars): raise ValueError("The parameter cannot be found") if par in self._constraints: - self._constraints[par].unconstrain() + self._constraints[par].unconstrain_parameter() del self._constraints[par] update = True @@ -1132,7 +1146,18 @@ def unconstrain(self, *pars): return - def constrain(self, par, con, ns={}): + @deprecated(unconstrain_dep_msg) + def unconstrain(self, *pars): + """This function has been deprecated and will be removed in + version 4.0.0. + + Please use diffpy.srfit.fitbase.FitRecipe.unconstrain_parameter + instead. + """ + self.unconstrain_parameter(*pars) + return + + def constrain_parameter(self, par, con, ns={}): """Constrain a parameter to an equation. Note that only one constraint can exist on a Parameter at a time. @@ -1190,7 +1215,18 @@ def constrain(self, par, con, ns={}): if par in self._parameters.values(): self.fix(par) - RecipeOrganizer.constrain(self, par, con, ns) + RecipeOrganizer.constrain_parameter(self, par, con, ns) + return + + @deprecated(constrain_dep_msg) + def constrain(self, par, con, ns={}): + """This function has been deprecated and will be removed in + version 4.0.0. + + Please use diffpy.srfit.fitbase.FitRecipe.constrain_parameter + instead. + """ + self.constrain_parameter(par, con, ns) return def get_values(self): diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index 495b3706..1b63030d 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -118,6 +118,27 @@ removal_version, ) +addRestraint_deprecation_msg = build_deprecation_message( + recipeorganizer_base, + "addRestraint", + "add_restraint", + removal_version, +) + +constrain_deprecation_msg = build_deprecation_message( + recipeorganizer_base, + "constrain", + "constrain_parameter", + removal_version, +) + +unconstrain_deprecation_msg = build_deprecation_message( + recipeorganizer_base, + "unconstrain", + "unconstrain_parameter", + removal_version, +) + class RecipeContainer(Observable, Configurable, Validatable): """Base class for organizing pieces of a FitRecipe. @@ -883,7 +904,7 @@ def evaluateEquation(self, eqstr, ns={}): """ return self.evaluate_equation(eqstr, func_params=ns) - def constrain(self, parameter, constraint_eq, params={}): + def constrain_parameter(self, parameter, constraint_eq, params={}): """Constrain a parameter to an equation. Note that only one constraint can exist on a Parameter at a time. @@ -936,7 +957,7 @@ def constrain(self, parameter, constraint_eq, params={}): # Make and store the constraint constraint_eq = Constraint() - constraint_eq.constrain(parameter, eq) + constraint_eq.constrain_parameter(parameter, eq) # Store the equation string so it can be shown later. constraint_eq.eqstr = eqstr self._constraints[parameter] = constraint_eq @@ -946,6 +967,18 @@ def constrain(self, parameter, constraint_eq, params={}): return + @deprecated(constrain_deprecation_msg) + def constrain(self, parameter, constraint_eq, params={}): + """This function has been deprecated and will be removed in + version 4.0.0. + + Please use + diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.constrain_parameter + instead. + """ + self.constrain_parameter(parameter, constraint_eq, params=params) + return + def is_constrained(self, parameter): """Determine if a Parameter is constrained in this object. @@ -977,14 +1010,14 @@ def isConstrained(self, parameter): """ return self.is_constrained(parameter) - def unconstrain(self, *pars): + def unconstrain_parameter(self, *pars): """Unconstrain a Parameter. This removes any constraints on a Parameter. Attributes ---------- - *pars + *pars : str or Parameter The names of Parameters or Parameters to unconstrain. @@ -1000,7 +1033,7 @@ def unconstrain(self, *pars): raise ValueError("The parameter cannot be found") if parameter in self._constraints: - self._constraints[parameter].unconstrain() + self._constraints[parameter].unconstrain_parameter() del self._constraints[parameter] update = True @@ -1014,6 +1047,18 @@ def unconstrain(self, *pars): return + @deprecated(unconstrain_deprecation_msg) + def unconstrain(self, *pars): + """This function has been deprecated and will be removed in + version 4.0.0. + + Please use + diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.unconstrain_parameter + instead. + """ + self.unconstrain_parameter(*pars) + return + def get_constrained_parmeters(self, recurse=False): """Get a list of constrained managed Parameters in this object. @@ -1059,7 +1104,7 @@ def clear_all_constraints(self, recurse=False): sub-objects are also cleared. """ if self._constraints: - self.unconstrain(*self._constraints) + self.unconstrain_parameter(*self._constraints) if recurse: for m in filter(_has_clear_constraints, self._iter_managed()): @@ -1136,15 +1181,15 @@ def restrain( # Make and store the restraint param_or_eq = Restraint(eq, lb, ub, sig, scaled) param_or_eq.eqstr = eqstr - self.addRestraint(param_or_eq) + self.add_restraint(param_or_eq) return param_or_eq - def addRestraint(self, res): + def add_restraint(self, res): """Add a Restraint instance to the RecipeOrganizer. - Attributes + Parameters ---------- - res + res : Restraint A Restraint instance. """ self._restraints.add(res) @@ -1152,6 +1197,18 @@ def addRestraint(self, res): self._update_configuration() return + @deprecated(addRestraint_deprecation_msg) + def addRestraint(self, res): + """This function has been deprecated and will be removed in + version 4.0.0. + + Please use + diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.add_restraint + instead. + """ + self.add_restraint(res) + return + def unrestrain(self, *ress): """Remove a Restraint from the RecipeOrganizer. @@ -1159,7 +1216,7 @@ def unrestrain(self, *ress): ---------- *ress Restraints returned from the 'restrain' method or added - with the 'addRestraint' method. + with the 'add_restraint' method. """ update = False restuple = tuple(self._restraints) diff --git a/src/diffpy/srfit/pdf/pdfcontribution.py b/src/diffpy/srfit/pdf/pdfcontribution.py index 9b2bdd93..8ef1578b 100644 --- a/src/diffpy/srfit/pdf/pdfcontribution.py +++ b/src/diffpy/srfit/pdf/pdfcontribution.py @@ -287,8 +287,8 @@ def _setup_generator(self, gen): gen.processMetaData() # Constrain the shared parameters - self.constrain(gen.qdamp, self.qdamp) - self.constrain(gen.qbroad, self.qbroad) + self.constrain_parameter(gen.qdamp, self.qdamp) + self.constrain_parameter(gen.qbroad, self.qbroad) return # Calculation setup methods diff --git a/src/diffpy/srfit/structure/sgconstraints.py b/src/diffpy/srfit/structure/sgconstraints.py index 8e1e198d..681bad1b 100644 --- a/src/diffpy/srfit/structure/sgconstraints.py +++ b/src/diffpy/srfit/structure/sgconstraints.py @@ -391,7 +391,7 @@ def _clear_constraints(self): for par in [scatterer.x, scatterer.y, scatterer.z]: if scatterer.is_constrained(par): - scatterer.unconstrain(par) + scatterer.unconstrain_parameter(par) par.setConst(False) # Clear the lattice @@ -408,7 +408,7 @@ def _clear_constraints(self): ] for par in latpars: if lattice.is_constrained(par): - lattice.unconstrain(par) + lattice.unconstrain_parameter(par) par.setConst(False) # Clear ADPs @@ -418,14 +418,14 @@ def _clear_constraints(self): par = scatterer.get(isosymbol) if par is not None: if scatterer.is_constrained(par): - scatterer.unconstrain(par) + scatterer.unconstrain_parameter(par) par.setConst(False) for pname in adpsymbols: par = scatterer.get(pname) if par is not None: if scatterer.is_constrained(par): - scatterer.unconstrain(par) + scatterer.unconstrain_parameter(par) par.setConst(False) return @@ -596,7 +596,7 @@ def _constrain_adps(self, positions): continue isoidx.append(j) scatterer = scatterers[j] - scatterer.constrain( + scatterer.constrain_parameter( isosymbol, isoname, params=self._parameters ) @@ -691,7 +691,7 @@ def _constrain_tetragonal(lattice): lattice.alpha.setConst(True, ang90) lattice.beta.setConst(True, ang90) lattice.gamma.setConst(True, ang90) - lattice.constrain(lattice.b, lattice.a) + lattice.constrain_parameter(lattice.b, lattice.a) return @@ -708,15 +708,15 @@ def _constrain_trigonal(lattice): ang90 = 90.0 * afactor ang120 = 120.0 * afactor if lattice.gamma.getValue() == ang120: - lattice.constrain(lattice.b, lattice.a) + lattice.constrain_parameter(lattice.b, lattice.a) lattice.alpha.setConst(True, ang90) lattice.beta.setConst(True, ang90) lattice.gamma.setConst(True, ang120) else: - lattice.constrain(lattice.b, lattice.a) - lattice.constrain(lattice.c, lattice.a) - lattice.constrain(lattice.beta, lattice.alpha) - lattice.constrain(lattice.gamma, lattice.alpha) + lattice.constrain_parameter(lattice.b, lattice.a) + lattice.constrain_parameter(lattice.c, lattice.a) + lattice.constrain_parameter(lattice.beta, lattice.alpha) + lattice.constrain_parameter(lattice.gamma, lattice.alpha) return @@ -731,7 +731,7 @@ def _constrain_hexagonal(lattice): afactor = deg2rad ang90 = 90.0 * afactor ang120 = 120.0 * afactor - lattice.constrain(lattice.b, lattice.a) + lattice.constrain_parameter(lattice.b, lattice.a) lattice.alpha.setConst(True, ang90) lattice.beta.setConst(True, ang90) lattice.gamma.setConst(True, ang120) @@ -748,8 +748,8 @@ def _constrain_cubic(lattice): if lattice.angunits == "rad": afactor = deg2rad ang90 = 90.0 * afactor - lattice.constrain(lattice.b, lattice.a) - lattice.constrain(lattice.c, lattice.a) + lattice.constrain_parameter(lattice.b, lattice.a) + lattice.constrain_parameter(lattice.c, lattice.a) lattice.alpha.setConst(True, ang90) lattice.beta.setConst(True, ang90) lattice.gamma.setConst(True, ang90) @@ -811,7 +811,7 @@ def _makeconstraint(parname, formula, scatterer, idx, ns={}): # If we got here, then we have a constraint equation # Fix any division issues formula = formula.replace("/", "*1.0/") - scatterer.constrain(par, formula, params=ns) + scatterer.constrain_parameter(par, formula, params=ns) return diff --git a/tests/conftest.py b/tests/conftest.py index f6e58e7e..e053c732 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -215,9 +215,9 @@ def build_recipe_two_contributions(): recipe.add_variable(contribution2.d, 0.1) # ---- Meaningful constraints ---- - recipe.constrain(contribution2.m, "2*k") - recipe.constrain(contribution2.d, contribution1.c) - recipe.constrain(contribution2.B, "0.5*A") + recipe.constrain_parameter(contribution2.m, "2*k") + recipe.constrain_parameter(contribution2.d, contribution1.c) + recipe.constrain_parameter(contribution2.B, "0.5*A") recipe.restrain(contribution1.A, 0.5, 1.5) recipe.restrain(contribution1.k, 0.8, 1.2) diff --git a/tests/test_constraint.py b/tests/test_constraint.py index 00da5b14..a72a69fa 100644 --- a/tests/test_constraint.py +++ b/tests/test_constraint.py @@ -24,6 +24,45 @@ class TestConstraint(unittest.TestCase): + def test_constrain_parameter(self): + """Test the Constraint class.""" + + p1 = Parameter("p1", 1) + p2 = Parameter("p2", 2) + + factory = EquationFactory() + + factory.registerArgument("p1", p1) + factory.registerArgument("p2", p2) + + c = Constraint() + # Constrain p1 = 2*p2 + eq = equationFromString("2*p2", factory) + c.constrain_parameter(p1, eq) + + self.assertTrue(p1.constrained) + self.assertFalse(p2.constrained) + + eq2 = equationFromString("2*p2+1", factory) + c2 = Constraint() + self.assertRaises(ValueError, c2.constrain, p1, eq2) + p2.setConst() + eq3 = equationFromString("p1", factory) + self.assertRaises(ValueError, c2.constrain, p2, eq3) + + p2.set_value(2.5) + c.update() + self.assertEqual(5.0, p1.getValue()) + + p2.set_value(8.1) + self.assertEqual(5.0, p1.getValue()) + c.update() + self.assertEqual(16.2, p1.getValue()) + return + + +class TestConstraint_deprecated(unittest.TestCase): + def testConstraint(self): """Test the Constraint class.""" diff --git a/tests/test_fitrecipe.py b/tests/test_fitrecipe.py index 4c928431..b64b3f99 100644 --- a/tests/test_fitrecipe.py +++ b/tests/test_fitrecipe.py @@ -119,7 +119,7 @@ def test_variables(self): # Constrain a parameter to the B-variable to give it a value p = Parameter("Bpar", -1) - recipe.constrain(recipe.B, p) + recipe.constrain_parameter(recipe.B, p) values = recipe.get_values() self.assertTrue((values == [2, 1, 0]).all()) recipe.delete_variable(recipe.B) @@ -215,11 +215,11 @@ def testResidual(self): # Try some constraints # Make c = 2*A, A = Avar var = self.recipe.create_new_variable("Avar") - self.recipe.constrain( + self.recipe.constrain_parameter( self.fitcontribution.c, "2*A", {"A": self.fitcontribution.A} ) self.assertEqual(2, self.fitcontribution.c.value) - self.recipe.constrain(self.fitcontribution.A, var) + self.recipe.constrain_parameter(self.fitcontribution.A, var) self.assertEqual(1, var.getValue()) self.assertEqual(self.recipe.cont.A.getValue(), var.getValue()) # c is constrained to a constrained parameter. @@ -240,7 +240,7 @@ def testResidual(self): # Clear the constraint and restore the value of c to 0. This should # give us chi2 = 0 again. - self.recipe.unconstrain(self.fitcontribution.c) + self.recipe.unconstrain_parameter(self.fitcontribution.c) self.fitcontribution.c.set_value(0) res = self.recipe.residual([self.recipe.cont.A.getValue()]) chi2 = 0 @@ -255,7 +255,7 @@ def testResidual(self): self.assertAlmostEqual(chi2, dot(res, res)) # Add constraints at the fitcontribution level. - self.fitcontribution.constrain(self.fitcontribution.c, "2*A") + self.fitcontribution.constrain_parameter(self.fitcontribution.c, "2*A") # This should evaluate to sin(x+2) x = self.profile.x y = sin(x + 2) diff --git a/tests/test_recipeorganizer.py b/tests/test_recipeorganizer.py index bdd38058..c6bc19a7 100644 --- a/tests/test_recipeorganizer.py +++ b/tests/test_recipeorganizer.py @@ -259,7 +259,7 @@ def testRemoveParameter(self): self.assertRaises(ValueError, m._remove_parameter, c) return - def test_constrain(self): + def test_constrain_parameter(self): """Test the constrain method.""" p1 = self.m._new_parameter("p1", 1) @@ -268,7 +268,7 @@ def test_constrain(self): self.assertFalse(p1.constrained) self.assertEqual(0, len(self.m._constraints)) - self.m.constrain(p1, "2*p2") + self.m.constrain_parameter(p1, "2*p2") actual_constrained_params = self.m.getConstrainedPars() actual_constrained_params = [p.name for p in actual_constrained_params] @@ -296,13 +296,13 @@ def test_constrain(self): self.assertRaises(ValueError, self.m.constrain, p1, "2*p2", {"p2": p3}) # Remove the constraint - self.m.unconstrain(p1) + self.m.unconstrain_parameter(p1) self.assertFalse(p1.constrained) self.assertEqual(0, len(self.m._constraints)) self.assertFalse(self.m.isConstrained(p1)) # Try an straight constraint - self.m.constrain(p1, p2) + self.m.constrain_parameter(p1, p2) p2.set_value(7) self.m._constraints[p1].update() self.assertEqual(7, p1.getValue()) @@ -314,7 +314,7 @@ def test_constrain(self): assert actual_constrained_params == expected_constrained_params # add constraint back and test the old function name `clearConstraints` - self.m.constrain(p1, p2) + self.m.constrain_parameter(p1, p2) actual_constrained_params = self.m.get_constrained_parmeters() actual_constrained_params = [p.name for p in actual_constrained_params] expected_constrained_params = [p1.name] @@ -375,8 +375,8 @@ def testGetConstraints(self): m2._add_parameter(p3) m2._add_parameter(p4) - self.m.constrain(p1, "p2") - m2.constrain(p3, "p4") + self.m.constrain_parameter(p1, "p2") + m2.constrain_parameter(p3, "p4") cons = self.m._get_constraints() self.assertTrue(p1 in cons) @@ -625,7 +625,7 @@ def capture_show(*args, **kwargs): assert "Constraints" not in lines1 assert "Restraints" not in lines1 organizer._new_parameter("z", 7) - organizer.constrain("y", "3 * z") + organizer.constrain_parameter("y", "3 * z") out2 = capture_show() lines2 = out2.strip().split("\n") assert 9 == len(lines2) diff --git a/tests/test_sgconstraints.py b/tests/test_sgconstraints.py index 11859a9f..19bb49f2 100644 --- a/tests/test_sgconstraints.py +++ b/tests/test_sgconstraints.py @@ -87,13 +87,13 @@ def test_ObjCryst_constrain_space_group(pyobjcryst_available): # Make sure we can't constrain these with pytest.raises(ValueError): - mn.constrain(mn.x, "y") + mn.constrain_parameter(mn.x, "y") with pytest.raises(ValueError): - mn.constrain(mn.y, "z") + mn.constrain_parameter(mn.y, "z") with pytest.raises(ValueError): - mn.constrain(mn.z, "x") + mn.constrain_parameter(mn.z, "x") # Nor can we make them into variables from diffpy.srfit.fitbase.fitrecipe import FitRecipe From 9993779e94e1701e7653f647ebb8eb61b8512220 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 9 Mar 2026 14:32:29 -0400 Subject: [PATCH 02/21] change name to register_restraint --- src/diffpy/srfit/fitbase/recipeorganizer.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index 1b63030d..d1f6a9ba 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -121,7 +121,7 @@ addRestraint_deprecation_msg = build_deprecation_message( recipeorganizer_base, "addRestraint", - "add_restraint", + "register_restraint", removal_version, ) @@ -1181,10 +1181,10 @@ def restrain( # Make and store the restraint param_or_eq = Restraint(eq, lb, ub, sig, scaled) param_or_eq.eqstr = eqstr - self.add_restraint(param_or_eq) + self.register_restraint(param_or_eq) return param_or_eq - def add_restraint(self, res): + def register_restraint(self, res): """Add a Restraint instance to the RecipeOrganizer. Parameters @@ -1203,10 +1203,10 @@ def addRestraint(self, res): version 4.0.0. Please use - diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.add_restraint + diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.register_restraint instead. """ - self.add_restraint(res) + self.register_restraint(res) return def unrestrain(self, *ress): @@ -1216,7 +1216,7 @@ def unrestrain(self, *ress): ---------- *ress Restraints returned from the 'restrain' method or added - with the 'add_restraint' method. + with the 'register_restraint' method. """ update = False restuple = tuple(self._restraints) From 1f301a37ef2cadbaec68b9d0bc3442002aec0015 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 9 Mar 2026 14:44:23 -0400 Subject: [PATCH 03/21] restrain deprecation --- docs/examples/crystalpdftwophase.py | 12 +++++------ docs/examples/debyemodel.py | 2 +- docs/examples/simplepdftwophase.py | 12 +++++------ src/diffpy/srfit/fitbase/fitrecipe.py | 2 +- src/diffpy/srfit/fitbase/recipeorganizer.py | 24 ++++++++++++++++++++- tests/conftest.py | 4 ++-- tests/test_fitrecipe.py | 10 +++++---- tests/test_recipeorganizer.py | 10 ++++----- 8 files changed, 50 insertions(+), 26 deletions(-) diff --git a/docs/examples/crystalpdftwophase.py b/docs/examples/crystalpdftwophase.py index 37feaad5..3f0a0194 100644 --- a/docs/examples/crystalpdftwophase.py +++ b/docs/examples/crystalpdftwophase.py @@ -123,18 +123,18 @@ def makeRecipe(niciffile, siciffile, datname): # derived has no uncertainty. Thus, we will tell the recipe to scale the # residual, which means that it will be weighted as much as the average # data point during the fit. - recipe.restrain("a_ni", lb=3.527, ub=3.527, scaled=True) + recipe.add_restraint("a_ni", lb=3.527, ub=3.527, scaled=True) # Now we do the same with the delta2 and Biso parameters (remember that # Biso = 8*pi**2*Uiso) - recipe.restrain("delta2_ni", lb=2.22, ub=2.22, scaled=True) - recipe.restrain("Biso_0_ni", lb=0.454, ub=0.454, scaled=True) + recipe.add_restraint("delta2_ni", lb=2.22, ub=2.22, scaled=True) + recipe.add_restraint("Biso_0_ni", lb=0.454, ub=0.454, scaled=True) # # We can do the same with the silicon values. We haven't done a thorough # job of measuring the uncertainties in the results, so we'll scale these # as well. - recipe.restrain("a_si", lb=5.430, ub=5.430, scaled=True) - recipe.restrain("delta2_si", lb=3.54, ub=3.54, scaled=True) - recipe.restrain("Biso_0_si", lb=0.645, ub=0.645, scaled=True) + recipe.add_restraint("a_si", lb=5.430, ub=5.430, scaled=True) + recipe.add_restraint("delta2_si", lb=3.54, ub=3.54, scaled=True) + recipe.add_restraint("Biso_0_si", lb=0.645, ub=0.645, scaled=True) # Give the recipe away so it can be used! return recipe diff --git a/docs/examples/debyemodel.py b/docs/examples/debyemodel.py index cd2dcbbd..c49c13b0 100644 --- a/docs/examples/debyemodel.py +++ b/docs/examples/debyemodel.py @@ -152,7 +152,7 @@ def makeRecipe(): # breaking the restraint by the point-average chi^2 value so that the # restraint is roughly as significant as any other data point throughout # the fit. - recipe.restrain(recipe.offset, lb=0, scaled=True) + recipe.add_restraint(recipe.offset, lb=0, scaled=True) # We're done setting up the recipe. We can now do other things with it. return recipe diff --git a/docs/examples/simplepdftwophase.py b/docs/examples/simplepdftwophase.py index 209abcf0..e0735514 100644 --- a/docs/examples/simplepdftwophase.py +++ b/docs/examples/simplepdftwophase.py @@ -82,18 +82,18 @@ def makeRecipe(niciffile, siciffile, datname): # derived has no uncertainty. Thus, we will tell the recipe to scale the # residual, which means that it will be weighted as much as the average # data point during the fit. - recipe.restrain("a_ni", lb=3.527, ub=3.527, scaled=True) + recipe.add_restraint("a_ni", lb=3.527, ub=3.527, scaled=True) # Now we do the same with the delta2 and Biso parameters (remember that # Biso = 8*pi**2*Uiso) - recipe.restrain("delta2_ni", lb=2.22, ub=2.22, scaled=True) - recipe.restrain("Biso_0_ni", lb=0.454, ub=0.454, scaled=True) + recipe.add_restraint("delta2_ni", lb=2.22, ub=2.22, scaled=True) + recipe.add_restraint("Biso_0_ni", lb=0.454, ub=0.454, scaled=True) # # We can do the same with the silicon values. We haven't done a thorough # job of measuring the uncertainties in the results, so we'll scale these # as well. - recipe.restrain("a_si", lb=5.430, ub=5.430, scaled=True) - recipe.restrain("delta2_si", lb=3.54, ub=3.54, scaled=True) - recipe.restrain("Biso_0_si", lb=0.645, ub=0.645, scaled=True) + recipe.add_restraint("a_si", lb=5.430, ub=5.430, scaled=True) + recipe.add_restraint("delta2_si", lb=3.54, ub=3.54, scaled=True) + recipe.add_restraint("Biso_0_si", lb=0.645, ub=0.645, scaled=True) # Give the recipe away so it can be used! return recipe diff --git a/src/diffpy/srfit/fitbase/fitrecipe.py b/src/diffpy/srfit/fitbase/fitrecipe.py index 7cc56b21..1dc61fbc 100644 --- a/src/diffpy/srfit/fitbase/fitrecipe.py +++ b/src/diffpy/srfit/fitbase/fitrecipe.py @@ -1756,7 +1756,7 @@ def convert_bounds_to_restraints(self, sig=1, scaled=False): if not hasattr(sig, "__iter__"): sig = [sig] * len(pars) for par, x in zip(pars, sig): - self.restrain( + self.add_restraint( par, par.bounds[0], par.bounds[1], sig=x, scaled=scaled ) return diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index d1f6a9ba..f35037bf 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -139,6 +139,13 @@ removal_version, ) +restrain_deprecation_msg = build_deprecation_message( + recipeorganizer_base, + "restrain", + "add_restraint", + removal_version, +) + class RecipeContainer(Observable, Configurable, Validatable): """Base class for organizing pieces of a FitRecipe. @@ -1122,7 +1129,7 @@ def clearConstraints(self, recurse=False): """ return self.clear_all_constraints(recurse=recurse) - def restrain( + def add_restraint( self, param_or_eq, lb=-inf, ub=inf, sig=1, scaled=False, params={} ): """Restrain an expression to specified bounds. @@ -1184,6 +1191,21 @@ def restrain( self.register_restraint(param_or_eq) return param_or_eq + @deprecated(restrain_deprecation_msg) + def restrain( + self, param_or_eq, lb=-inf, ub=inf, sig=1, scaled=False, params={} + ): + """This function has been deprecated and will be removed in + version 4.0.0. + + Please use + diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.add_restraint + instead. + """ + return self.add_restraint( + param_or_eq, lb=lb, ub=ub, sig=sig, scaled=scaled, params=params + ) + def register_restraint(self, res): """Add a Restraint instance to the RecipeOrganizer. diff --git a/tests/conftest.py b/tests/conftest.py index e053c732..118f30d4 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -218,8 +218,8 @@ def build_recipe_two_contributions(): recipe.constrain_parameter(contribution2.m, "2*k") recipe.constrain_parameter(contribution2.d, contribution1.c) recipe.constrain_parameter(contribution2.B, "0.5*A") - recipe.restrain(contribution1.A, 0.5, 1.5) - recipe.restrain(contribution1.k, 0.8, 1.2) + recipe.add_restraint(contribution1.A, 0.5, 1.5) + recipe.add_restraint(contribution1.k, 0.8, 1.2) return recipe diff --git a/tests/test_fitrecipe.py b/tests/test_fitrecipe.py index b64b3f99..2fb6f8c2 100644 --- a/tests/test_fitrecipe.py +++ b/tests/test_fitrecipe.py @@ -232,7 +232,7 @@ def testResidual(self): # Now try some restraints. We want c to be exactly zero. It should give # a penalty of (c-0)**2, which is 4 in this case - r1 = self.recipe.restrain(self.fitcontribution.c, 0, 0, 1) + r1 = self.recipe.add_restraint(self.fitcontribution.c, 0, 0, 1) self.recipe._ready = False res = self.recipe.residual() chi2 = 4 + dot(y - self.profile.y, y - self.profile.y) @@ -263,7 +263,9 @@ def testResidual(self): self.assertTrue(array_equal(y - self.profile.y, res)) # Add a restraint at the fitcontribution level. - r1 = self.fitcontribution.restrain(self.fitcontribution.c, 0, 0, 1) + r1 = self.fitcontribution.add_restraint( + self.fitcontribution.c, 0, 0, 1 + ) self.recipe._ready = False # The chi2 is the same as above, plus 4 res = self.recipe.residual() @@ -361,7 +363,7 @@ def testPrintFitHook(capturestdout): recipe.addContribution(fitcontribution) recipe.add_variable(fitcontribution.c) - recipe.restrain("c", lb=5) + recipe.add_restraint("c", lb=5) (pfh,) = recipe.getFitHooks() out = capturestdout(recipe.scalar_residual) assert "" == out @@ -437,7 +439,7 @@ def test_add_contribution(capturestdout): recipe.add_contribution(fitcontribution) recipe.add_variable(fitcontribution.c) - recipe.restrain("c", lb=5) + recipe.add_restraint("c", lb=5) (pfh,) = recipe.get_fit_hooks() out = capturestdout(recipe.scalar_residual) assert "" == out diff --git a/tests/test_recipeorganizer.py b/tests/test_recipeorganizer.py index c6bc19a7..cb93d929 100644 --- a/tests/test_recipeorganizer.py +++ b/tests/test_recipeorganizer.py @@ -328,7 +328,7 @@ def test_constrain_parameter(self): return - def testRestrain(self): + def test_add_restraint(self): """Test the restrain method.""" p1 = Parameter("p1", 1) @@ -338,7 +338,7 @@ def testRestrain(self): self.m._eqfactory.registerArgument("p2", p2) self.assertEqual(0, len(self.m._restraints)) - r = self.m.restrain("p1+p2", ub=10) + r = self.m.add_restraint("p1+p2", ub=10) self.assertEqual(1, len(self.m._restraints)) p2.set_value(10) self.assertEqual(1, r.penalty()) @@ -402,8 +402,8 @@ def testGetRestraints(self): m2._add_parameter(p3) m2._add_parameter(p4) - r1 = self.m.restrain("p1 + p2") - r2 = m2.restrain("2*p3 + p4") + r1 = self.m.add_restraint("p1 + p2") + r2 = m2.add_restraint("2*p3 + p4") res = self.m._get_restraints() self.assertTrue(r1 in res) @@ -632,7 +632,7 @@ def capture_show(*args, **kwargs): assert "Parameters" in lines2 assert "Constraints" in lines2 assert "Restraints" not in lines2 - organizer.restrain("z", lb=2, ub=3, sig=0.001) + organizer.add_restraint("z", lb=2, ub=3, sig=0.001) out3 = capture_show() lines3 = out3.strip().split("\n") assert 13 == len(lines3) From 8184be43f1e8ad70846ec62151a9b9def6e29f9b Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 9 Mar 2026 14:49:05 -0400 Subject: [PATCH 04/21] unrestrain deprecation --- src/diffpy/srfit/fitbase/recipeorganizer.py | 27 ++++++++++++++++++--- tests/test_fitrecipe.py | 4 +-- tests/test_recipeorganizer.py | 2 +- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index f35037bf..f9415df0 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -146,6 +146,13 @@ removal_version, ) +unrestrain_deprecation_msg = build_deprecation_message( + recipeorganizer_base, + "unrestrain", + "remove_restraint", + removal_version, +) + class RecipeContainer(Observable, Configurable, Validatable): """Base class for organizing pieces of a FitRecipe. @@ -1231,13 +1238,13 @@ def addRestraint(self, res): self.register_restraint(res) return - def unrestrain(self, *ress): + def remove_restraint(self, *ress): """Remove a Restraint from the RecipeOrganizer. Attributes ---------- - *ress - Restraints returned from the 'restrain' method or added + *ress : + The Restraints returned from the 'add_restraint' method or added with the 'register_restraint' method. """ update = False @@ -1253,6 +1260,18 @@ def unrestrain(self, *ress): return + @deprecated(unrestrain_deprecation_msg) + def unrestrain(self, *ress): + """This function has been deprecated and will be removed in + version 4.0.0. + + Please use + diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.remove_restraint + instead. + """ + self.remove_restraint(*ress) + return + def clearRestraints(self, recurse=False): """Clear all restraints. @@ -1262,7 +1281,7 @@ def clearRestraints(self, recurse=False): Recurse into managed objects and clear all restraints found there as well. """ - self.unrestrain(*self._restraints) + self.remove_restraint(*self._restraints) if recurse: for msg in filter(_has_clear_restraints, self._iter_managed()): msg.clearRestraints(recurse) diff --git a/tests/test_fitrecipe.py b/tests/test_fitrecipe.py index 2fb6f8c2..3e165268 100644 --- a/tests/test_fitrecipe.py +++ b/tests/test_fitrecipe.py @@ -247,7 +247,7 @@ def testResidual(self): self.assertAlmostEqual(chi2, dot(res, res)) # Remove the restraint and variable - self.recipe.unrestrain(r1) + self.recipe.remove_restraint(r1) self.recipe.delete_variable(self.recipe.Avar) self.recipe._ready = False res = self.recipe.residual() @@ -275,7 +275,7 @@ def testResidual(self): self.assertAlmostEqual(chi2, dot(res, res)) # Remove those - self.fitcontribution.unrestrain(r1) + self.fitcontribution.remove_restraint(r1) self.recipe._ready = False self.fitcontribution.unconstrain(self.fitcontribution.c) self.fitcontribution.c.set_value(0) diff --git a/tests/test_recipeorganizer.py b/tests/test_recipeorganizer.py index cb93d929..922cb33f 100644 --- a/tests/test_recipeorganizer.py +++ b/tests/test_recipeorganizer.py @@ -342,7 +342,7 @@ def test_add_restraint(self): self.assertEqual(1, len(self.m._restraints)) p2.set_value(10) self.assertEqual(1, r.penalty()) - self.m.unrestrain(r) + self.m.remove_restraint(r) self.assertEqual(0, len(self.m._restraints)) r = self.m.restrain(p1, ub=10) From 5d958fba4d45465fa96f9d983e79f745130a4748 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 9 Mar 2026 14:55:32 -0400 Subject: [PATCH 05/21] clearRestraints deprecation --- src/diffpy/srfit/fitbase/recipeorganizer.py | 27 ++++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index f9415df0..0522a80f 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -153,6 +153,13 @@ removal_version, ) +clearRestraints_deprecation_msg = build_deprecation_message( + recipeorganizer_base, + "clearRestraints", + "clear_all_restraints", + removal_version, +) + class RecipeContainer(Observable, Configurable, Validatable): """Base class for organizing pieces of a FitRecipe. @@ -1243,7 +1250,7 @@ def remove_restraint(self, *ress): Attributes ---------- - *ress : + *ress : Restraint The Restraints returned from the 'add_restraint' method or added with the 'register_restraint' method. """ @@ -1272,7 +1279,7 @@ def unrestrain(self, *ress): self.remove_restraint(*ress) return - def clearRestraints(self, recurse=False): + def clear_all_restraints(self, recurse=False): """Clear all restraints. Attributes @@ -1284,7 +1291,19 @@ def clearRestraints(self, recurse=False): self.remove_restraint(*self._restraints) if recurse: for msg in filter(_has_clear_restraints, self._iter_managed()): - msg.clearRestraints(recurse) + msg.clear_all_restraints(recurse) + return + + @deprecated(clearRestraints_deprecation_msg) + def clearRestraints(self, recurse=False): + """This function has been deprecated and will be removed in + version 4.0.0. + + Please use + diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.clear_all_restraints + instead. + """ + self.clear_all_restraints(recurse=recurse) return def _get_constraints(self, recurse=True): @@ -1528,7 +1547,7 @@ def _has_clear_constraints(msg): def _has_clear_restraints(msg): - return hasattr(msg, "clearRestraints") + return hasattr(msg, "clear_all_restraints") def _has_get_restraints(msg): From 7a2e29dc4120d57255509a823cf286c8f8195fda Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 9 Mar 2026 15:09:52 -0400 Subject: [PATCH 06/21] equationFromString deprecation --- src/diffpy/srfit/fitbase/fitcontribution.py | 8 ++- src/diffpy/srfit/fitbase/recipeorganizer.py | 73 ++++++++++++++++----- tests/test_constraint.py | 14 ++-- tests/test_recipeorganizer.py | 19 ++++-- tests/test_restraint.py | 4 +- 5 files changed, 83 insertions(+), 35 deletions(-) diff --git a/src/diffpy/srfit/fitbase/fitcontribution.py b/src/diffpy/srfit/fitbase/fitcontribution.py index d5af4709..feda75ba 100644 --- a/src/diffpy/srfit/fitbase/fitcontribution.py +++ b/src/diffpy/srfit/fitbase/fitcontribution.py @@ -28,7 +28,7 @@ from diffpy.srfit.fitbase.parameter import ParameterProxy from diffpy.srfit.fitbase.parameterset import ParameterSet from diffpy.srfit.fitbase.profile import Profile -from diffpy.srfit.fitbase.recipeorganizer import equationFromString +from diffpy.srfit.fitbase.recipeorganizer import get_equation_from_string from diffpy.utils._deprecator import build_deprecation_message, deprecated base = "diffpy.srfit.fitbase.FitContribution" @@ -296,7 +296,9 @@ def set_equation(self, eqstr, ns={}): variable. """ # Build the equation instance. - eq = equationFromString(eqstr, self._eqfactory, buildargs=True, ns=ns) + eq = get_equation_from_string( + eqstr, self._eqfactory, buildargs=True, ns=ns + ) eq.name = "eq" # Register any new Parameters. @@ -386,7 +388,7 @@ def set_residual_equation(self, eqstr): elif eqstr == "resv": eqstr = resvstr - reseq = equationFromString(eqstr, self._eqfactory) + reseq = get_equation_from_string(eqstr, self._eqfactory) self._eqfactory.wipeout(self._reseq) self._reseq = reseq diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index 0522a80f..256faa9d 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -17,10 +17,10 @@ RecipeContainer is the base class for organizing Parameters, and other RecipeContainers. RecipeOrganizer is an extended RecipeContainer that incorporates equation building, constraints and Restraints. -equationFromString creates an Equation instance from a string. +get_equation_from_string creates an Equation instance from a string. """ -__all__ = ["RecipeContainer", "RecipeOrganizer", "equationFromString"] +__all__ = ["RecipeContainer", "RecipeOrganizer", "get_equation_from_string"] import re from collections import OrderedDict @@ -160,6 +160,13 @@ removal_version, ) +equationFromString_deprecation_msg = build_deprecation_message( + recipeorganizer_base, + "equationFromString", + "get_equation_from_string", + removal_version, +) + class RecipeContainer(Observable, Configurable, Validatable): """Base class for organizing pieces of a FitRecipe. @@ -851,7 +858,7 @@ def register_string_function(self, function_str, name, func_params={}): """ # Build the equation instance. - eq = equationFromString( + eq = get_equation_from_string( function_str, self._eqfactory, ns=func_params, buildargs=True ) eq.name = name @@ -907,7 +914,9 @@ def evaluate_equation(self, equation_str, func_params={}): If `func_params` uses a name that is already used for a variable. """ - eq = equationFromString(equation_str, self._eqfactory, func_params) + eq = get_equation_from_string( + equation_str, self._eqfactory, func_params + ) try: returned_value = eq() finally: @@ -969,7 +978,9 @@ def constrain_parameter(self, parameter, constraint_eq, params={}): if isinstance(constraint_eq, str): eqstr = constraint_eq - eq = equationFromString(constraint_eq, self._eqfactory, params) + eq = get_equation_from_string( + constraint_eq, self._eqfactory, params + ) else: eq = Equation(root=constraint_eq) eqstr = constraint_eq.name @@ -1194,7 +1205,7 @@ def add_restraint( """ if isinstance(param_or_eq, str): eqstr = param_or_eq - eq = equationFromString(param_or_eq, self._eqfactory, params) + eq = get_equation_from_string(param_or_eq, self._eqfactory, params) else: eq = Equation(root=param_or_eq) eqstr = param_or_eq.name @@ -1489,38 +1500,45 @@ def show(self, pattern="", textwidth=78): # End RecipeOrganizer -def equationFromString( +def get_equation_from_string( eqstr, factory, ns={}, buildargs=False, argclass=Parameter, argkw={} ): - """Make an equation from a string. + """Make an Equation object from a string. Attributes ---------- - eqstr + eqstr : str A string representation of the equation. The equation must consist of numpy operators and "known" Parameters. Parameters are known if they are in ns, or already defined in the factory. - factory + factory : EquationFactory An EquationFactory instance. - ns - A dictionary of Parameters indexed by name that are used + ns : dict, optional + The dictionary of Parameters indexed by name that are used in the eqstr but not already defined in the factory (default {}). - buildargs + buildargs : bool, optional A flag indicating whether missing Parameters can be created by the Factory (default False). If False, then the a ValueError will be raised if there are undefined arguments in the eqstr. - argclass + argclass : Parameter class, optional Class to use when creating new Arguments (default Parameter). The class constructor must accept the 'name' key word. - argkw + argkw : dict, optional Key word dictionary to pass to the argclass constructor (default {}). + Returns + ------- + eq : Equation + An Equation instance representing the equation in eqstr. - Raises ValueError if ns uses a name that is already defined in the factory. - Raises ValueError if the equation has undefined parameters. + Raises + ------ + ValueError + If buildargs is False and there are undefined parameters in eqstr + or if ns uses a name that is already defined in the factory. """ defined = set(factory.builders.keys()) @@ -1542,6 +1560,27 @@ def equationFromString( return eq +@deprecated(equationFromString_deprecation_msg) +def equationFromString( + eqstr, factory, ns={}, buildargs=False, argclass=Parameter, argkw={} +): + """This function has been deprecated and will be removed in version + 4.0.0. + + Please use + diffpy.srfit.fitbase.recipeorganizer.get_equation_from_string + instead. + """ + return get_equation_from_string( + eqstr, + factory, + ns=ns, + buildargs=buildargs, + argclass=argclass, + argkw=argkw, + ) + + def _has_clear_constraints(msg): return hasattr(msg, "clear_all_constraints") diff --git a/tests/test_constraint.py b/tests/test_constraint.py index a72a69fa..014e8939 100644 --- a/tests/test_constraint.py +++ b/tests/test_constraint.py @@ -19,7 +19,7 @@ from diffpy.srfit.equation.builder import EquationFactory from diffpy.srfit.fitbase.constraint import Constraint from diffpy.srfit.fitbase.parameter import Parameter -from diffpy.srfit.fitbase.recipeorganizer import equationFromString +from diffpy.srfit.fitbase.recipeorganizer import get_equation_from_string class TestConstraint(unittest.TestCase): @@ -37,17 +37,17 @@ def test_constrain_parameter(self): c = Constraint() # Constrain p1 = 2*p2 - eq = equationFromString("2*p2", factory) + eq = get_equation_from_string("2*p2", factory) c.constrain_parameter(p1, eq) self.assertTrue(p1.constrained) self.assertFalse(p2.constrained) - eq2 = equationFromString("2*p2+1", factory) + eq2 = get_equation_from_string("2*p2+1", factory) c2 = Constraint() self.assertRaises(ValueError, c2.constrain, p1, eq2) p2.setConst() - eq3 = equationFromString("p1", factory) + eq3 = get_equation_from_string("p1", factory) self.assertRaises(ValueError, c2.constrain, p2, eq3) p2.set_value(2.5) @@ -76,17 +76,17 @@ def testConstraint(self): c = Constraint() # Constrain p1 = 2*p2 - eq = equationFromString("2*p2", factory) + eq = get_equation_from_string("2*p2", factory) c.constrain(p1, eq) self.assertTrue(p1.constrained) self.assertFalse(p2.constrained) - eq2 = equationFromString("2*p2+1", factory) + eq2 = get_equation_from_string("2*p2+1", factory) c2 = Constraint() self.assertRaises(ValueError, c2.constrain, p1, eq2) p2.setConst() - eq3 = equationFromString("p1", factory) + eq3 = get_equation_from_string("p1", factory) self.assertRaises(ValueError, c2.constrain, p2, eq3) p2.set_value(2.5) diff --git a/tests/test_recipeorganizer.py b/tests/test_recipeorganizer.py index 922cb33f..0302ca6e 100644 --- a/tests/test_recipeorganizer.py +++ b/tests/test_recipeorganizer.py @@ -25,6 +25,7 @@ RecipeContainer, RecipeOrganizer, equationFromString, + get_equation_from_string, ) # ---------------------------------------------------------------------------- @@ -32,8 +33,8 @@ class TestEquationFromString(unittest.TestCase): - def testEquationFromString(self): - """Test the equationFromString method.""" + def test_get_equation_from_string(self): + """Test the get_equation_from_string method.""" p1 = Parameter("p1", 1) p2 = Parameter("p2", 2) @@ -46,7 +47,7 @@ def testEquationFromString(self): factory.registerArgument("p2", p2) # Check usage where all parameters are registered with the factory - eq = equationFromString("p1+p2", factory) + eq = get_equation_from_string("p1+p2", factory) self.assertEqual(2, len(eq.args)) self.assertTrue(p1 in eq.args) @@ -54,7 +55,9 @@ def testEquationFromString(self): self.assertEqual(3, eq()) # Try to use a parameter that is not registered - self.assertRaises(ValueError, equationFromString, "p1+p2+p3", factory) + self.assertRaises( + ValueError, get_equation_from_string, "p1+p2+p3", factory + ) # Pass that argument in the ns dictionary eq = equationFromString("p1+p2+p3", factory, {"p3": p3}) @@ -69,12 +72,16 @@ def testEquationFromString(self): # Pass and use an unregistered parameter self.assertRaises( - ValueError, equationFromString, "p1+p2+p3+p4", factory, {"p3": p3} + ValueError, + get_equation_from_string, + "p1+p2+p3+p4", + factory, + {"p3": p3}, ) # Try to overload a registered parameter self.assertRaises( - ValueError, equationFromString, "p1+p2", factory, {"p2": p4} + ValueError, get_equation_from_string, "p1+p2", factory, {"p2": p4} ) return diff --git a/tests/test_restraint.py b/tests/test_restraint.py index c5ef55c7..09819531 100644 --- a/tests/test_restraint.py +++ b/tests/test_restraint.py @@ -18,7 +18,7 @@ from diffpy.srfit.equation.builder import EquationFactory from diffpy.srfit.fitbase.parameter import Parameter -from diffpy.srfit.fitbase.recipeorganizer import equationFromString +from diffpy.srfit.fitbase.recipeorganizer import get_equation_from_string from diffpy.srfit.fitbase.restraint import Restraint @@ -36,7 +36,7 @@ def testRestraint(self): factory.registerArgument("p2", p2) # Restrain 1 < p1 + p2 < 5 - eq = equationFromString("p1 + p2", factory) + eq = get_equation_from_string("p1 + p2", factory) r = Restraint(eq, 1, 5) # This should have no penalty From a5fb23c05c5e131a320a172fda0a6be3f639903a Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 9 Mar 2026 15:57:17 -0400 Subject: [PATCH 07/21] news --- news/recipeorg-dep2.rst | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 news/recipeorg-dep2.rst diff --git a/news/recipeorg-dep2.rst b/news/recipeorg-dep2.rst new file mode 100644 index 00000000..2bf0d1c5 --- /dev/null +++ b/news/recipeorg-dep2.rst @@ -0,0 +1,35 @@ +**Added:** + +* Added ``constrain_parameter`` method to ``RecipeOrganizer``. +* Added ``unconstrain_parameter`` method to ``RecipeOrganizer``. +* Added ``add_restraint`` method to ``RecipeOrganizer``. +* Added ``remove_restraint`` method to ``RecipeOrganizer``. +* Added ``register_restraint`` method to ``RecipeOrganizer``. +* Added ``clear_all_restraints`` method to ``RecipeOrganizer``. +* Added ``get_equation_from_string`` method to ``RecipeOrganizer``. + +**Changed:** + +* + +**Deprecated:** + +* Deprecated ``constrain`` method of ``RecipeOrganizer``. Use ``constrain_parameter`` instead. +* Deprecated ``unconstrain`` method of ``RecipeOrganizer``. Use ``unconstrain_parameter`` instead. +* Deprecated ``restrain`` method of ``RecipeOrganizer``. Use ``add_restraint`` instead. +* Deprecated ``unrestrain`` methods of ``RecipeOrganizer``. Use ``remove_restraint`` instead. +* Deprecated ``addRestraint`` method of ``RecipeOrganizer``. Use ``register_restraint`` instead. +* Deprecate ``clearRestraints`` method of ``RecipeOrganizer``. Use ``clear_all_restraints`` instead. +* Deprecated ``equationFromString`` method of ``RecipeOrganizer``. Use ``get_equation_from_string`` instead. + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* From a79dbec735554d7947b77788127c6bca830de985 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Thu, 12 Mar 2026 10:17:30 -0400 Subject: [PATCH 08/21] constrain_parameter --> add_constraint --- docs/examples/coreshellnp.py | 10 +++++----- docs/examples/crystalpdfall.py | 14 ++++++------- docs/examples/crystalpdftwodata.py | 4 ++-- docs/examples/crystalpdftwophase.py | 8 ++++---- docs/examples/debyemodelII.py | 4 ++-- docs/examples/npintensity.py | 6 +++--- docs/examples/npintensityII.py | 6 +++--- docs/examples/nppdfobjcryst.py | 4 ++-- docs/examples/nppdfsas.py | 6 +++--- docs/examples/simplepdftwophase.py | 4 ++-- docs/examples/threedoublepeaks.py | 18 ++++++++--------- news/recipeorg-dep2.rst | 4 ++-- src/diffpy/srfit/fitbase/constraint.py | 8 ++++---- src/diffpy/srfit/fitbase/fitrecipe.py | 10 +++++----- src/diffpy/srfit/fitbase/recipeorganizer.py | 10 +++++----- src/diffpy/srfit/pdf/pdfcontribution.py | 4 ++-- src/diffpy/srfit/structure/sgconstraints.py | 22 ++++++++++----------- tests/conftest.py | 6 +++--- tests/test_constraint.py | 2 +- tests/test_fitrecipe.py | 8 ++++---- tests/test_recipeorganizer.py | 12 +++++------ tests/test_sgconstraints.py | 6 +++--- 22 files changed, 88 insertions(+), 88 deletions(-) diff --git a/docs/examples/coreshellnp.py b/docs/examples/coreshellnp.py index 4a413a70..23871a46 100644 --- a/docs/examples/coreshellnp.py +++ b/docs/examples/coreshellnp.py @@ -91,14 +91,14 @@ def makeRecipe(stru1, stru2, datname): # diameter to twice the shell radius. recipe.add_variable(contribution.radius, 15) recipe.add_variable(contribution.thickness, 11) - recipe.constrain_parameter(contribution.psize, "2 * radius") + recipe.add_constraint(contribution.psize, "2 * radius") # Configure the fit variables # Start by configuring the scale factor and resolution factors. # We want the sum of the phase scale factors to be 1. recipe.create_new_variable("scale_CdS", 0.7) - recipe.constrain_parameter(generator_cds.scale, "scale_CdS") - recipe.constrain_parameter(generator_zns.scale, "1 - scale_CdS") + recipe.add_constraint(generator_cds.scale, "scale_CdS") + recipe.add_constraint(generator_zns.scale, "1 - scale_CdS") # We also want the resolution factor to be the same on each. # Vary the global scale as well. @@ -117,7 +117,7 @@ def makeRecipe(stru1, stru2, datname): ) # Since we know these have stacking disorder, constrain the B33 adps for # each atom type. - recipe.constrain_parameter("B33_1_cds", "B33_0_cds") + recipe.add_constraint("B33_1_cds", "B33_0_cds") recipe.add_variable(generator_cds.delta2, name="delta2_cds", value=5) phase_zns = generator_zns.phase @@ -128,7 +128,7 @@ def makeRecipe(stru1, stru2, datname): recipe.add_variable( phase_zns.sgpars.xyzpars.z_1, name="z_1_zns", tag="xyz" ) - recipe.constrain_parameter("B33_1_zns", "B33_0_zns") + recipe.add_constraint("B33_1_zns", "B33_0_zns") recipe.add_variable(generator_zns.delta2, name="delta2_zns", value=2.5) # Give the recipe away so it can be used! diff --git a/docs/examples/crystalpdfall.py b/docs/examples/crystalpdfall.py index 32e573f0..1ee8903c 100644 --- a/docs/examples/crystalpdfall.py +++ b/docs/examples/crystalpdfall.py @@ -113,15 +113,15 @@ def makeRecipe( for par in phase_ni.sgpars: recipe.add_variable(par, name=par.name + "_ni") delta2_ni = recipe.create_new_variable("delta2_ni", 2.5) - recipe.constrain_parameter(xgenerator_ni.delta2, delta2_ni) - recipe.constrain_parameter(ngenerator_ni.delta2, delta2_ni) - recipe.constrain_parameter(xgenerator_sini_ni.delta2, delta2_ni) + recipe.add_constraint(xgenerator_ni.delta2, delta2_ni) + recipe.add_constraint(ngenerator_ni.delta2, delta2_ni) + recipe.add_constraint(xgenerator_sini_ni.delta2, delta2_ni) for par in phase_si.sgpars: recipe.add_variable(par, name=par.name + "_si") delta2_si = recipe.create_new_variable("delta2_si", 2.5) - recipe.constrain_parameter(xgenerator_si.delta2, delta2_si) - recipe.constrain_parameter(xgenerator_sini_si.delta2, delta2_si) + recipe.add_constraint(xgenerator_si.delta2, delta2_si) + recipe.add_constraint(xgenerator_sini_si.delta2, delta2_si) # Now the experimental parameters recipe.add_variable(xgenerator_ni.scale, name="xscale_ni") @@ -129,8 +129,8 @@ def makeRecipe( recipe.add_variable(ngenerator_ni.scale, name="nscale_ni") recipe.add_variable(xcontribution_sini.scale, 1.0, "xscale_sini") recipe.create_new_variable("pscale_sini_ni", 0.8) - recipe.constrain_parameter(xgenerator_sini_ni.scale, "pscale_sini_ni") - recipe.constrain_parameter(xgenerator_sini_si.scale, "1 - pscale_sini_ni") + recipe.add_constraint(xgenerator_sini_ni.scale, "pscale_sini_ni") + recipe.add_constraint(xgenerator_sini_si.scale, "1 - pscale_sini_ni") # The qdamp parameters are too correlated to vary so we fix them based on # previous measurements. diff --git a/docs/examples/crystalpdftwodata.py b/docs/examples/crystalpdftwodata.py index e82374b2..0b0d6093 100644 --- a/docs/examples/crystalpdftwodata.py +++ b/docs/examples/crystalpdftwodata.py @@ -121,8 +121,8 @@ def makeRecipe(ciffile, xdatname, ndatname): # delta2 is a non-structual material property. Thus, we constrain together # delta2 Parameter from each PDFGenerator. delta2 = recipe.create_new_variable("delta2", 2) - recipe.constrain_parameter(xgenerator.delta2, delta2) - recipe.constrain_parameter(ngenerator.delta2, delta2) + recipe.add_constraint(xgenerator.delta2, delta2) + recipe.add_constraint(ngenerator.delta2, delta2) # We only need to constrain phase properties once since there is a single # ObjCrystCrystalParSet for the Crystal. diff --git a/docs/examples/crystalpdftwophase.py b/docs/examples/crystalpdftwophase.py index 3f0a0194..2071ac7a 100644 --- a/docs/examples/crystalpdftwophase.py +++ b/docs/examples/crystalpdftwophase.py @@ -90,12 +90,12 @@ def makeRecipe(niciffile, siciffile, datname): # Start by configuring the scale factor and resolution factors. # We want the sum of the phase scale factors to be 1. recipe.create_new_variable("scale_ni", 0.1) - recipe.constrain_parameter(generator_ni.scale, "scale_ni") - recipe.constrain_parameter(generator_si.scale, "1 - scale_ni") + recipe.add_constraint(generator_ni.scale, "scale_ni") + recipe.add_constraint(generator_si.scale, "1 - scale_ni") # We also want the resolution factor to be the same on each. recipe.create_new_variable("qdamp", 0.03) - recipe.constrain_parameter(generator_ni.qdamp, "qdamp") - recipe.constrain_parameter(generator_si.qdamp, "qdamp") + recipe.add_constraint(generator_ni.qdamp, "qdamp") + recipe.add_constraint(generator_si.qdamp, "qdamp") # Vary the global scale as well. recipe.add_variable(contribution.scale, 1) diff --git a/docs/examples/debyemodelII.py b/docs/examples/debyemodelII.py index 4115179f..a79df5f6 100644 --- a/docs/examples/debyemodelII.py +++ b/docs/examples/debyemodelII.py @@ -89,8 +89,8 @@ def makeRecipeII(): # We create a new Variable and use the recipe's "constrain" method to # associate the Debye temperature parameters with that variable. recipe.create_new_variable("thetaD", 100) - recipe.constrain_parameter(recipe.lowT.thetaD, "thetaD") - recipe.constrain_parameter(recipe.highT.thetaD, "thetaD") + recipe.add_constraint(recipe.lowT.thetaD, "thetaD") + recipe.add_constraint(recipe.highT.thetaD, "thetaD") return recipe diff --git a/docs/examples/npintensity.py b/docs/examples/npintensity.py index 54843d9f..eaf17012 100644 --- a/docs/examples/npintensity.py +++ b/docs/examples/npintensity.py @@ -290,15 +290,15 @@ def gaussian(q, q0, width): lattice = phase.getLattice() a = lattice.a recipe.add_variable(a) - recipe.constrain_parameter(lattice.b, a) - recipe.constrain_parameter(lattice.c, a) + recipe.add_constraint(lattice.b, a) + recipe.add_constraint(lattice.c, a) # We want to refine the thermal parameters as well. We will add a new # Variable that we call "Uiso" and constrain the atomic Uiso values to # this. Note that we don't give Uiso an initial value. The initial value # will be inferred from the following constraints. Uiso = recipe.create_new_variable("Uiso") for atom in phase.getScatterers(): - recipe.constrain_parameter(atom.Uiso, Uiso) + recipe.add_constraint(atom.Uiso, Uiso) # Give the recipe away so it can be used! return recipe diff --git a/docs/examples/npintensityII.py b/docs/examples/npintensityII.py index 69b54efc..5a6415a6 100644 --- a/docs/examples/npintensityII.py +++ b/docs/examples/npintensityII.py @@ -173,15 +173,15 @@ def gaussian(q, q0, width): a = recipe.add_variable(lattice.a) # We want to allow for isotropic expansion, so we'll make constraints for # that. - recipe.constrain_parameter(lattice.b, a) - recipe.constrain_parameter(lattice.c, a) + recipe.add_constraint(lattice.b, a) + recipe.add_constraint(lattice.c, a) # We want to refine the thermal parameters as well. We will add a new # variable that we call "Uiso" and constrain the atomic Uiso values to # this. Note that we don't give Uiso an initial value. The initial value # will be inferred from the subsequent constraints. Uiso = recipe.create_new_variable("Uiso") for atom in phase.getScatterers(): - recipe.constrain_parameter(atom.Uiso, Uiso) + recipe.add_constraint(atom.Uiso, Uiso) # Give the recipe away so it can be used! return recipe diff --git a/docs/examples/nppdfobjcryst.py b/docs/examples/nppdfobjcryst.py index 33a387ab..03511d19 100644 --- a/docs/examples/nppdfobjcryst.py +++ b/docs/examples/nppdfobjcryst.py @@ -73,7 +73,7 @@ def makeRecipe(molecule, datname): # has no scattering power. It is only used as a reference point for # our bond length. We don't want to constrain it. if not atom.isDummy(): - recipe.constrain_parameter(atom.Biso, Biso) + recipe.add_constraint(atom.Biso, Biso) # We need to let the molecule expand. If we were modeling it as a crystal, # we could let the unit cell expand. For instruction purposes, we use a @@ -97,7 +97,7 @@ def makeRecipe(molecule, datname): # This creates a Parameter that moves the second atom according to the # bond length. Note that each Parameter needs a unique name. par = c60.addBondLengthParameter("rad%i" % i, center, atom) - recipe.constrain_parameter(par, radius) + recipe.add_constraint(par, radius) # Add the correlation term, scale. The scale is too short to effectively # determine qdamp. diff --git a/docs/examples/nppdfsas.py b/docs/examples/nppdfsas.py index adaae5bc..c2cdbe84 100644 --- a/docs/examples/nppdfsas.py +++ b/docs/examples/nppdfsas.py @@ -113,8 +113,8 @@ def makeRecipe(ciffile, grdata, iqdata): # Even though the cfcalculator and sasgenerator depend on the same sas # model, we must still constrain the cfcalculator Parameters so that it is # informed of changes in the refined parameters. - recipe.constrain_parameter(cfcalculator.radius_a, "radius_a") - recipe.constrain_parameter(cfcalculator.radius_b, "radius_b") + recipe.add_constraint(cfcalculator.radius_a, "radius_a") + recipe.add_constraint(cfcalculator.radius_b, "radius_b") return recipe @@ -126,7 +126,7 @@ def fitRecipe(recipe): recipe.set_weight(recipe.pdf, 0) recipe.fix("all") recipe.free("radius_a", "radius_b", iqscale=1e8) - recipe.constrain_parameter("radius_b", "radius_a") + recipe.add_constraint("radius_b", "radius_a") scipyOptimize(recipe) recipe.unconstrain_parameter("radius_b") diff --git a/docs/examples/simplepdftwophase.py b/docs/examples/simplepdftwophase.py index e0735514..17ff79a2 100644 --- a/docs/examples/simplepdftwophase.py +++ b/docs/examples/simplepdftwophase.py @@ -47,8 +47,8 @@ def makeRecipe(niciffile, siciffile, datname): # Start by configuring the scale factor and resolution factors. # We want the sum of the phase scale factors to be 1. recipe.create_new_variable("scale_ni", 0.1) - recipe.constrain_parameter(contribution.ni.scale, "scale_ni") - recipe.constrain_parameter(contribution.si.scale, "1 - scale_ni") + recipe.add_constraint(contribution.ni.scale, "scale_ni") + recipe.add_constraint(contribution.si.scale, "1 - scale_ni") # We also want the resolution factor to be the same on each. This is done # for free by the PDFContribution. We simply need to add it to the recipe. recipe.add_variable(contribution.qdamp, 0.03) diff --git a/docs/examples/threedoublepeaks.py b/docs/examples/threedoublepeaks.py index 17d23ed1..ecf01bf4 100644 --- a/docs/examples/threedoublepeaks.py +++ b/docs/examples/threedoublepeaks.py @@ -121,9 +121,9 @@ def peakloc(mu): return 180 / pi * arcsin(pi / 180 * l2 * sin(mu) / l1) recipe.register_function(peakloc) - recipe.constrain_parameter(contribution.mu12, "peakloc(mu11)") - recipe.constrain_parameter(contribution.mu22, "peakloc(mu21)") - recipe.constrain_parameter(contribution.mu32, "peakloc(mu31)") + recipe.add_constraint(contribution.mu12, "peakloc(mu11)") + recipe.add_constraint(contribution.mu22, "peakloc(mu21)") + recipe.add_constraint(contribution.mu32, "peakloc(mu31)") # Vary the width of the peaks. We know the functional form of the peak # broadening. @@ -139,20 +139,20 @@ def sig(sig0, dsig, mu): # Now constrain the peak widths to this recipe.sig0.value = 0.001 recipe.dsig.value = 4.0 - recipe.constrain_parameter(contribution.sig11, "sig(sig0, dsig, mu11)") - recipe.constrain_parameter( + recipe.add_constraint(contribution.sig11, "sig(sig0, dsig, mu11)") + recipe.add_constraint( contribution.sig12, "sig(sig0, dsig, mu12)", ns={"mu12": contribution.mu12}, ) - recipe.constrain_parameter(contribution.sig21, "sig(sig0, dsig, mu21)") - recipe.constrain_parameter( + recipe.add_constraint(contribution.sig21, "sig(sig0, dsig, mu21)") + recipe.add_constraint( contribution.sig22, "sig(sig0, dsig, mu22)", ns={"mu22": contribution.mu22}, ) - recipe.constrain_parameter(contribution.sig31, "sig(sig0, dsig, mu31)") - recipe.constrain_parameter( + recipe.add_constraint(contribution.sig31, "sig(sig0, dsig, mu31)") + recipe.add_constraint( contribution.sig32, "sig(sig0, dsig, mu32)", ns={"mu32": contribution.mu32}, diff --git a/news/recipeorg-dep2.rst b/news/recipeorg-dep2.rst index 2bf0d1c5..b2236f9e 100644 --- a/news/recipeorg-dep2.rst +++ b/news/recipeorg-dep2.rst @@ -1,6 +1,6 @@ **Added:** -* Added ``constrain_parameter`` method to ``RecipeOrganizer``. +* Added ``add_constraint`` method to ``RecipeOrganizer``. * Added ``unconstrain_parameter`` method to ``RecipeOrganizer``. * Added ``add_restraint`` method to ``RecipeOrganizer``. * Added ``remove_restraint`` method to ``RecipeOrganizer``. @@ -14,7 +14,7 @@ **Deprecated:** -* Deprecated ``constrain`` method of ``RecipeOrganizer``. Use ``constrain_parameter`` instead. +* Deprecated ``constrain`` method of ``RecipeOrganizer``. Use ``add_constraint`` instead. * Deprecated ``unconstrain`` method of ``RecipeOrganizer``. Use ``unconstrain_parameter`` instead. * Deprecated ``restrain`` method of ``RecipeOrganizer``. Use ``add_restraint`` instead. * Deprecated ``unrestrain`` methods of ``RecipeOrganizer``. Use ``remove_restraint`` instead. diff --git a/src/diffpy/srfit/fitbase/constraint.py b/src/diffpy/srfit/fitbase/constraint.py index c24eaf35..dba135b4 100644 --- a/src/diffpy/srfit/fitbase/constraint.py +++ b/src/diffpy/srfit/fitbase/constraint.py @@ -32,7 +32,7 @@ constrain_deprecation_msg = build_deprecation_message( base, "constrain", - "constrain_parameter", + "add_constraint", removal_version, ) @@ -65,7 +65,7 @@ def __init__(self): self.eq = None return - def constrain_parameter(self, par, eq): + def add_constraint(self, par, eq): """Constrain a Parameter according to an Equation. The parameter will be set constant once it is constrained. This @@ -103,10 +103,10 @@ def constrain(self, par, eq): version 4.0.0. Please use - diffpy.srfit.fitbase.constraint.Constraint.constrain_parameter + diffpy.srfit.fitbase.constraint.Constraint.add_constraint instead. """ - self.constrain_parameter(par, eq) + self.add_constraint(par, eq) return def unconstrain_parameter(self): diff --git a/src/diffpy/srfit/fitbase/fitrecipe.py b/src/diffpy/srfit/fitbase/fitrecipe.py index 1dc61fbc..12ac4428 100644 --- a/src/diffpy/srfit/fitbase/fitrecipe.py +++ b/src/diffpy/srfit/fitbase/fitrecipe.py @@ -126,7 +126,7 @@ ) constrain_dep_msg = build_deprecation_message( - base, "constrain", "constrain_parameter", removal_version + base, "constrain", "add_constraint", removal_version ) unconstrain_dep_msg = build_deprecation_message( @@ -1157,7 +1157,7 @@ def unconstrain(self, *pars): self.unconstrain_parameter(*pars) return - def constrain_parameter(self, par, con, ns={}): + def add_constraint(self, par, con, ns={}): """Constrain a parameter to an equation. Note that only one constraint can exist on a Parameter at a time. @@ -1215,7 +1215,7 @@ def constrain_parameter(self, par, con, ns={}): if par in self._parameters.values(): self.fix(par) - RecipeOrganizer.constrain_parameter(self, par, con, ns) + RecipeOrganizer.add_constraint(self, par, con, ns) return @deprecated(constrain_dep_msg) @@ -1223,10 +1223,10 @@ def constrain(self, par, con, ns={}): """This function has been deprecated and will be removed in version 4.0.0. - Please use diffpy.srfit.fitbase.FitRecipe.constrain_parameter + Please use diffpy.srfit.fitbase.FitRecipe.add_constraint instead. """ - self.constrain_parameter(par, con, ns) + self.add_constraint(par, con, ns) return def get_values(self): diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index 256faa9d..333a9a3c 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -128,7 +128,7 @@ constrain_deprecation_msg = build_deprecation_message( recipeorganizer_base, "constrain", - "constrain_parameter", + "add_constraint", removal_version, ) @@ -934,7 +934,7 @@ def evaluateEquation(self, eqstr, ns={}): """ return self.evaluate_equation(eqstr, func_params=ns) - def constrain_parameter(self, parameter, constraint_eq, params={}): + def add_constraint(self, parameter, constraint_eq, params={}): """Constrain a parameter to an equation. Note that only one constraint can exist on a Parameter at a time. @@ -989,7 +989,7 @@ def constrain_parameter(self, parameter, constraint_eq, params={}): # Make and store the constraint constraint_eq = Constraint() - constraint_eq.constrain_parameter(parameter, eq) + constraint_eq.add_constraint(parameter, eq) # Store the equation string so it can be shown later. constraint_eq.eqstr = eqstr self._constraints[parameter] = constraint_eq @@ -1005,10 +1005,10 @@ def constrain(self, parameter, constraint_eq, params={}): version 4.0.0. Please use - diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.constrain_parameter + diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.add_constraint instead. """ - self.constrain_parameter(parameter, constraint_eq, params=params) + self.add_constraint(parameter, constraint_eq, params=params) return def is_constrained(self, parameter): diff --git a/src/diffpy/srfit/pdf/pdfcontribution.py b/src/diffpy/srfit/pdf/pdfcontribution.py index 8ef1578b..07d33c5d 100644 --- a/src/diffpy/srfit/pdf/pdfcontribution.py +++ b/src/diffpy/srfit/pdf/pdfcontribution.py @@ -287,8 +287,8 @@ def _setup_generator(self, gen): gen.processMetaData() # Constrain the shared parameters - self.constrain_parameter(gen.qdamp, self.qdamp) - self.constrain_parameter(gen.qbroad, self.qbroad) + self.add_constraint(gen.qdamp, self.qdamp) + self.add_constraint(gen.qbroad, self.qbroad) return # Calculation setup methods diff --git a/src/diffpy/srfit/structure/sgconstraints.py b/src/diffpy/srfit/structure/sgconstraints.py index 681bad1b..19b6d187 100644 --- a/src/diffpy/srfit/structure/sgconstraints.py +++ b/src/diffpy/srfit/structure/sgconstraints.py @@ -596,7 +596,7 @@ def _constrain_adps(self, positions): continue isoidx.append(j) scatterer = scatterers[j] - scatterer.constrain_parameter( + scatterer.add_constraint( isosymbol, isoname, params=self._parameters ) @@ -691,7 +691,7 @@ def _constrain_tetragonal(lattice): lattice.alpha.setConst(True, ang90) lattice.beta.setConst(True, ang90) lattice.gamma.setConst(True, ang90) - lattice.constrain_parameter(lattice.b, lattice.a) + lattice.add_constraint(lattice.b, lattice.a) return @@ -708,15 +708,15 @@ def _constrain_trigonal(lattice): ang90 = 90.0 * afactor ang120 = 120.0 * afactor if lattice.gamma.getValue() == ang120: - lattice.constrain_parameter(lattice.b, lattice.a) + lattice.add_constraint(lattice.b, lattice.a) lattice.alpha.setConst(True, ang90) lattice.beta.setConst(True, ang90) lattice.gamma.setConst(True, ang120) else: - lattice.constrain_parameter(lattice.b, lattice.a) - lattice.constrain_parameter(lattice.c, lattice.a) - lattice.constrain_parameter(lattice.beta, lattice.alpha) - lattice.constrain_parameter(lattice.gamma, lattice.alpha) + lattice.add_constraint(lattice.b, lattice.a) + lattice.add_constraint(lattice.c, lattice.a) + lattice.add_constraint(lattice.beta, lattice.alpha) + lattice.add_constraint(lattice.gamma, lattice.alpha) return @@ -731,7 +731,7 @@ def _constrain_hexagonal(lattice): afactor = deg2rad ang90 = 90.0 * afactor ang120 = 120.0 * afactor - lattice.constrain_parameter(lattice.b, lattice.a) + lattice.add_constraint(lattice.b, lattice.a) lattice.alpha.setConst(True, ang90) lattice.beta.setConst(True, ang90) lattice.gamma.setConst(True, ang120) @@ -748,8 +748,8 @@ def _constrain_cubic(lattice): if lattice.angunits == "rad": afactor = deg2rad ang90 = 90.0 * afactor - lattice.constrain_parameter(lattice.b, lattice.a) - lattice.constrain_parameter(lattice.c, lattice.a) + lattice.add_constraint(lattice.b, lattice.a) + lattice.add_constraint(lattice.c, lattice.a) lattice.alpha.setConst(True, ang90) lattice.beta.setConst(True, ang90) lattice.gamma.setConst(True, ang90) @@ -811,7 +811,7 @@ def _makeconstraint(parname, formula, scatterer, idx, ns={}): # If we got here, then we have a constraint equation # Fix any division issues formula = formula.replace("/", "*1.0/") - scatterer.constrain_parameter(par, formula, params=ns) + scatterer.add_constraint(par, formula, params=ns) return diff --git a/tests/conftest.py b/tests/conftest.py index 118f30d4..8dc38c9a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -215,9 +215,9 @@ def build_recipe_two_contributions(): recipe.add_variable(contribution2.d, 0.1) # ---- Meaningful constraints ---- - recipe.constrain_parameter(contribution2.m, "2*k") - recipe.constrain_parameter(contribution2.d, contribution1.c) - recipe.constrain_parameter(contribution2.B, "0.5*A") + recipe.add_constraint(contribution2.m, "2*k") + recipe.add_constraint(contribution2.d, contribution1.c) + recipe.add_constraint(contribution2.B, "0.5*A") recipe.add_restraint(contribution1.A, 0.5, 1.5) recipe.add_restraint(contribution1.k, 0.8, 1.2) diff --git a/tests/test_constraint.py b/tests/test_constraint.py index 014e8939..d045feb5 100644 --- a/tests/test_constraint.py +++ b/tests/test_constraint.py @@ -38,7 +38,7 @@ def test_constrain_parameter(self): c = Constraint() # Constrain p1 = 2*p2 eq = get_equation_from_string("2*p2", factory) - c.constrain_parameter(p1, eq) + c.add_constraint(p1, eq) self.assertTrue(p1.constrained) self.assertFalse(p2.constrained) diff --git a/tests/test_fitrecipe.py b/tests/test_fitrecipe.py index 3e165268..75066f65 100644 --- a/tests/test_fitrecipe.py +++ b/tests/test_fitrecipe.py @@ -119,7 +119,7 @@ def test_variables(self): # Constrain a parameter to the B-variable to give it a value p = Parameter("Bpar", -1) - recipe.constrain_parameter(recipe.B, p) + recipe.add_constraint(recipe.B, p) values = recipe.get_values() self.assertTrue((values == [2, 1, 0]).all()) recipe.delete_variable(recipe.B) @@ -215,11 +215,11 @@ def testResidual(self): # Try some constraints # Make c = 2*A, A = Avar var = self.recipe.create_new_variable("Avar") - self.recipe.constrain_parameter( + self.recipe.add_constraint( self.fitcontribution.c, "2*A", {"A": self.fitcontribution.A} ) self.assertEqual(2, self.fitcontribution.c.value) - self.recipe.constrain_parameter(self.fitcontribution.A, var) + self.recipe.add_constraint(self.fitcontribution.A, var) self.assertEqual(1, var.getValue()) self.assertEqual(self.recipe.cont.A.getValue(), var.getValue()) # c is constrained to a constrained parameter. @@ -255,7 +255,7 @@ def testResidual(self): self.assertAlmostEqual(chi2, dot(res, res)) # Add constraints at the fitcontribution level. - self.fitcontribution.constrain_parameter(self.fitcontribution.c, "2*A") + self.fitcontribution.add_constraint(self.fitcontribution.c, "2*A") # This should evaluate to sin(x+2) x = self.profile.x y = sin(x + 2) diff --git a/tests/test_recipeorganizer.py b/tests/test_recipeorganizer.py index 0302ca6e..4af757e0 100644 --- a/tests/test_recipeorganizer.py +++ b/tests/test_recipeorganizer.py @@ -275,7 +275,7 @@ def test_constrain_parameter(self): self.assertFalse(p1.constrained) self.assertEqual(0, len(self.m._constraints)) - self.m.constrain_parameter(p1, "2*p2") + self.m.add_constraint(p1, "2*p2") actual_constrained_params = self.m.getConstrainedPars() actual_constrained_params = [p.name for p in actual_constrained_params] @@ -309,7 +309,7 @@ def test_constrain_parameter(self): self.assertFalse(self.m.isConstrained(p1)) # Try an straight constraint - self.m.constrain_parameter(p1, p2) + self.m.add_constraint(p1, p2) p2.set_value(7) self.m._constraints[p1].update() self.assertEqual(7, p1.getValue()) @@ -321,7 +321,7 @@ def test_constrain_parameter(self): assert actual_constrained_params == expected_constrained_params # add constraint back and test the old function name `clearConstraints` - self.m.constrain_parameter(p1, p2) + self.m.add_constraint(p1, p2) actual_constrained_params = self.m.get_constrained_parmeters() actual_constrained_params = [p.name for p in actual_constrained_params] expected_constrained_params = [p1.name] @@ -382,8 +382,8 @@ def testGetConstraints(self): m2._add_parameter(p3) m2._add_parameter(p4) - self.m.constrain_parameter(p1, "p2") - m2.constrain_parameter(p3, "p4") + self.m.add_constraint(p1, "p2") + m2.add_constraint(p3, "p4") cons = self.m._get_constraints() self.assertTrue(p1 in cons) @@ -632,7 +632,7 @@ def capture_show(*args, **kwargs): assert "Constraints" not in lines1 assert "Restraints" not in lines1 organizer._new_parameter("z", 7) - organizer.constrain_parameter("y", "3 * z") + organizer.add_constraint("y", "3 * z") out2 = capture_show() lines2 = out2.strip().split("\n") assert 9 == len(lines2) diff --git a/tests/test_sgconstraints.py b/tests/test_sgconstraints.py index 19bb49f2..211d61be 100644 --- a/tests/test_sgconstraints.py +++ b/tests/test_sgconstraints.py @@ -87,13 +87,13 @@ def test_ObjCryst_constrain_space_group(pyobjcryst_available): # Make sure we can't constrain these with pytest.raises(ValueError): - mn.constrain_parameter(mn.x, "y") + mn.add_constraint(mn.x, "y") with pytest.raises(ValueError): - mn.constrain_parameter(mn.y, "z") + mn.add_constraint(mn.y, "z") with pytest.raises(ValueError): - mn.constrain_parameter(mn.z, "x") + mn.add_constraint(mn.z, "x") # Nor can we make them into variables from diffpy.srfit.fitbase.fitrecipe import FitRecipe From 80173b60520bff15d3d45322893161194e7a043a Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Thu, 12 Mar 2026 10:18:20 -0400 Subject: [PATCH 09/21] unconstrain_parameter --> remove_constraint --- docs/examples/nppdfsas.py | 2 +- news/recipeorg-dep2.rst | 4 ++-- src/diffpy/srfit/fitbase/constraint.py | 8 ++++---- src/diffpy/srfit/fitbase/fitrecipe.py | 10 +++++----- src/diffpy/srfit/fitbase/recipeorganizer.py | 12 ++++++------ src/diffpy/srfit/structure/sgconstraints.py | 8 ++++---- tests/test_fitrecipe.py | 2 +- tests/test_recipeorganizer.py | 2 +- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/docs/examples/nppdfsas.py b/docs/examples/nppdfsas.py index c2cdbe84..7bea8069 100644 --- a/docs/examples/nppdfsas.py +++ b/docs/examples/nppdfsas.py @@ -128,7 +128,7 @@ def fitRecipe(recipe): recipe.free("radius_a", "radius_b", iqscale=1e8) recipe.add_constraint("radius_b", "radius_a") scipyOptimize(recipe) - recipe.unconstrain_parameter("radius_b") + recipe.remove_constraint("radius_b") # Tune PDF recipe.set_weight(recipe.pdf, 1) diff --git a/news/recipeorg-dep2.rst b/news/recipeorg-dep2.rst index b2236f9e..28018154 100644 --- a/news/recipeorg-dep2.rst +++ b/news/recipeorg-dep2.rst @@ -1,7 +1,7 @@ **Added:** * Added ``add_constraint`` method to ``RecipeOrganizer``. -* Added ``unconstrain_parameter`` method to ``RecipeOrganizer``. +* Added ``remove_constraint`` method to ``RecipeOrganizer``. * Added ``add_restraint`` method to ``RecipeOrganizer``. * Added ``remove_restraint`` method to ``RecipeOrganizer``. * Added ``register_restraint`` method to ``RecipeOrganizer``. @@ -15,7 +15,7 @@ **Deprecated:** * Deprecated ``constrain`` method of ``RecipeOrganizer``. Use ``add_constraint`` instead. -* Deprecated ``unconstrain`` method of ``RecipeOrganizer``. Use ``unconstrain_parameter`` instead. +* Deprecated ``unconstrain`` method of ``RecipeOrganizer``. Use ``remove_constraint`` instead. * Deprecated ``restrain`` method of ``RecipeOrganizer``. Use ``add_restraint`` instead. * Deprecated ``unrestrain`` methods of ``RecipeOrganizer``. Use ``remove_restraint`` instead. * Deprecated ``addRestraint`` method of ``RecipeOrganizer``. Use ``register_restraint`` instead. diff --git a/src/diffpy/srfit/fitbase/constraint.py b/src/diffpy/srfit/fitbase/constraint.py index dba135b4..dab6002b 100644 --- a/src/diffpy/srfit/fitbase/constraint.py +++ b/src/diffpy/srfit/fitbase/constraint.py @@ -39,7 +39,7 @@ unconstrain_deprecation_msg = build_deprecation_message( base, "unconstrain", - "unconstrain_parameter", + "remove_constraint", removal_version, ) @@ -109,7 +109,7 @@ def constrain(self, par, eq): self.add_constraint(par, eq) return - def unconstrain_parameter(self): + def remove_constraint(self): """Clear the constraint from a Parameter.""" self.par.constrained = False self.par = None @@ -122,10 +122,10 @@ def unconstrain(self): version 4.0.0. Please use - diffpy.srfit.fitbase.constraint.Constraint.unconstrain_parameter + diffpy.srfit.fitbase.constraint.Constraint.remove_constraint instead. """ - self.unconstrain_parameter() + self.remove_constraint() return def update(self): diff --git a/src/diffpy/srfit/fitbase/fitrecipe.py b/src/diffpy/srfit/fitbase/fitrecipe.py index 12ac4428..416133cb 100644 --- a/src/diffpy/srfit/fitbase/fitrecipe.py +++ b/src/diffpy/srfit/fitbase/fitrecipe.py @@ -130,7 +130,7 @@ ) unconstrain_dep_msg = build_deprecation_message( - base, "unconstrain", "unconstrain_parameter", removal_version + base, "unconstrain", "remove_constraint", removal_version ) @@ -1107,7 +1107,7 @@ def isFree(self, var): """ return self.is_free(var) - def unconstrain_parameter(self, *pars): + def remove_constraint(self, *pars): """Unconstrain a Parameter. This removes any constraints on a Parameter. If the Parameter is also a @@ -1133,7 +1133,7 @@ def unconstrain_parameter(self, *pars): raise ValueError("The parameter cannot be found") if par in self._constraints: - self._constraints[par].unconstrain_parameter() + self._constraints[par].remove_constraint() del self._constraints[par] update = True @@ -1151,10 +1151,10 @@ def unconstrain(self, *pars): """This function has been deprecated and will be removed in version 4.0.0. - Please use diffpy.srfit.fitbase.FitRecipe.unconstrain_parameter + Please use diffpy.srfit.fitbase.FitRecipe.remove_constraint instead. """ - self.unconstrain_parameter(*pars) + self.remove_constraint(*pars) return def add_constraint(self, par, con, ns={}): diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index 333a9a3c..b3a92bfe 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -135,7 +135,7 @@ unconstrain_deprecation_msg = build_deprecation_message( recipeorganizer_base, "unconstrain", - "unconstrain_parameter", + "remove_constraint", removal_version, ) @@ -1042,7 +1042,7 @@ def isConstrained(self, parameter): """ return self.is_constrained(parameter) - def unconstrain_parameter(self, *pars): + def remove_constraint(self, *pars): """Unconstrain a Parameter. This removes any constraints on a Parameter. @@ -1065,7 +1065,7 @@ def unconstrain_parameter(self, *pars): raise ValueError("The parameter cannot be found") if parameter in self._constraints: - self._constraints[parameter].unconstrain_parameter() + self._constraints[parameter].remove_constraint() del self._constraints[parameter] update = True @@ -1085,10 +1085,10 @@ def unconstrain(self, *pars): version 4.0.0. Please use - diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.unconstrain_parameter + diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.remove_constraint instead. """ - self.unconstrain_parameter(*pars) + self.remove_constraint(*pars) return def get_constrained_parmeters(self, recurse=False): @@ -1136,7 +1136,7 @@ def clear_all_constraints(self, recurse=False): sub-objects are also cleared. """ if self._constraints: - self.unconstrain_parameter(*self._constraints) + self.remove_constraint(*self._constraints) if recurse: for m in filter(_has_clear_constraints, self._iter_managed()): diff --git a/src/diffpy/srfit/structure/sgconstraints.py b/src/diffpy/srfit/structure/sgconstraints.py index 19b6d187..2b48ec1d 100644 --- a/src/diffpy/srfit/structure/sgconstraints.py +++ b/src/diffpy/srfit/structure/sgconstraints.py @@ -391,7 +391,7 @@ def _clear_constraints(self): for par in [scatterer.x, scatterer.y, scatterer.z]: if scatterer.is_constrained(par): - scatterer.unconstrain_parameter(par) + scatterer.remove_constraint(par) par.setConst(False) # Clear the lattice @@ -408,7 +408,7 @@ def _clear_constraints(self): ] for par in latpars: if lattice.is_constrained(par): - lattice.unconstrain_parameter(par) + lattice.remove_constraint(par) par.setConst(False) # Clear ADPs @@ -418,14 +418,14 @@ def _clear_constraints(self): par = scatterer.get(isosymbol) if par is not None: if scatterer.is_constrained(par): - scatterer.unconstrain_parameter(par) + scatterer.remove_constraint(par) par.setConst(False) for pname in adpsymbols: par = scatterer.get(pname) if par is not None: if scatterer.is_constrained(par): - scatterer.unconstrain_parameter(par) + scatterer.remove_constraint(par) par.setConst(False) return diff --git a/tests/test_fitrecipe.py b/tests/test_fitrecipe.py index 75066f65..885a34a7 100644 --- a/tests/test_fitrecipe.py +++ b/tests/test_fitrecipe.py @@ -240,7 +240,7 @@ def testResidual(self): # Clear the constraint and restore the value of c to 0. This should # give us chi2 = 0 again. - self.recipe.unconstrain_parameter(self.fitcontribution.c) + self.recipe.remove_constraint(self.fitcontribution.c) self.fitcontribution.c.set_value(0) res = self.recipe.residual([self.recipe.cont.A.getValue()]) chi2 = 0 diff --git a/tests/test_recipeorganizer.py b/tests/test_recipeorganizer.py index 4af757e0..66cff9b4 100644 --- a/tests/test_recipeorganizer.py +++ b/tests/test_recipeorganizer.py @@ -303,7 +303,7 @@ def test_constrain_parameter(self): self.assertRaises(ValueError, self.m.constrain, p1, "2*p2", {"p2": p3}) # Remove the constraint - self.m.unconstrain_parameter(p1) + self.m.remove_constraint(p1) self.assertFalse(p1.constrained) self.assertEqual(0, len(self.m._constraints)) self.assertFalse(self.m.isConstrained(p1)) From 31a57fc22905151629d5feb63bf7bfccbf4dd56f Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Thu, 12 Mar 2026 10:19:36 -0400 Subject: [PATCH 10/21] add_restraint --> add_penalty --- docs/examples/crystalpdftwophase.py | 12 ++++++------ docs/examples/debyemodel.py | 2 +- docs/examples/simplepdftwophase.py | 12 ++++++------ news/recipeorg-dep2.rst | 4 ++-- src/diffpy/srfit/fitbase/fitrecipe.py | 2 +- src/diffpy/srfit/fitbase/recipeorganizer.py | 10 +++++----- tests/conftest.py | 4 ++-- tests/test_fitrecipe.py | 10 ++++------ tests/test_recipeorganizer.py | 8 ++++---- 9 files changed, 31 insertions(+), 33 deletions(-) diff --git a/docs/examples/crystalpdftwophase.py b/docs/examples/crystalpdftwophase.py index 2071ac7a..c7354211 100644 --- a/docs/examples/crystalpdftwophase.py +++ b/docs/examples/crystalpdftwophase.py @@ -123,18 +123,18 @@ def makeRecipe(niciffile, siciffile, datname): # derived has no uncertainty. Thus, we will tell the recipe to scale the # residual, which means that it will be weighted as much as the average # data point during the fit. - recipe.add_restraint("a_ni", lb=3.527, ub=3.527, scaled=True) + recipe.add_penalty("a_ni", lb=3.527, ub=3.527, scaled=True) # Now we do the same with the delta2 and Biso parameters (remember that # Biso = 8*pi**2*Uiso) - recipe.add_restraint("delta2_ni", lb=2.22, ub=2.22, scaled=True) - recipe.add_restraint("Biso_0_ni", lb=0.454, ub=0.454, scaled=True) + recipe.add_penalty("delta2_ni", lb=2.22, ub=2.22, scaled=True) + recipe.add_penalty("Biso_0_ni", lb=0.454, ub=0.454, scaled=True) # # We can do the same with the silicon values. We haven't done a thorough # job of measuring the uncertainties in the results, so we'll scale these # as well. - recipe.add_restraint("a_si", lb=5.430, ub=5.430, scaled=True) - recipe.add_restraint("delta2_si", lb=3.54, ub=3.54, scaled=True) - recipe.add_restraint("Biso_0_si", lb=0.645, ub=0.645, scaled=True) + recipe.add_penalty("a_si", lb=5.430, ub=5.430, scaled=True) + recipe.add_penalty("delta2_si", lb=3.54, ub=3.54, scaled=True) + recipe.add_penalty("Biso_0_si", lb=0.645, ub=0.645, scaled=True) # Give the recipe away so it can be used! return recipe diff --git a/docs/examples/debyemodel.py b/docs/examples/debyemodel.py index c49c13b0..a373f307 100644 --- a/docs/examples/debyemodel.py +++ b/docs/examples/debyemodel.py @@ -152,7 +152,7 @@ def makeRecipe(): # breaking the restraint by the point-average chi^2 value so that the # restraint is roughly as significant as any other data point throughout # the fit. - recipe.add_restraint(recipe.offset, lb=0, scaled=True) + recipe.add_penalty(recipe.offset, lb=0, scaled=True) # We're done setting up the recipe. We can now do other things with it. return recipe diff --git a/docs/examples/simplepdftwophase.py b/docs/examples/simplepdftwophase.py index 17ff79a2..ec496542 100644 --- a/docs/examples/simplepdftwophase.py +++ b/docs/examples/simplepdftwophase.py @@ -82,18 +82,18 @@ def makeRecipe(niciffile, siciffile, datname): # derived has no uncertainty. Thus, we will tell the recipe to scale the # residual, which means that it will be weighted as much as the average # data point during the fit. - recipe.add_restraint("a_ni", lb=3.527, ub=3.527, scaled=True) + recipe.add_penalty("a_ni", lb=3.527, ub=3.527, scaled=True) # Now we do the same with the delta2 and Biso parameters (remember that # Biso = 8*pi**2*Uiso) - recipe.add_restraint("delta2_ni", lb=2.22, ub=2.22, scaled=True) - recipe.add_restraint("Biso_0_ni", lb=0.454, ub=0.454, scaled=True) + recipe.add_penalty("delta2_ni", lb=2.22, ub=2.22, scaled=True) + recipe.add_penalty("Biso_0_ni", lb=0.454, ub=0.454, scaled=True) # # We can do the same with the silicon values. We haven't done a thorough # job of measuring the uncertainties in the results, so we'll scale these # as well. - recipe.add_restraint("a_si", lb=5.430, ub=5.430, scaled=True) - recipe.add_restraint("delta2_si", lb=3.54, ub=3.54, scaled=True) - recipe.add_restraint("Biso_0_si", lb=0.645, ub=0.645, scaled=True) + recipe.add_penalty("a_si", lb=5.430, ub=5.430, scaled=True) + recipe.add_penalty("delta2_si", lb=3.54, ub=3.54, scaled=True) + recipe.add_penalty("Biso_0_si", lb=0.645, ub=0.645, scaled=True) # Give the recipe away so it can be used! return recipe diff --git a/news/recipeorg-dep2.rst b/news/recipeorg-dep2.rst index 28018154..2c707f1d 100644 --- a/news/recipeorg-dep2.rst +++ b/news/recipeorg-dep2.rst @@ -2,7 +2,7 @@ * Added ``add_constraint`` method to ``RecipeOrganizer``. * Added ``remove_constraint`` method to ``RecipeOrganizer``. -* Added ``add_restraint`` method to ``RecipeOrganizer``. +* Added ``add_penalty`` method to ``RecipeOrganizer``. * Added ``remove_restraint`` method to ``RecipeOrganizer``. * Added ``register_restraint`` method to ``RecipeOrganizer``. * Added ``clear_all_restraints`` method to ``RecipeOrganizer``. @@ -16,7 +16,7 @@ * Deprecated ``constrain`` method of ``RecipeOrganizer``. Use ``add_constraint`` instead. * Deprecated ``unconstrain`` method of ``RecipeOrganizer``. Use ``remove_constraint`` instead. -* Deprecated ``restrain`` method of ``RecipeOrganizer``. Use ``add_restraint`` instead. +* Deprecated ``restrain`` method of ``RecipeOrganizer``. Use ``add_penalty`` instead. * Deprecated ``unrestrain`` methods of ``RecipeOrganizer``. Use ``remove_restraint`` instead. * Deprecated ``addRestraint`` method of ``RecipeOrganizer``. Use ``register_restraint`` instead. * Deprecate ``clearRestraints`` method of ``RecipeOrganizer``. Use ``clear_all_restraints`` instead. diff --git a/src/diffpy/srfit/fitbase/fitrecipe.py b/src/diffpy/srfit/fitbase/fitrecipe.py index 416133cb..7740a20e 100644 --- a/src/diffpy/srfit/fitbase/fitrecipe.py +++ b/src/diffpy/srfit/fitbase/fitrecipe.py @@ -1756,7 +1756,7 @@ def convert_bounds_to_restraints(self, sig=1, scaled=False): if not hasattr(sig, "__iter__"): sig = [sig] * len(pars) for par, x in zip(pars, sig): - self.add_restraint( + self.add_penalty( par, par.bounds[0], par.bounds[1], sig=x, scaled=scaled ) return diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index b3a92bfe..c9e72e11 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -142,7 +142,7 @@ restrain_deprecation_msg = build_deprecation_message( recipeorganizer_base, "restrain", - "add_restraint", + "add_penalty", removal_version, ) @@ -1154,7 +1154,7 @@ def clearConstraints(self, recurse=False): """ return self.clear_all_constraints(recurse=recurse) - def add_restraint( + def add_penalty( self, param_or_eq, lb=-inf, ub=inf, sig=1, scaled=False, params={} ): """Restrain an expression to specified bounds. @@ -1224,10 +1224,10 @@ def restrain( version 4.0.0. Please use - diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.add_restraint + diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.add_penalty instead. """ - return self.add_restraint( + return self.add_penalty( param_or_eq, lb=lb, ub=ub, sig=sig, scaled=scaled, params=params ) @@ -1262,7 +1262,7 @@ def remove_restraint(self, *ress): Attributes ---------- *ress : Restraint - The Restraints returned from the 'add_restraint' method or added + The Restraints returned from the 'add_penalty' method or added with the 'register_restraint' method. """ update = False diff --git a/tests/conftest.py b/tests/conftest.py index 8dc38c9a..8fb14445 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -218,8 +218,8 @@ def build_recipe_two_contributions(): recipe.add_constraint(contribution2.m, "2*k") recipe.add_constraint(contribution2.d, contribution1.c) recipe.add_constraint(contribution2.B, "0.5*A") - recipe.add_restraint(contribution1.A, 0.5, 1.5) - recipe.add_restraint(contribution1.k, 0.8, 1.2) + recipe.add_penalty(contribution1.A, 0.5, 1.5) + recipe.add_penalty(contribution1.k, 0.8, 1.2) return recipe diff --git a/tests/test_fitrecipe.py b/tests/test_fitrecipe.py index 885a34a7..0982f630 100644 --- a/tests/test_fitrecipe.py +++ b/tests/test_fitrecipe.py @@ -232,7 +232,7 @@ def testResidual(self): # Now try some restraints. We want c to be exactly zero. It should give # a penalty of (c-0)**2, which is 4 in this case - r1 = self.recipe.add_restraint(self.fitcontribution.c, 0, 0, 1) + r1 = self.recipe.add_penalty(self.fitcontribution.c, 0, 0, 1) self.recipe._ready = False res = self.recipe.residual() chi2 = 4 + dot(y - self.profile.y, y - self.profile.y) @@ -263,9 +263,7 @@ def testResidual(self): self.assertTrue(array_equal(y - self.profile.y, res)) # Add a restraint at the fitcontribution level. - r1 = self.fitcontribution.add_restraint( - self.fitcontribution.c, 0, 0, 1 - ) + r1 = self.fitcontribution.add_penalty(self.fitcontribution.c, 0, 0, 1) self.recipe._ready = False # The chi2 is the same as above, plus 4 res = self.recipe.residual() @@ -363,7 +361,7 @@ def testPrintFitHook(capturestdout): recipe.addContribution(fitcontribution) recipe.add_variable(fitcontribution.c) - recipe.add_restraint("c", lb=5) + recipe.add_penalty("c", lb=5) (pfh,) = recipe.getFitHooks() out = capturestdout(recipe.scalar_residual) assert "" == out @@ -439,7 +437,7 @@ def test_add_contribution(capturestdout): recipe.add_contribution(fitcontribution) recipe.add_variable(fitcontribution.c) - recipe.add_restraint("c", lb=5) + recipe.add_penalty("c", lb=5) (pfh,) = recipe.get_fit_hooks() out = capturestdout(recipe.scalar_residual) assert "" == out diff --git a/tests/test_recipeorganizer.py b/tests/test_recipeorganizer.py index 66cff9b4..12fbc3c6 100644 --- a/tests/test_recipeorganizer.py +++ b/tests/test_recipeorganizer.py @@ -345,7 +345,7 @@ def test_add_restraint(self): self.m._eqfactory.registerArgument("p2", p2) self.assertEqual(0, len(self.m._restraints)) - r = self.m.add_restraint("p1+p2", ub=10) + r = self.m.add_penalty("p1+p2", ub=10) self.assertEqual(1, len(self.m._restraints)) p2.set_value(10) self.assertEqual(1, r.penalty()) @@ -409,8 +409,8 @@ def testGetRestraints(self): m2._add_parameter(p3) m2._add_parameter(p4) - r1 = self.m.add_restraint("p1 + p2") - r2 = m2.add_restraint("2*p3 + p4") + r1 = self.m.add_penalty("p1 + p2") + r2 = m2.add_penalty("2*p3 + p4") res = self.m._get_restraints() self.assertTrue(r1 in res) @@ -639,7 +639,7 @@ def capture_show(*args, **kwargs): assert "Parameters" in lines2 assert "Constraints" in lines2 assert "Restraints" not in lines2 - organizer.add_restraint("z", lb=2, ub=3, sig=0.001) + organizer.add_penalty("z", lb=2, ub=3, sig=0.001) out3 = capture_show() lines3 = out3.strip().split("\n") assert 13 == len(lines3) From f04475cf7e969c6fa1eac1b7743b0a1334956d98 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Thu, 12 Mar 2026 10:20:18 -0400 Subject: [PATCH 11/21] remove_restraint --> remove_penalty --- news/recipeorg-dep2.rst | 4 ++-- src/diffpy/srfit/fitbase/recipeorganizer.py | 10 +++++----- tests/test_fitrecipe.py | 4 ++-- tests/test_recipeorganizer.py | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/news/recipeorg-dep2.rst b/news/recipeorg-dep2.rst index 2c707f1d..3d8f9535 100644 --- a/news/recipeorg-dep2.rst +++ b/news/recipeorg-dep2.rst @@ -3,7 +3,7 @@ * Added ``add_constraint`` method to ``RecipeOrganizer``. * Added ``remove_constraint`` method to ``RecipeOrganizer``. * Added ``add_penalty`` method to ``RecipeOrganizer``. -* Added ``remove_restraint`` method to ``RecipeOrganizer``. +* Added ``remove_penalty`` method to ``RecipeOrganizer``. * Added ``register_restraint`` method to ``RecipeOrganizer``. * Added ``clear_all_restraints`` method to ``RecipeOrganizer``. * Added ``get_equation_from_string`` method to ``RecipeOrganizer``. @@ -17,7 +17,7 @@ * Deprecated ``constrain`` method of ``RecipeOrganizer``. Use ``add_constraint`` instead. * Deprecated ``unconstrain`` method of ``RecipeOrganizer``. Use ``remove_constraint`` instead. * Deprecated ``restrain`` method of ``RecipeOrganizer``. Use ``add_penalty`` instead. -* Deprecated ``unrestrain`` methods of ``RecipeOrganizer``. Use ``remove_restraint`` instead. +* Deprecated ``unrestrain`` methods of ``RecipeOrganizer``. Use ``remove_penalty`` instead. * Deprecated ``addRestraint`` method of ``RecipeOrganizer``. Use ``register_restraint`` instead. * Deprecate ``clearRestraints`` method of ``RecipeOrganizer``. Use ``clear_all_restraints`` instead. * Deprecated ``equationFromString`` method of ``RecipeOrganizer``. Use ``get_equation_from_string`` instead. diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index c9e72e11..16d2a256 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -149,7 +149,7 @@ unrestrain_deprecation_msg = build_deprecation_message( recipeorganizer_base, "unrestrain", - "remove_restraint", + "remove_penalty", removal_version, ) @@ -1256,7 +1256,7 @@ def addRestraint(self, res): self.register_restraint(res) return - def remove_restraint(self, *ress): + def remove_penalty(self, *ress): """Remove a Restraint from the RecipeOrganizer. Attributes @@ -1284,10 +1284,10 @@ def unrestrain(self, *ress): version 4.0.0. Please use - diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.remove_restraint + diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.remove_penalty instead. """ - self.remove_restraint(*ress) + self.remove_penalty(*ress) return def clear_all_restraints(self, recurse=False): @@ -1299,7 +1299,7 @@ def clear_all_restraints(self, recurse=False): Recurse into managed objects and clear all restraints found there as well. """ - self.remove_restraint(*self._restraints) + self.remove_penalty(*self._restraints) if recurse: for msg in filter(_has_clear_restraints, self._iter_managed()): msg.clear_all_restraints(recurse) diff --git a/tests/test_fitrecipe.py b/tests/test_fitrecipe.py index 0982f630..515f4fd2 100644 --- a/tests/test_fitrecipe.py +++ b/tests/test_fitrecipe.py @@ -247,7 +247,7 @@ def testResidual(self): self.assertAlmostEqual(chi2, dot(res, res)) # Remove the restraint and variable - self.recipe.remove_restraint(r1) + self.recipe.remove_penalty(r1) self.recipe.delete_variable(self.recipe.Avar) self.recipe._ready = False res = self.recipe.residual() @@ -273,7 +273,7 @@ def testResidual(self): self.assertAlmostEqual(chi2, dot(res, res)) # Remove those - self.fitcontribution.remove_restraint(r1) + self.fitcontribution.remove_penalty(r1) self.recipe._ready = False self.fitcontribution.unconstrain(self.fitcontribution.c) self.fitcontribution.c.set_value(0) diff --git a/tests/test_recipeorganizer.py b/tests/test_recipeorganizer.py index 12fbc3c6..186f7092 100644 --- a/tests/test_recipeorganizer.py +++ b/tests/test_recipeorganizer.py @@ -349,7 +349,7 @@ def test_add_restraint(self): self.assertEqual(1, len(self.m._restraints)) p2.set_value(10) self.assertEqual(1, r.penalty()) - self.m.remove_restraint(r) + self.m.remove_penalty(r) self.assertEqual(0, len(self.m._restraints)) r = self.m.restrain(p1, ub=10) From a197c76bca3cd6b762e97714774767391b72ae55 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Thu, 12 Mar 2026 10:22:19 -0400 Subject: [PATCH 12/21] register_restraint --> register_penalty --- news/recipeorg-dep2.rst | 4 ++-- src/diffpy/srfit/fitbase/recipeorganizer.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/news/recipeorg-dep2.rst b/news/recipeorg-dep2.rst index 3d8f9535..03e2feaf 100644 --- a/news/recipeorg-dep2.rst +++ b/news/recipeorg-dep2.rst @@ -4,7 +4,7 @@ * Added ``remove_constraint`` method to ``RecipeOrganizer``. * Added ``add_penalty`` method to ``RecipeOrganizer``. * Added ``remove_penalty`` method to ``RecipeOrganizer``. -* Added ``register_restraint`` method to ``RecipeOrganizer``. +* Added ``register_penalty`` method to ``RecipeOrganizer``. * Added ``clear_all_restraints`` method to ``RecipeOrganizer``. * Added ``get_equation_from_string`` method to ``RecipeOrganizer``. @@ -18,7 +18,7 @@ * Deprecated ``unconstrain`` method of ``RecipeOrganizer``. Use ``remove_constraint`` instead. * Deprecated ``restrain`` method of ``RecipeOrganizer``. Use ``add_penalty`` instead. * Deprecated ``unrestrain`` methods of ``RecipeOrganizer``. Use ``remove_penalty`` instead. -* Deprecated ``addRestraint`` method of ``RecipeOrganizer``. Use ``register_restraint`` instead. +* Deprecated ``addRestraint`` method of ``RecipeOrganizer``. Use ``register_penalty`` instead. * Deprecate ``clearRestraints`` method of ``RecipeOrganizer``. Use ``clear_all_restraints`` instead. * Deprecated ``equationFromString`` method of ``RecipeOrganizer``. Use ``get_equation_from_string`` instead. diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index 16d2a256..239fffab 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -121,7 +121,7 @@ addRestraint_deprecation_msg = build_deprecation_message( recipeorganizer_base, "addRestraint", - "register_restraint", + "register_penalty", removal_version, ) @@ -1213,7 +1213,7 @@ def add_penalty( # Make and store the restraint param_or_eq = Restraint(eq, lb, ub, sig, scaled) param_or_eq.eqstr = eqstr - self.register_restraint(param_or_eq) + self.register_penalty(param_or_eq) return param_or_eq @deprecated(restrain_deprecation_msg) @@ -1231,7 +1231,7 @@ def restrain( param_or_eq, lb=lb, ub=ub, sig=sig, scaled=scaled, params=params ) - def register_restraint(self, res): + def register_penalty(self, res): """Add a Restraint instance to the RecipeOrganizer. Parameters @@ -1250,10 +1250,10 @@ def addRestraint(self, res): version 4.0.0. Please use - diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.register_restraint + diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.register_penalty instead. """ - self.register_restraint(res) + self.register_penalty(res) return def remove_penalty(self, *ress): @@ -1263,7 +1263,7 @@ def remove_penalty(self, *ress): ---------- *ress : Restraint The Restraints returned from the 'add_penalty' method or added - with the 'register_restraint' method. + with the 'register_penalty' method. """ update = False restuple = tuple(self._restraints) From d77c6ac69e48738fca82aacbd79c72a8e354a450 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Thu, 12 Mar 2026 10:23:04 -0400 Subject: [PATCH 13/21] clear_all_restraints --> clear_all_penalties --- news/recipeorg-dep2.rst | 4 ++-- src/diffpy/srfit/fitbase/recipeorganizer.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/news/recipeorg-dep2.rst b/news/recipeorg-dep2.rst index 03e2feaf..0b75f313 100644 --- a/news/recipeorg-dep2.rst +++ b/news/recipeorg-dep2.rst @@ -5,7 +5,7 @@ * Added ``add_penalty`` method to ``RecipeOrganizer``. * Added ``remove_penalty`` method to ``RecipeOrganizer``. * Added ``register_penalty`` method to ``RecipeOrganizer``. -* Added ``clear_all_restraints`` method to ``RecipeOrganizer``. +* Added ``clear_all_penalties`` method to ``RecipeOrganizer``. * Added ``get_equation_from_string`` method to ``RecipeOrganizer``. **Changed:** @@ -19,7 +19,7 @@ * Deprecated ``restrain`` method of ``RecipeOrganizer``. Use ``add_penalty`` instead. * Deprecated ``unrestrain`` methods of ``RecipeOrganizer``. Use ``remove_penalty`` instead. * Deprecated ``addRestraint`` method of ``RecipeOrganizer``. Use ``register_penalty`` instead. -* Deprecate ``clearRestraints`` method of ``RecipeOrganizer``. Use ``clear_all_restraints`` instead. +* Deprecate ``clearRestraints`` method of ``RecipeOrganizer``. Use ``clear_all_penalties`` instead. * Deprecated ``equationFromString`` method of ``RecipeOrganizer``. Use ``get_equation_from_string`` instead. **Removed:** diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index 239fffab..bed024b7 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -156,7 +156,7 @@ clearRestraints_deprecation_msg = build_deprecation_message( recipeorganizer_base, "clearRestraints", - "clear_all_restraints", + "clear_all_penalties", removal_version, ) @@ -1290,7 +1290,7 @@ def unrestrain(self, *ress): self.remove_penalty(*ress) return - def clear_all_restraints(self, recurse=False): + def clear_all_penalties(self, recurse=False): """Clear all restraints. Attributes @@ -1302,7 +1302,7 @@ def clear_all_restraints(self, recurse=False): self.remove_penalty(*self._restraints) if recurse: for msg in filter(_has_clear_restraints, self._iter_managed()): - msg.clear_all_restraints(recurse) + msg.clear_all_penalties(recurse) return @deprecated(clearRestraints_deprecation_msg) @@ -1311,10 +1311,10 @@ def clearRestraints(self, recurse=False): version 4.0.0. Please use - diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.clear_all_restraints + diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.clear_all_penalties instead. """ - self.clear_all_restraints(recurse=recurse) + self.clear_all_penalties(recurse=recurse) return def _get_constraints(self, recurse=True): @@ -1586,7 +1586,7 @@ def _has_clear_constraints(msg): def _has_clear_restraints(msg): - return hasattr(msg, "clear_all_restraints") + return hasattr(msg, "clear_all_penalties") def _has_get_restraints(msg): From 0b9e9f78aad6617f0dd42f26866222f6fc0201fd Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 13 Mar 2026 11:34:01 -0400 Subject: [PATCH 14/21] change lb to lower_bound everywhere --- docs/examples/crystalpdftwophase.py | 12 ++++---- docs/examples/debyemodel.py | 2 +- docs/examples/simplepdftwophase.py | 12 ++++---- src/diffpy/srfit/fitbase/parameter.py | 18 +++++------ src/diffpy/srfit/fitbase/recipeorganizer.py | 33 ++++++++++++++++----- src/diffpy/srfit/fitbase/restraint.py | 22 +++++++------- tests/test_fitrecipe.py | 8 ++--- tests/test_recipeorganizer.py | 2 +- 8 files changed, 64 insertions(+), 45 deletions(-) diff --git a/docs/examples/crystalpdftwophase.py b/docs/examples/crystalpdftwophase.py index c7354211..dc5aaadb 100644 --- a/docs/examples/crystalpdftwophase.py +++ b/docs/examples/crystalpdftwophase.py @@ -123,18 +123,18 @@ def makeRecipe(niciffile, siciffile, datname): # derived has no uncertainty. Thus, we will tell the recipe to scale the # residual, which means that it will be weighted as much as the average # data point during the fit. - recipe.add_penalty("a_ni", lb=3.527, ub=3.527, scaled=True) + recipe.add_penalty("a_ni", lower_bound=3.527, ub=3.527, scaled=True) # Now we do the same with the delta2 and Biso parameters (remember that # Biso = 8*pi**2*Uiso) - recipe.add_penalty("delta2_ni", lb=2.22, ub=2.22, scaled=True) - recipe.add_penalty("Biso_0_ni", lb=0.454, ub=0.454, scaled=True) + recipe.add_penalty("delta2_ni", lower_bound=2.22, ub=2.22, scaled=True) + recipe.add_penalty("Biso_0_ni", lower_bound=0.454, ub=0.454, scaled=True) # # We can do the same with the silicon values. We haven't done a thorough # job of measuring the uncertainties in the results, so we'll scale these # as well. - recipe.add_penalty("a_si", lb=5.430, ub=5.430, scaled=True) - recipe.add_penalty("delta2_si", lb=3.54, ub=3.54, scaled=True) - recipe.add_penalty("Biso_0_si", lb=0.645, ub=0.645, scaled=True) + recipe.add_penalty("a_si", lower_bound=5.430, ub=5.430, scaled=True) + recipe.add_penalty("delta2_si", lower_bound=3.54, ub=3.54, scaled=True) + recipe.add_penalty("Biso_0_si", lower_bound=0.645, ub=0.645, scaled=True) # Give the recipe away so it can be used! return recipe diff --git a/docs/examples/debyemodel.py b/docs/examples/debyemodel.py index a373f307..5173c376 100644 --- a/docs/examples/debyemodel.py +++ b/docs/examples/debyemodel.py @@ -152,7 +152,7 @@ def makeRecipe(): # breaking the restraint by the point-average chi^2 value so that the # restraint is roughly as significant as any other data point throughout # the fit. - recipe.add_penalty(recipe.offset, lb=0, scaled=True) + recipe.add_penalty(recipe.offset, lower_bound=0, scaled=True) # We're done setting up the recipe. We can now do other things with it. return recipe diff --git a/docs/examples/simplepdftwophase.py b/docs/examples/simplepdftwophase.py index ec496542..0c86ada5 100644 --- a/docs/examples/simplepdftwophase.py +++ b/docs/examples/simplepdftwophase.py @@ -82,18 +82,18 @@ def makeRecipe(niciffile, siciffile, datname): # derived has no uncertainty. Thus, we will tell the recipe to scale the # residual, which means that it will be weighted as much as the average # data point during the fit. - recipe.add_penalty("a_ni", lb=3.527, ub=3.527, scaled=True) + recipe.add_penalty("a_ni", lower_bound=3.527, ub=3.527, scaled=True) # Now we do the same with the delta2 and Biso parameters (remember that # Biso = 8*pi**2*Uiso) - recipe.add_penalty("delta2_ni", lb=2.22, ub=2.22, scaled=True) - recipe.add_penalty("Biso_0_ni", lb=0.454, ub=0.454, scaled=True) + recipe.add_penalty("delta2_ni", lower_bound=2.22, ub=2.22, scaled=True) + recipe.add_penalty("Biso_0_ni", lower_bound=0.454, ub=0.454, scaled=True) # # We can do the same with the silicon values. We haven't done a thorough # job of measuring the uncertainties in the results, so we'll scale these # as well. - recipe.add_penalty("a_si", lb=5.430, ub=5.430, scaled=True) - recipe.add_penalty("delta2_si", lb=3.54, ub=3.54, scaled=True) - recipe.add_penalty("Biso_0_si", lb=0.645, ub=0.645, scaled=True) + recipe.add_penalty("a_si", lower_bound=5.430, ub=5.430, scaled=True) + recipe.add_penalty("delta2_si", lower_bound=3.54, ub=3.54, scaled=True) + recipe.add_penalty("Biso_0_si", lower_bound=0.645, ub=0.645, scaled=True) # Give the recipe away so it can be used! return recipe diff --git a/src/diffpy/srfit/fitbase/parameter.py b/src/diffpy/srfit/fitbase/parameter.py index 1c7450d7..1f323d62 100644 --- a/src/diffpy/srfit/fitbase/parameter.py +++ b/src/diffpy/srfit/fitbase/parameter.py @@ -95,7 +95,7 @@ def set_value(self, val): ---------- val The value to assign. - lb + lower_bound : float The lower bounds for the bounds list. If this is None (default), then the lower bound will not be alterered. ub @@ -142,12 +142,12 @@ def setConst(self, const=True, value=None): self.set_value(value) return self - def boundRange(self, lb=None, ub=None): + def boundRange(self, lower_bound=None, ub=None): """Set lower and upper bound of the Parameter. Attributes ---------- - lb + lower_bound : float The lower bound for the bounds list. ub The upper bound for the bounds list. @@ -157,8 +157,8 @@ def boundRange(self, lb=None, ub=None): self Returns self so that mutators can be chained. """ - if lb is not None: - self.bounds[0] = lb + if lower_bound is not None: + self.bounds[0] = lower_bound if ub is not None: self.bounds[1] = ub return self @@ -182,11 +182,11 @@ def boundWindow(self, lr=0, ur=None): Returns self so that mutators can be chained. """ val = self.getValue() - lb = val - lr + lower_bound = val - lr if ur is None: ur = lr ub = val + ur - self.bounds = [lb, ub] + self.bounds = [lower_bound, ub] return self def _validate(self): @@ -283,8 +283,8 @@ def setConst(self, const=True, value=None): return self.par.setConst(const, value) @wraps(Parameter.boundRange) - def boundRange(self, lb=None, ub=None): - return self.par.boundRange(lb, ub) + def boundRange(self, lower_bound=None, ub=None): + return self.par.boundRange(lower_bound, ub) @wraps(Parameter.boundWindow) def boundWindow(self, lr=0, ur=None): diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index bed024b7..3a3edb86 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -1155,7 +1155,13 @@ def clearConstraints(self, recurse=False): return self.clear_all_constraints(recurse=recurse) def add_penalty( - self, param_or_eq, lb=-inf, ub=inf, sig=1, scaled=False, params={} + self, + param_or_eq, + lower_bound=-inf, + ub=inf, + sig=1, + scaled=False, + params={}, ): """Restrain an expression to specified bounds. @@ -1163,7 +1169,7 @@ def add_penalty( ---------- param_or_eq : str or Parameter The equation string or a Parameter object to restrain. - lb : float, optional + lower_bound : float, optional The lower bound for the restraint evaluation (default is -inf). ub : float, optional The upper bound for the restraint evaluation (default is inf). @@ -1188,7 +1194,7 @@ def add_penalty( The penalty is calculated as: .. - (max(0, lb - val, val - ub) / sig) ** 2 + (max(0, lower_bound - val, val - ub) / sig) ** 2 where `val` is the value of the evaluated equation. If `scaled` is True, this penalty is multiplied by @@ -1211,14 +1217,20 @@ def add_penalty( eqstr = param_or_eq.name # Make and store the restraint - param_or_eq = Restraint(eq, lb, ub, sig, scaled) + param_or_eq = Restraint(eq, lower_bound, ub, sig, scaled) param_or_eq.eqstr = eqstr self.register_penalty(param_or_eq) return param_or_eq @deprecated(restrain_deprecation_msg) def restrain( - self, param_or_eq, lb=-inf, ub=inf, sig=1, scaled=False, params={} + self, + param_or_eq, + lower_bound=-inf, + ub=inf, + sig=1, + scaled=False, + params={}, ): """This function has been deprecated and will be removed in version 4.0.0. @@ -1228,7 +1240,12 @@ def restrain( instead. """ return self.add_penalty( - param_or_eq, lb=lb, ub=ub, sig=sig, scaled=scaled, params=params + param_or_eq, + lower_bound=lower_bound, + ub=ub, + sig=sig, + scaled=scaled, + params=params, ) def register_penalty(self, res): @@ -1429,9 +1446,9 @@ def _format_restraints(self): rset = self._get_restraints() rlines = [] for res in rset: - line = "%s: lb = %f, ub = %f, sig = %f, scaled = %s" % ( + line = "%s: lower_bound = %f, ub = %f, sig = %f, scaled = %s" % ( res.eqstr, - res.lb, + res.lower_bound, res.ub, res.sig, res.scaled, diff --git a/src/diffpy/srfit/fitbase/restraint.py b/src/diffpy/srfit/fitbase/restraint.py index 5630d808..4b0210fc 100644 --- a/src/diffpy/srfit/fitbase/restraint.py +++ b/src/diffpy/srfit/fitbase/restraint.py @@ -33,28 +33,28 @@ class Restraint(Validatable): Attributes ---------- - eq + eq : Equation An equation whose evaluation is compared against the restraint bounds. - lb + lower_bound : float The lower bound on the restraint evaluation (default -inf). - ub + ub : float The lower bound on the restraint evaluation (default inf). - sig + sig : float The uncertainty on the bounds (default 1). - scaled + scaled : bool 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 + (max(0, lower_bound - val, val - ub)/sig)**2 and val is the value of the calculated equation. This is multiplied by the average chi^2 if scaled is True. """ - def __init__(self, eq, lb=-inf, ub=inf, sig=1, scaled=False): + def __init__(self, eq, lower_bound=-inf, ub=inf, sig=1, scaled=False): """Restrain an equation to specified bounds. Attributes @@ -62,7 +62,7 @@ def __init__(self, eq, lb=-inf, ub=inf, sig=1, scaled=False): eq An equation whose evaluation is compared against the restraint bounds. - lb + lower_bound The lower bound on the restraint evaluation (float, default -inf). ub @@ -76,7 +76,7 @@ def __init__(self, eq, lb=-inf, ub=inf, sig=1, scaled=False): (bool, default False). """ self.eq = eq - self.lb = float(lb) + self.lower_bound = float(lower_bound) self.ub = float(ub) self.sig = float(sig) self.scaled = bool(scaled) @@ -97,7 +97,9 @@ def penalty(self, w=1.0): Returns the penalty as a float """ val = self.eq() - penalty = (max(0, self.lb - val, val - self.ub) / self.sig) ** 2 + penalty = ( + max(0, self.lower_bound - val, val - self.ub) / self.sig + ) ** 2 if self.scaled: penalty *= w diff --git a/tests/test_fitrecipe.py b/tests/test_fitrecipe.py index 515f4fd2..85842a8c 100644 --- a/tests/test_fitrecipe.py +++ b/tests/test_fitrecipe.py @@ -314,7 +314,7 @@ def test_boundsToRestraints(): restraints = list(recipe._restraints) assert len(restraints) == 1 r = restraints[0] - actual_lower_bound = r.lb + actual_lower_bound = r.lower_bound actual_upper_bound = r.ub actual_sigma = r.sig assert actual_lower_bound == expected_lower_bound @@ -333,7 +333,7 @@ def test_convert_bounds_to_restraints(): restraints = list(recipe._restraints) assert len(restraints) == 1 r = restraints[0] - assert r.lb == -1 + assert r.lower_bound == -1 assert r.ub == 1 assert r.sig == 2 assert r.scaled is True @@ -361,7 +361,7 @@ def testPrintFitHook(capturestdout): recipe.addContribution(fitcontribution) recipe.add_variable(fitcontribution.c) - recipe.add_penalty("c", lb=5) + recipe.add_penalty("c", lower_bound=5) (pfh,) = recipe.getFitHooks() out = capturestdout(recipe.scalar_residual) assert "" == out @@ -437,7 +437,7 @@ def test_add_contribution(capturestdout): recipe.add_contribution(fitcontribution) recipe.add_variable(fitcontribution.c) - recipe.add_penalty("c", lb=5) + recipe.add_penalty("c", lower_bound=5) (pfh,) = recipe.get_fit_hooks() out = capturestdout(recipe.scalar_residual) assert "" == out diff --git a/tests/test_recipeorganizer.py b/tests/test_recipeorganizer.py index 186f7092..a1d9b69a 100644 --- a/tests/test_recipeorganizer.py +++ b/tests/test_recipeorganizer.py @@ -639,7 +639,7 @@ def capture_show(*args, **kwargs): assert "Parameters" in lines2 assert "Constraints" in lines2 assert "Restraints" not in lines2 - organizer.add_penalty("z", lb=2, ub=3, sig=0.001) + organizer.add_penalty("z", lower_bound=2, ub=3, sig=0.001) out3 = capture_show() lines3 = out3.strip().split("\n") assert 13 == len(lines3) From 273e63a16e999231e02e55bcba98a200ef8e8b6d Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 13 Mar 2026 11:36:05 -0400 Subject: [PATCH 15/21] change ub to upper_bound everywhere --- docs/examples/crystalpdftwophase.py | 24 +++++++++++++----- docs/examples/simplepdftwophase.py | 24 +++++++++++++----- src/diffpy/srfit/fitbase/parameter.py | 18 +++++++------- src/diffpy/srfit/fitbase/recipeorganizer.py | 27 ++++++++++++--------- src/diffpy/srfit/fitbase/restraint.py | 14 ++++++----- tests/test_fitrecipe.py | 4 +-- tests/test_recipeorganizer.py | 6 ++--- tests/test_restraint.py | 2 +- 8 files changed, 74 insertions(+), 45 deletions(-) diff --git a/docs/examples/crystalpdftwophase.py b/docs/examples/crystalpdftwophase.py index dc5aaadb..a39b1b2f 100644 --- a/docs/examples/crystalpdftwophase.py +++ b/docs/examples/crystalpdftwophase.py @@ -123,18 +123,30 @@ def makeRecipe(niciffile, siciffile, datname): # derived has no uncertainty. Thus, we will tell the recipe to scale the # residual, which means that it will be weighted as much as the average # data point during the fit. - recipe.add_penalty("a_ni", lower_bound=3.527, ub=3.527, scaled=True) + recipe.add_penalty( + "a_ni", lower_bound=3.527, upper_bound=3.527, scaled=True + ) # Now we do the same with the delta2 and Biso parameters (remember that # Biso = 8*pi**2*Uiso) - recipe.add_penalty("delta2_ni", lower_bound=2.22, ub=2.22, scaled=True) - recipe.add_penalty("Biso_0_ni", lower_bound=0.454, ub=0.454, scaled=True) + recipe.add_penalty( + "delta2_ni", lower_bound=2.22, upper_bound=2.22, scaled=True + ) + recipe.add_penalty( + "Biso_0_ni", lower_bound=0.454, upper_bound=0.454, scaled=True + ) # # We can do the same with the silicon values. We haven't done a thorough # job of measuring the uncertainties in the results, so we'll scale these # as well. - recipe.add_penalty("a_si", lower_bound=5.430, ub=5.430, scaled=True) - recipe.add_penalty("delta2_si", lower_bound=3.54, ub=3.54, scaled=True) - recipe.add_penalty("Biso_0_si", lower_bound=0.645, ub=0.645, scaled=True) + recipe.add_penalty( + "a_si", lower_bound=5.430, upper_bound=5.430, scaled=True + ) + recipe.add_penalty( + "delta2_si", lower_bound=3.54, upper_bound=3.54, scaled=True + ) + recipe.add_penalty( + "Biso_0_si", lower_bound=0.645, upper_bound=0.645, scaled=True + ) # Give the recipe away so it can be used! return recipe diff --git a/docs/examples/simplepdftwophase.py b/docs/examples/simplepdftwophase.py index 0c86ada5..8a48cd60 100644 --- a/docs/examples/simplepdftwophase.py +++ b/docs/examples/simplepdftwophase.py @@ -82,18 +82,30 @@ def makeRecipe(niciffile, siciffile, datname): # derived has no uncertainty. Thus, we will tell the recipe to scale the # residual, which means that it will be weighted as much as the average # data point during the fit. - recipe.add_penalty("a_ni", lower_bound=3.527, ub=3.527, scaled=True) + recipe.add_penalty( + "a_ni", lower_bound=3.527, upper_bound=3.527, scaled=True + ) # Now we do the same with the delta2 and Biso parameters (remember that # Biso = 8*pi**2*Uiso) - recipe.add_penalty("delta2_ni", lower_bound=2.22, ub=2.22, scaled=True) - recipe.add_penalty("Biso_0_ni", lower_bound=0.454, ub=0.454, scaled=True) + recipe.add_penalty( + "delta2_ni", lower_bound=2.22, upper_bound=2.22, scaled=True + ) + recipe.add_penalty( + "Biso_0_ni", lower_bound=0.454, upper_bound=0.454, scaled=True + ) # # We can do the same with the silicon values. We haven't done a thorough # job of measuring the uncertainties in the results, so we'll scale these # as well. - recipe.add_penalty("a_si", lower_bound=5.430, ub=5.430, scaled=True) - recipe.add_penalty("delta2_si", lower_bound=3.54, ub=3.54, scaled=True) - recipe.add_penalty("Biso_0_si", lower_bound=0.645, ub=0.645, scaled=True) + recipe.add_penalty( + "a_si", lower_bound=5.430, upper_bound=5.430, scaled=True + ) + recipe.add_penalty( + "delta2_si", lower_bound=3.54, upper_bound=3.54, scaled=True + ) + recipe.add_penalty( + "Biso_0_si", lower_bound=0.645, upper_bound=0.645, scaled=True + ) # Give the recipe away so it can be used! return recipe diff --git a/src/diffpy/srfit/fitbase/parameter.py b/src/diffpy/srfit/fitbase/parameter.py index 1f323d62..6bddf43b 100644 --- a/src/diffpy/srfit/fitbase/parameter.py +++ b/src/diffpy/srfit/fitbase/parameter.py @@ -98,7 +98,7 @@ def set_value(self, val): lower_bound : float The lower bounds for the bounds list. If this is None (default), then the lower bound will not be alterered. - ub + upper_bound : float The upper bounds for the bounds list. If this is None (default), then the upper bound will not be alterered. @@ -142,14 +142,14 @@ def setConst(self, const=True, value=None): self.set_value(value) return self - def boundRange(self, lower_bound=None, ub=None): + def boundRange(self, lower_bound=None, upper_bound=None): """Set lower and upper bound of the Parameter. Attributes ---------- lower_bound : float The lower bound for the bounds list. - ub + upper_bound : float The upper bound for the bounds list. Returns @@ -159,8 +159,8 @@ def boundRange(self, lower_bound=None, ub=None): """ if lower_bound is not None: self.bounds[0] = lower_bound - if ub is not None: - self.bounds[1] = ub + if upper_bound is not None: + self.bounds[1] = upper_bound return self def boundWindow(self, lr=0, ur=None): @@ -185,8 +185,8 @@ def boundWindow(self, lr=0, ur=None): lower_bound = val - lr if ur is None: ur = lr - ub = val + ur - self.bounds = [lower_bound, ub] + upper_bound = val + ur + self.bounds = [lower_bound, upper_bound] return self def _validate(self): @@ -283,8 +283,8 @@ def setConst(self, const=True, value=None): return self.par.setConst(const, value) @wraps(Parameter.boundRange) - def boundRange(self, lower_bound=None, ub=None): - return self.par.boundRange(lower_bound, ub) + def boundRange(self, lower_bound=None, upper_bound=None): + return self.par.boundRange(lower_bound, upper_bound) @wraps(Parameter.boundWindow) def boundWindow(self, lr=0, ur=None): diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index 3a3edb86..9c0cd3f7 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -1158,7 +1158,7 @@ def add_penalty( self, param_or_eq, lower_bound=-inf, - ub=inf, + upper_bound=inf, sig=1, scaled=False, params={}, @@ -1171,7 +1171,7 @@ def add_penalty( The equation string or a Parameter object to restrain. lower_bound : float, optional The lower bound for the restraint evaluation (default is -inf). - ub : float, optional + upper_bound : float, optional The upper bound for the restraint evaluation (default is inf). sig : float, optional The uncertainty associated with the bounds (default is 1). @@ -1194,7 +1194,7 @@ def add_penalty( The penalty is calculated as: .. - (max(0, lower_bound - val, val - ub) / sig) ** 2 + (max(0, lower_bound - val, val - upper_bound) / sig) ** 2 where `val` is the value of the evaluated equation. If `scaled` is True, this penalty is multiplied by @@ -1217,7 +1217,7 @@ def add_penalty( eqstr = param_or_eq.name # Make and store the restraint - param_or_eq = Restraint(eq, lower_bound, ub, sig, scaled) + param_or_eq = Restraint(eq, lower_bound, upper_bound, sig, scaled) param_or_eq.eqstr = eqstr self.register_penalty(param_or_eq) return param_or_eq @@ -1227,7 +1227,7 @@ def restrain( self, param_or_eq, lower_bound=-inf, - ub=inf, + upper_bound=inf, sig=1, scaled=False, params={}, @@ -1242,7 +1242,7 @@ def restrain( return self.add_penalty( param_or_eq, lower_bound=lower_bound, - ub=ub, + upper_bound=upper_bound, sig=sig, scaled=scaled, params=params, @@ -1446,12 +1446,15 @@ def _format_restraints(self): rset = self._get_restraints() rlines = [] for res in rset: - line = "%s: lower_bound = %f, ub = %f, sig = %f, scaled = %s" % ( - res.eqstr, - res.lower_bound, - res.ub, - res.sig, - res.scaled, + line = ( + "%s: lower_bound = %f, upper_bound = %f, sig = %f, scaled = %s" + % ( + res.eqstr, + res.lower_bound, + res.upper_bound, + res.sig, + res.scaled, + ) ) rlines.append(line) rlines.sort(key=numstr) diff --git a/src/diffpy/srfit/fitbase/restraint.py b/src/diffpy/srfit/fitbase/restraint.py index 4b0210fc..7b6ec747 100644 --- a/src/diffpy/srfit/fitbase/restraint.py +++ b/src/diffpy/srfit/fitbase/restraint.py @@ -38,7 +38,7 @@ class Restraint(Validatable): bounds. lower_bound : float The lower bound on the restraint evaluation (default -inf). - ub : float + upper_bound : float The lower bound on the restraint evaluation (default inf). sig : float The uncertainty on the bounds (default 1). @@ -49,12 +49,14 @@ class Restraint(Validatable): The penalty is calculated as - (max(0, lower_bound - val, val - ub)/sig)**2 + (max(0, lower_bound - val, val - upper_bound)/sig)**2 and val is the value of the calculated equation. This is multiplied by the average chi^2 if scaled is True. """ - def __init__(self, eq, lower_bound=-inf, ub=inf, sig=1, scaled=False): + def __init__( + self, eq, lower_bound=-inf, upper_bound=inf, sig=1, scaled=False + ): """Restrain an equation to specified bounds. Attributes @@ -65,7 +67,7 @@ def __init__(self, eq, lower_bound=-inf, ub=inf, sig=1, scaled=False): lower_bound The lower bound on the restraint evaluation (float, default -inf). - ub + upper_bound The lower bound on the restraint evaluation (float, default inf). sig @@ -77,7 +79,7 @@ def __init__(self, eq, lower_bound=-inf, ub=inf, sig=1, scaled=False): """ self.eq = eq self.lower_bound = float(lower_bound) - self.ub = float(ub) + self.upper_bound = float(upper_bound) self.sig = float(sig) self.scaled = bool(scaled) return @@ -98,7 +100,7 @@ def penalty(self, w=1.0): """ val = self.eq() penalty = ( - max(0, self.lower_bound - val, val - self.ub) / self.sig + max(0, self.lower_bound - val, val - self.upper_bound) / self.sig ) ** 2 if self.scaled: diff --git a/tests/test_fitrecipe.py b/tests/test_fitrecipe.py index 85842a8c..9e09568b 100644 --- a/tests/test_fitrecipe.py +++ b/tests/test_fitrecipe.py @@ -315,7 +315,7 @@ def test_boundsToRestraints(): assert len(restraints) == 1 r = restraints[0] actual_lower_bound = r.lower_bound - actual_upper_bound = r.ub + actual_upper_bound = r.upper_bound actual_sigma = r.sig assert actual_lower_bound == expected_lower_bound assert actual_upper_bound == expected_upper_bound @@ -334,7 +334,7 @@ def test_convert_bounds_to_restraints(): assert len(restraints) == 1 r = restraints[0] assert r.lower_bound == -1 - assert r.ub == 1 + assert r.upper_bound == 1 assert r.sig == 2 assert r.scaled is True diff --git a/tests/test_recipeorganizer.py b/tests/test_recipeorganizer.py index a1d9b69a..7d4e216f 100644 --- a/tests/test_recipeorganizer.py +++ b/tests/test_recipeorganizer.py @@ -345,14 +345,14 @@ def test_add_restraint(self): self.m._eqfactory.registerArgument("p2", p2) self.assertEqual(0, len(self.m._restraints)) - r = self.m.add_penalty("p1+p2", ub=10) + r = self.m.add_penalty("p1+p2", upper_bound=10) self.assertEqual(1, len(self.m._restraints)) p2.set_value(10) self.assertEqual(1, r.penalty()) self.m.remove_penalty(r) self.assertEqual(0, len(self.m._restraints)) - r = self.m.restrain(p1, ub=10) + r = self.m.restrain(p1, upper_bound=10) self.assertEqual(1, len(self.m._restraints)) p1.set_value(11) self.assertEqual(1, r.penalty()) @@ -639,7 +639,7 @@ def capture_show(*args, **kwargs): assert "Parameters" in lines2 assert "Constraints" in lines2 assert "Restraints" not in lines2 - organizer.add_penalty("z", lower_bound=2, ub=3, sig=0.001) + organizer.add_penalty("z", lower_bound=2, upper_bound=3, sig=0.001) out3 = capture_show() lines3 = out3.strip().split("\n") assert 13 == len(lines3) diff --git a/tests/test_restraint.py b/tests/test_restraint.py index 09819531..befae689 100644 --- a/tests/test_restraint.py +++ b/tests/test_restraint.py @@ -63,7 +63,7 @@ def testRestraint(self): # Make a really large number to check the upper bound import numpy - r.ub = numpy.inf + r.upper_bound = numpy.inf p1.set_value(1e100) self.assertEqual(0, r.penalty()) From 7032d6b54c2eb08fbe1aac0ed71cfe150c391af2 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 13 Mar 2026 11:39:36 -0400 Subject: [PATCH 16/21] add_penalty --> add_soft_bounds --- docs/examples/crystalpdftwophase.py | 12 ++++++------ docs/examples/debyemodel.py | 2 +- docs/examples/simplepdftwophase.py | 12 ++++++------ news/recipeorg-dep2.rst | 4 ++-- src/diffpy/srfit/fitbase/fitrecipe.py | 2 +- src/diffpy/srfit/fitbase/recipeorganizer.py | 10 +++++----- tests/conftest.py | 4 ++-- tests/test_fitrecipe.py | 10 ++++++---- tests/test_recipeorganizer.py | 8 ++++---- 9 files changed, 33 insertions(+), 31 deletions(-) diff --git a/docs/examples/crystalpdftwophase.py b/docs/examples/crystalpdftwophase.py index a39b1b2f..1d6878b2 100644 --- a/docs/examples/crystalpdftwophase.py +++ b/docs/examples/crystalpdftwophase.py @@ -123,28 +123,28 @@ def makeRecipe(niciffile, siciffile, datname): # derived has no uncertainty. Thus, we will tell the recipe to scale the # residual, which means that it will be weighted as much as the average # data point during the fit. - recipe.add_penalty( + recipe.add_soft_bounds( "a_ni", lower_bound=3.527, upper_bound=3.527, scaled=True ) # Now we do the same with the delta2 and Biso parameters (remember that # Biso = 8*pi**2*Uiso) - recipe.add_penalty( + recipe.add_soft_bounds( "delta2_ni", lower_bound=2.22, upper_bound=2.22, scaled=True ) - recipe.add_penalty( + recipe.add_soft_bounds( "Biso_0_ni", lower_bound=0.454, upper_bound=0.454, scaled=True ) # # We can do the same with the silicon values. We haven't done a thorough # job of measuring the uncertainties in the results, so we'll scale these # as well. - recipe.add_penalty( + recipe.add_soft_bounds( "a_si", lower_bound=5.430, upper_bound=5.430, scaled=True ) - recipe.add_penalty( + recipe.add_soft_bounds( "delta2_si", lower_bound=3.54, upper_bound=3.54, scaled=True ) - recipe.add_penalty( + recipe.add_soft_bounds( "Biso_0_si", lower_bound=0.645, upper_bound=0.645, scaled=True ) diff --git a/docs/examples/debyemodel.py b/docs/examples/debyemodel.py index 5173c376..7813be4e 100644 --- a/docs/examples/debyemodel.py +++ b/docs/examples/debyemodel.py @@ -152,7 +152,7 @@ def makeRecipe(): # breaking the restraint by the point-average chi^2 value so that the # restraint is roughly as significant as any other data point throughout # the fit. - recipe.add_penalty(recipe.offset, lower_bound=0, scaled=True) + recipe.add_soft_bounds(recipe.offset, lower_bound=0, scaled=True) # We're done setting up the recipe. We can now do other things with it. return recipe diff --git a/docs/examples/simplepdftwophase.py b/docs/examples/simplepdftwophase.py index 8a48cd60..0b7ee046 100644 --- a/docs/examples/simplepdftwophase.py +++ b/docs/examples/simplepdftwophase.py @@ -82,28 +82,28 @@ def makeRecipe(niciffile, siciffile, datname): # derived has no uncertainty. Thus, we will tell the recipe to scale the # residual, which means that it will be weighted as much as the average # data point during the fit. - recipe.add_penalty( + recipe.add_soft_bounds( "a_ni", lower_bound=3.527, upper_bound=3.527, scaled=True ) # Now we do the same with the delta2 and Biso parameters (remember that # Biso = 8*pi**2*Uiso) - recipe.add_penalty( + recipe.add_soft_bounds( "delta2_ni", lower_bound=2.22, upper_bound=2.22, scaled=True ) - recipe.add_penalty( + recipe.add_soft_bounds( "Biso_0_ni", lower_bound=0.454, upper_bound=0.454, scaled=True ) # # We can do the same with the silicon values. We haven't done a thorough # job of measuring the uncertainties in the results, so we'll scale these # as well. - recipe.add_penalty( + recipe.add_soft_bounds( "a_si", lower_bound=5.430, upper_bound=5.430, scaled=True ) - recipe.add_penalty( + recipe.add_soft_bounds( "delta2_si", lower_bound=3.54, upper_bound=3.54, scaled=True ) - recipe.add_penalty( + recipe.add_soft_bounds( "Biso_0_si", lower_bound=0.645, upper_bound=0.645, scaled=True ) diff --git a/news/recipeorg-dep2.rst b/news/recipeorg-dep2.rst index 0b75f313..45e9f19d 100644 --- a/news/recipeorg-dep2.rst +++ b/news/recipeorg-dep2.rst @@ -2,7 +2,7 @@ * Added ``add_constraint`` method to ``RecipeOrganizer``. * Added ``remove_constraint`` method to ``RecipeOrganizer``. -* Added ``add_penalty`` method to ``RecipeOrganizer``. +* Added ``add_soft_bounds`` method to ``RecipeOrganizer``. * Added ``remove_penalty`` method to ``RecipeOrganizer``. * Added ``register_penalty`` method to ``RecipeOrganizer``. * Added ``clear_all_penalties`` method to ``RecipeOrganizer``. @@ -16,7 +16,7 @@ * Deprecated ``constrain`` method of ``RecipeOrganizer``. Use ``add_constraint`` instead. * Deprecated ``unconstrain`` method of ``RecipeOrganizer``. Use ``remove_constraint`` instead. -* Deprecated ``restrain`` method of ``RecipeOrganizer``. Use ``add_penalty`` instead. +* Deprecated ``restrain`` method of ``RecipeOrganizer``. Use ``add_soft_bounds`` instead. * Deprecated ``unrestrain`` methods of ``RecipeOrganizer``. Use ``remove_penalty`` instead. * Deprecated ``addRestraint`` method of ``RecipeOrganizer``. Use ``register_penalty`` instead. * Deprecate ``clearRestraints`` method of ``RecipeOrganizer``. Use ``clear_all_penalties`` instead. diff --git a/src/diffpy/srfit/fitbase/fitrecipe.py b/src/diffpy/srfit/fitbase/fitrecipe.py index 7740a20e..0961b504 100644 --- a/src/diffpy/srfit/fitbase/fitrecipe.py +++ b/src/diffpy/srfit/fitbase/fitrecipe.py @@ -1756,7 +1756,7 @@ def convert_bounds_to_restraints(self, sig=1, scaled=False): if not hasattr(sig, "__iter__"): sig = [sig] * len(pars) for par, x in zip(pars, sig): - self.add_penalty( + self.add_soft_bounds( par, par.bounds[0], par.bounds[1], sig=x, scaled=scaled ) return diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index 9c0cd3f7..ca664be4 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -142,7 +142,7 @@ restrain_deprecation_msg = build_deprecation_message( recipeorganizer_base, "restrain", - "add_penalty", + "add_soft_bounds", removal_version, ) @@ -1154,7 +1154,7 @@ def clearConstraints(self, recurse=False): """ return self.clear_all_constraints(recurse=recurse) - def add_penalty( + def add_soft_bounds( self, param_or_eq, lower_bound=-inf, @@ -1236,10 +1236,10 @@ def restrain( version 4.0.0. Please use - diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.add_penalty + diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.add_soft_bounds instead. """ - return self.add_penalty( + return self.add_soft_bounds( param_or_eq, lower_bound=lower_bound, upper_bound=upper_bound, @@ -1279,7 +1279,7 @@ def remove_penalty(self, *ress): Attributes ---------- *ress : Restraint - The Restraints returned from the 'add_penalty' method or added + The Restraints returned from the 'add_soft_bounds' method or added with the 'register_penalty' method. """ update = False diff --git a/tests/conftest.py b/tests/conftest.py index 8fb14445..6eb797c3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -218,8 +218,8 @@ def build_recipe_two_contributions(): recipe.add_constraint(contribution2.m, "2*k") recipe.add_constraint(contribution2.d, contribution1.c) recipe.add_constraint(contribution2.B, "0.5*A") - recipe.add_penalty(contribution1.A, 0.5, 1.5) - recipe.add_penalty(contribution1.k, 0.8, 1.2) + recipe.add_soft_bounds(contribution1.A, 0.5, 1.5) + recipe.add_soft_bounds(contribution1.k, 0.8, 1.2) return recipe diff --git a/tests/test_fitrecipe.py b/tests/test_fitrecipe.py index 9e09568b..fe8f8352 100644 --- a/tests/test_fitrecipe.py +++ b/tests/test_fitrecipe.py @@ -232,7 +232,7 @@ def testResidual(self): # Now try some restraints. We want c to be exactly zero. It should give # a penalty of (c-0)**2, which is 4 in this case - r1 = self.recipe.add_penalty(self.fitcontribution.c, 0, 0, 1) + r1 = self.recipe.add_soft_bounds(self.fitcontribution.c, 0, 0, 1) self.recipe._ready = False res = self.recipe.residual() chi2 = 4 + dot(y - self.profile.y, y - self.profile.y) @@ -263,7 +263,9 @@ def testResidual(self): self.assertTrue(array_equal(y - self.profile.y, res)) # Add a restraint at the fitcontribution level. - r1 = self.fitcontribution.add_penalty(self.fitcontribution.c, 0, 0, 1) + r1 = self.fitcontribution.add_soft_bounds( + self.fitcontribution.c, 0, 0, 1 + ) self.recipe._ready = False # The chi2 is the same as above, plus 4 res = self.recipe.residual() @@ -361,7 +363,7 @@ def testPrintFitHook(capturestdout): recipe.addContribution(fitcontribution) recipe.add_variable(fitcontribution.c) - recipe.add_penalty("c", lower_bound=5) + recipe.add_soft_bounds("c", lower_bound=5) (pfh,) = recipe.getFitHooks() out = capturestdout(recipe.scalar_residual) assert "" == out @@ -437,7 +439,7 @@ def test_add_contribution(capturestdout): recipe.add_contribution(fitcontribution) recipe.add_variable(fitcontribution.c) - recipe.add_penalty("c", lower_bound=5) + recipe.add_soft_bounds("c", lower_bound=5) (pfh,) = recipe.get_fit_hooks() out = capturestdout(recipe.scalar_residual) assert "" == out diff --git a/tests/test_recipeorganizer.py b/tests/test_recipeorganizer.py index 7d4e216f..d10441c2 100644 --- a/tests/test_recipeorganizer.py +++ b/tests/test_recipeorganizer.py @@ -345,7 +345,7 @@ def test_add_restraint(self): self.m._eqfactory.registerArgument("p2", p2) self.assertEqual(0, len(self.m._restraints)) - r = self.m.add_penalty("p1+p2", upper_bound=10) + r = self.m.add_soft_bounds("p1+p2", upper_bound=10) self.assertEqual(1, len(self.m._restraints)) p2.set_value(10) self.assertEqual(1, r.penalty()) @@ -409,8 +409,8 @@ def testGetRestraints(self): m2._add_parameter(p3) m2._add_parameter(p4) - r1 = self.m.add_penalty("p1 + p2") - r2 = m2.add_penalty("2*p3 + p4") + r1 = self.m.add_soft_bounds("p1 + p2") + r2 = m2.add_soft_bounds("2*p3 + p4") res = self.m._get_restraints() self.assertTrue(r1 in res) @@ -639,7 +639,7 @@ def capture_show(*args, **kwargs): assert "Parameters" in lines2 assert "Constraints" in lines2 assert "Restraints" not in lines2 - organizer.add_penalty("z", lower_bound=2, upper_bound=3, sig=0.001) + organizer.add_soft_bounds("z", lower_bound=2, upper_bound=3, sig=0.001) out3 = capture_show() lines3 = out3.strip().split("\n") assert 13 == len(lines3) From dab361fd3745d3386dca46971abdf21df359216c Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 13 Mar 2026 11:40:02 -0400 Subject: [PATCH 17/21] remove_penalty --> remove_soft_bounds --- news/recipeorg-dep2.rst | 4 ++-- src/diffpy/srfit/fitbase/recipeorganizer.py | 10 +++++----- tests/test_fitrecipe.py | 4 ++-- tests/test_recipeorganizer.py | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/news/recipeorg-dep2.rst b/news/recipeorg-dep2.rst index 45e9f19d..ee4da736 100644 --- a/news/recipeorg-dep2.rst +++ b/news/recipeorg-dep2.rst @@ -3,7 +3,7 @@ * Added ``add_constraint`` method to ``RecipeOrganizer``. * Added ``remove_constraint`` method to ``RecipeOrganizer``. * Added ``add_soft_bounds`` method to ``RecipeOrganizer``. -* Added ``remove_penalty`` method to ``RecipeOrganizer``. +* Added ``remove_soft_bounds`` method to ``RecipeOrganizer``. * Added ``register_penalty`` method to ``RecipeOrganizer``. * Added ``clear_all_penalties`` method to ``RecipeOrganizer``. * Added ``get_equation_from_string`` method to ``RecipeOrganizer``. @@ -17,7 +17,7 @@ * Deprecated ``constrain`` method of ``RecipeOrganizer``. Use ``add_constraint`` instead. * Deprecated ``unconstrain`` method of ``RecipeOrganizer``. Use ``remove_constraint`` instead. * Deprecated ``restrain`` method of ``RecipeOrganizer``. Use ``add_soft_bounds`` instead. -* Deprecated ``unrestrain`` methods of ``RecipeOrganizer``. Use ``remove_penalty`` instead. +* Deprecated ``unrestrain`` methods of ``RecipeOrganizer``. Use ``remove_soft_bounds`` instead. * Deprecated ``addRestraint`` method of ``RecipeOrganizer``. Use ``register_penalty`` instead. * Deprecate ``clearRestraints`` method of ``RecipeOrganizer``. Use ``clear_all_penalties`` instead. * Deprecated ``equationFromString`` method of ``RecipeOrganizer``. Use ``get_equation_from_string`` instead. diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index ca664be4..32ab497f 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -149,7 +149,7 @@ unrestrain_deprecation_msg = build_deprecation_message( recipeorganizer_base, "unrestrain", - "remove_penalty", + "remove_soft_bounds", removal_version, ) @@ -1273,7 +1273,7 @@ def addRestraint(self, res): self.register_penalty(res) return - def remove_penalty(self, *ress): + def remove_soft_bounds(self, *ress): """Remove a Restraint from the RecipeOrganizer. Attributes @@ -1301,10 +1301,10 @@ def unrestrain(self, *ress): version 4.0.0. Please use - diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.remove_penalty + diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.remove_soft_bounds instead. """ - self.remove_penalty(*ress) + self.remove_soft_bounds(*ress) return def clear_all_penalties(self, recurse=False): @@ -1316,7 +1316,7 @@ def clear_all_penalties(self, recurse=False): Recurse into managed objects and clear all restraints found there as well. """ - self.remove_penalty(*self._restraints) + self.remove_soft_bounds(*self._restraints) if recurse: for msg in filter(_has_clear_restraints, self._iter_managed()): msg.clear_all_penalties(recurse) diff --git a/tests/test_fitrecipe.py b/tests/test_fitrecipe.py index fe8f8352..4da38ab8 100644 --- a/tests/test_fitrecipe.py +++ b/tests/test_fitrecipe.py @@ -247,7 +247,7 @@ def testResidual(self): self.assertAlmostEqual(chi2, dot(res, res)) # Remove the restraint and variable - self.recipe.remove_penalty(r1) + self.recipe.remove_soft_bounds(r1) self.recipe.delete_variable(self.recipe.Avar) self.recipe._ready = False res = self.recipe.residual() @@ -275,7 +275,7 @@ def testResidual(self): self.assertAlmostEqual(chi2, dot(res, res)) # Remove those - self.fitcontribution.remove_penalty(r1) + self.fitcontribution.remove_soft_bounds(r1) self.recipe._ready = False self.fitcontribution.unconstrain(self.fitcontribution.c) self.fitcontribution.c.set_value(0) diff --git a/tests/test_recipeorganizer.py b/tests/test_recipeorganizer.py index d10441c2..08d46dec 100644 --- a/tests/test_recipeorganizer.py +++ b/tests/test_recipeorganizer.py @@ -349,7 +349,7 @@ def test_add_restraint(self): self.assertEqual(1, len(self.m._restraints)) p2.set_value(10) self.assertEqual(1, r.penalty()) - self.m.remove_penalty(r) + self.m.remove_soft_bounds(r) self.assertEqual(0, len(self.m._restraints)) r = self.m.restrain(p1, upper_bound=10) From e67595da6bcd3036c6bf16d368cf4827feae5c4d Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 13 Mar 2026 11:41:09 -0400 Subject: [PATCH 18/21] register_penalty --> register_soft_bounds --- news/recipeorg-dep2.rst | 4 ++-- src/diffpy/srfit/fitbase/recipeorganizer.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/news/recipeorg-dep2.rst b/news/recipeorg-dep2.rst index ee4da736..b30655c3 100644 --- a/news/recipeorg-dep2.rst +++ b/news/recipeorg-dep2.rst @@ -4,7 +4,7 @@ * Added ``remove_constraint`` method to ``RecipeOrganizer``. * Added ``add_soft_bounds`` method to ``RecipeOrganizer``. * Added ``remove_soft_bounds`` method to ``RecipeOrganizer``. -* Added ``register_penalty`` method to ``RecipeOrganizer``. +* Added ``register_soft_bounds`` method to ``RecipeOrganizer``. * Added ``clear_all_penalties`` method to ``RecipeOrganizer``. * Added ``get_equation_from_string`` method to ``RecipeOrganizer``. @@ -18,7 +18,7 @@ * Deprecated ``unconstrain`` method of ``RecipeOrganizer``. Use ``remove_constraint`` instead. * Deprecated ``restrain`` method of ``RecipeOrganizer``. Use ``add_soft_bounds`` instead. * Deprecated ``unrestrain`` methods of ``RecipeOrganizer``. Use ``remove_soft_bounds`` instead. -* Deprecated ``addRestraint`` method of ``RecipeOrganizer``. Use ``register_penalty`` instead. +* Deprecated ``addRestraint`` method of ``RecipeOrganizer``. Use ``register_soft_bounds`` instead. * Deprecate ``clearRestraints`` method of ``RecipeOrganizer``. Use ``clear_all_penalties`` instead. * Deprecated ``equationFromString`` method of ``RecipeOrganizer``. Use ``get_equation_from_string`` instead. diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index 32ab497f..f00dcbc0 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -121,7 +121,7 @@ addRestraint_deprecation_msg = build_deprecation_message( recipeorganizer_base, "addRestraint", - "register_penalty", + "register_soft_bounds", removal_version, ) @@ -1219,7 +1219,7 @@ def add_soft_bounds( # Make and store the restraint param_or_eq = Restraint(eq, lower_bound, upper_bound, sig, scaled) param_or_eq.eqstr = eqstr - self.register_penalty(param_or_eq) + self.register_soft_bounds(param_or_eq) return param_or_eq @deprecated(restrain_deprecation_msg) @@ -1248,7 +1248,7 @@ def restrain( params=params, ) - def register_penalty(self, res): + def register_soft_bounds(self, res): """Add a Restraint instance to the RecipeOrganizer. Parameters @@ -1267,10 +1267,10 @@ def addRestraint(self, res): version 4.0.0. Please use - diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.register_penalty + diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.register_soft_bounds instead. """ - self.register_penalty(res) + self.register_soft_bounds(res) return def remove_soft_bounds(self, *ress): @@ -1280,7 +1280,7 @@ def remove_soft_bounds(self, *ress): ---------- *ress : Restraint The Restraints returned from the 'add_soft_bounds' method or added - with the 'register_penalty' method. + with the 'register_soft_bounds' method. """ update = False restuple = tuple(self._restraints) From 680fee45156e0bfff5a726eadad1f3340ea29cf5 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 13 Mar 2026 11:42:26 -0400 Subject: [PATCH 19/21] clear_all_penalties --> clear_all_soft_bounds --- news/recipeorg-dep2.rst | 4 ++-- src/diffpy/srfit/fitbase/recipeorganizer.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/news/recipeorg-dep2.rst b/news/recipeorg-dep2.rst index b30655c3..bcd8a3b0 100644 --- a/news/recipeorg-dep2.rst +++ b/news/recipeorg-dep2.rst @@ -5,7 +5,7 @@ * Added ``add_soft_bounds`` method to ``RecipeOrganizer``. * Added ``remove_soft_bounds`` method to ``RecipeOrganizer``. * Added ``register_soft_bounds`` method to ``RecipeOrganizer``. -* Added ``clear_all_penalties`` method to ``RecipeOrganizer``. +* Added ``clear_all_soft_bounds`` method to ``RecipeOrganizer``. * Added ``get_equation_from_string`` method to ``RecipeOrganizer``. **Changed:** @@ -19,7 +19,7 @@ * Deprecated ``restrain`` method of ``RecipeOrganizer``. Use ``add_soft_bounds`` instead. * Deprecated ``unrestrain`` methods of ``RecipeOrganizer``. Use ``remove_soft_bounds`` instead. * Deprecated ``addRestraint`` method of ``RecipeOrganizer``. Use ``register_soft_bounds`` instead. -* Deprecate ``clearRestraints`` method of ``RecipeOrganizer``. Use ``clear_all_penalties`` instead. +* Deprecate ``clearRestraints`` method of ``RecipeOrganizer``. Use ``clear_all_soft_bounds`` instead. * Deprecated ``equationFromString`` method of ``RecipeOrganizer``. Use ``get_equation_from_string`` instead. **Removed:** diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index f00dcbc0..36f8c9b2 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -156,7 +156,7 @@ clearRestraints_deprecation_msg = build_deprecation_message( recipeorganizer_base, "clearRestraints", - "clear_all_penalties", + "clear_all_soft_bounds", removal_version, ) @@ -1307,7 +1307,7 @@ def unrestrain(self, *ress): self.remove_soft_bounds(*ress) return - def clear_all_penalties(self, recurse=False): + def clear_all_soft_bounds(self, recurse=False): """Clear all restraints. Attributes @@ -1319,7 +1319,7 @@ def clear_all_penalties(self, recurse=False): self.remove_soft_bounds(*self._restraints) if recurse: for msg in filter(_has_clear_restraints, self._iter_managed()): - msg.clear_all_penalties(recurse) + msg.clear_all_soft_bounds(recurse) return @deprecated(clearRestraints_deprecation_msg) @@ -1328,10 +1328,10 @@ def clearRestraints(self, recurse=False): version 4.0.0. Please use - diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.clear_all_penalties + diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.clear_all_soft_bounds instead. """ - self.clear_all_penalties(recurse=recurse) + self.clear_all_soft_bounds(recurse=recurse) return def _get_constraints(self, recurse=True): @@ -1606,7 +1606,7 @@ def _has_clear_constraints(msg): def _has_clear_restraints(msg): - return hasattr(msg, "clear_all_penalties") + return hasattr(msg, "clear_all_soft_bounds") def _has_get_restraints(msg): From 8c3618cc972d85394fdbcfd79c6334f0919655b6 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 13 Mar 2026 12:08:47 -0400 Subject: [PATCH 20/21] add more to add_soft_bounds docstring --- src/diffpy/srfit/fitbase/recipeorganizer.py | 32 +++++++++++++++------ 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index 36f8c9b2..dc7e8543 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -1167,21 +1167,22 @@ def add_soft_bounds( Parameters ---------- - param_or_eq : str or Parameter - The equation string or a Parameter object to restrain. + param_or_eq : str + The equation or parameter to restrain. lower_bound : float, optional The lower bound for the restraint evaluation (default is -inf). upper_bound : float, optional The upper bound for the restraint evaluation (default is inf). sig : float, optional The uncertainty associated with the bounds (default is 1). + Please see Notes for how this is used in the penalty calculation. scaled : bool, optional If True, the restraint penalty is scaled by the unrestrained point-average chi^2 (chi^2/numpoints) (default is False). params : dict, optional - The dictionary of Parameters, indexed by name, that are used in the - equation string but are not part of the RecipeOrganizer - (default is {}). + The dictionary of Parameters, indexed by name, that are used in + `param_or_eq` (if an equation string is used) but are not part + of the RecipeOrganizer (default is {}). Returns ------- @@ -1196,18 +1197,33 @@ def add_soft_bounds( .. (max(0, lower_bound - val, val - upper_bound) / sig) ** 2 - where `val` is the value of the evaluated equation. + where `val` is the value of the evaluated `param_or_eq`. If `scaled` is True, this penalty is multiplied by the average chi^2. + Examples + -------- + Restraining the lattice parameters of an Ni lattice to be + approximately 7.4Å (2x the original lattice param) + can be done with the following code: + .. + recipe.add_soft_bounds( + "a_ni + b_ni", + lower_bound=7.0, + upper_bound=7.5, + sig=0.1, + scaled=True, + params={"b_ni": Parameter("b_ni", 3.473)} + ) + Raises ------ ValueError - If `func_params` contains a name that is already used + If `params` contains a name that is already used for a Parameter. ValueError If `param_or_eq` depends on a Parameter that is not part of the - RecipeOrganizer and is not defined in `func_params`. + RecipeOrganizer and is not defined in `params`. """ if isinstance(param_or_eq, str): eqstr = param_or_eq From c11547896b0cfe46d6bbcfb5e0fba70e6b560ddc Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 13 Mar 2026 12:09:39 -0400 Subject: [PATCH 21/21] more to docstring --- src/diffpy/srfit/fitbase/recipeorganizer.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index dc7e8543..a3d442f7 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -1165,6 +1165,8 @@ def add_soft_bounds( ): """Restrain an expression to specified bounds. + See Notes for how the penalty is calculated. + Parameters ---------- param_or_eq : str