From d6e29b7b95555b3d857d49dc663494149bfc2838 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 6 Mar 2026 14:51:43 -0500 Subject: [PATCH 01/13] iterPars deprecation --- src/diffpy/srfit/fitbase/fitrecipe.py | 2 +- src/diffpy/srfit/fitbase/parameterset.py | 2 +- src/diffpy/srfit/fitbase/recipeorganizer.py | 48 +++++++++++++++++---- tests/test_pdf.py | 4 +- 4 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src/diffpy/srfit/fitbase/fitrecipe.py b/src/diffpy/srfit/fitbase/fitrecipe.py index 0aff6ecd..3000aae4 100644 --- a/src/diffpy/srfit/fitbase/fitrecipe.py +++ b/src/diffpy/srfit/fitbase/fitrecipe.py @@ -622,7 +622,7 @@ def __verify_parameters(self): # Get all parameters with a value of None badpars = [] - for par in self.iterPars(): + for par in self.iterate_over_parameters(): try: par.getValue() except ValueError: diff --git a/src/diffpy/srfit/fitbase/parameterset.py b/src/diffpy/srfit/fitbase/parameterset.py index e5e51e39..6ebc4fc7 100644 --- a/src/diffpy/srfit/fitbase/parameterset.py +++ b/src/diffpy/srfit/fitbase/parameterset.py @@ -156,7 +156,7 @@ def setConst(self, const=True): Flag indicating if the parameter is constant (default True). """ - for par in self.iterPars(): + for par in self.iterate_over_parameters(): par.setConst(const) return diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index 3daf5674..6d0878e1 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -61,6 +61,13 @@ removal_version, ) +iterPars_deprecation_msg = build_deprecation_message( + recipecontainer_base, + "iterPars", + "iterate_over_parameters", + removal_version, +) + class RecipeContainer(Observable, Configurable, Validatable): """Base class for organizing pieces of a FitRecipe. @@ -134,16 +141,30 @@ def _iter_managed(self): """Get iterator over managed objects.""" return chain(*(d.values() for d in self.__managed)) - def iterPars(self, pattern="", recurse=True): + def iterate_over_parameters(self, pattern="", recurse=True): """Iterate over the Parameters contained in this object. Parameters ---------- - pattern : str - Iterate over parameters with names matching this regular - expression (all parameters by default). - recurse : bool - Recurse into managed objects when True (default). + pattern : str, optional + The regular expression pattern to match parameter + names against. Only parameters with names matching + this pattern will be returned. Default is an empty + string, which matches all parameter names. + recurse : bool, optional + The flag indicating whether to recurse into managed + objects when iterating over parameters. If True + (default), the method will also iterate over + parameters in managed sub-objects. If False, only + top-level parameters will be iterated over. + + Example + ------- + + .. + for param in recipe.iterate_over_parameters(pattern="scale_"): + # print the name and value of parameters containing "scale_" + print(f"{param.name}={param.value}") """ regexp = re.compile(pattern) for par in list(self._parameters.values()): @@ -156,11 +177,22 @@ def iterPars(self, pattern="", recurse=True): managed.remove(self._parameters) for m in managed: for obj in m.values(): - if hasattr(obj, "iterPars"): - for par in obj.iterPars(pattern=pattern): + if hasattr(obj, "iterate_over_parameters"): + for par in obj.iterate_over_parameters(pattern=pattern): yield par return + @deprecated(iterPars_deprecation_msg) + def iterPars(self, pattern="", recurse=True): + """This function has been deprecated and will be removed in + version 4.0.0. + + Please use + diffpy.srfit.fitbase.recipeorganizer.RecipeContainer.iterate_over_parameters + instead. + """ + return self.iterate_over_parameters(pattern=pattern, recurse=recurse) + def __iter__(self): """Iterate over top-level parameters.""" return iter(self._parameters.values()) diff --git a/tests/test_pdf.py b/tests/test_pdf.py index b46fa2a6..9b528f5d 100644 --- a/tests/test_pdf.py +++ b/tests/test_pdf.py @@ -308,7 +308,9 @@ def test_pickling( pc2 = pickle.loads(pickle.dumps(pc)) res0 = pc.residual() assert numpy.array_equal(res0, pc2.residual()) - for p in chain(pc.iterPars("Uiso"), pc2.iterPars("Uiso")): + for p in chain( + pc.iterate_over_parameters("Uiso"), pc2.iterate_over_parameters("Uiso") + ): p.value = 0.004 res1 = pc.residual() assert not numpy.allclose(res0, res1) From ffe76bf28e15cff1ecc2759e1de8610ddf0f0f95 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 6 Mar 2026 15:00:07 -0500 Subject: [PATCH 02/13] registerCalculator deprecation --- docs/examples/nppdfsas.py | 2 +- src/diffpy/srfit/fitbase/calculator.py | 2 +- src/diffpy/srfit/fitbase/recipeorganizer.py | 38 ++++++++++++----- tests/test_recipeorganizer.py | 45 +++++++++++++++++++++ 4 files changed, 76 insertions(+), 11 deletions(-) diff --git a/docs/examples/nppdfsas.py b/docs/examples/nppdfsas.py index 6626c603..2117ac58 100644 --- a/docs/examples/nppdfsas.py +++ b/docs/examples/nppdfsas.py @@ -87,7 +87,7 @@ def makeRecipe(ciffile, grdata, iqdata): # Register the calculator with the pdf contribution and define the fitting # equation. - pdfcontribution.registerCalculator(cfcalculator) + pdfcontribution.register_calculator(cfcalculator) # The PDF for a nanoscale crystalline is approximated by # Gnano = f * Gcryst pdfcontribution.set_equation("f * G") diff --git a/src/diffpy/srfit/fitbase/calculator.py b/src/diffpy/srfit/fitbase/calculator.py index 22e96540..41ebde60 100644 --- a/src/diffpy/srfit/fitbase/calculator.py +++ b/src/diffpy/srfit/fitbase/calculator.py @@ -20,7 +20,7 @@ overloaded to accept external arguments. Calculators are used to wrap registered functions so that the function's Parameters are contained in an object specific to the function. A custom Calculator can be added to -another RecipeOrganizer with the 'registerCalculator' method. +another RecipeOrganizer with the 'register_calculator' method. """ __all__ = ["Calculator"] diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index 6d0878e1..c68e02ef 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -68,6 +68,15 @@ removal_version, ) +recipeorganizer_base = "diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer" + +registerCalculator_deprecation_msg = build_deprecation_message( + recipeorganizer_base, + "registerCalculator", + "register_calculator", + removal_version, +) + class RecipeContainer(Observable, Configurable, Validatable): """Base class for organizing pieces of a FitRecipe. @@ -545,7 +554,7 @@ def _remove_parameter(self, par): self._eqfactory.deRegisterBuilder(par.name) return - def registerCalculator(self, f, argnames=None): + def register_calculator(self, calculator, argnames=None): """Register a Calculator so it can be used within equation strings. @@ -557,18 +566,18 @@ def registerCalculator(self, f, argnames=None): Attributes ---------- - f + calculator : Calculator object The Calculator to register. - argnames - The names of the arguments to f (list or None). + argnames : list or None, optional + The names of the arguments to calculator (list or None). If this is None, then the argument names will be extracted from the function. """ - self._eqfactory.registerOperator(f.name, f) - self._add_object(f, self._calculators) + self._eqfactory.registerOperator(calculator.name, calculator) + self._add_object(calculator, self._calculators) # Register arguments of the calculator if argnames is None: - fncode = f.__call__.__func__.__code__ + fncode = calculator.__call__.__func__.__code__ argnames = list(fncode.co_varnames) argnames = argnames[1 : fncode.co_argcount] @@ -577,12 +586,23 @@ def registerCalculator(self, f, argnames=None): par = self._new_parameter(pname, 0) else: par = self.get(pname) - f.addLiteral(par) + calculator.addLiteral(par) # Now return an equation object - eq = self._eqfactory.makeEquation(f.name) + eq = self._eqfactory.makeEquation(calculator.name) return eq + @deprecated(registerCalculator_deprecation_msg) + def registerCalculator(self, f, argnames=None): + """This function has been deprecated and will be removed in + version 4.0.0. + + Please use + diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.register_calculator + instead. + """ + return self.register_calculator(f, argnames=argnames) + def registerFunction(self, f, name=None, argnames=None): """Register a function so it can be used within equation strings. diff --git a/tests/test_recipeorganizer.py b/tests/test_recipeorganizer.py index a5db1e1b..cd55ccd4 100644 --- a/tests/test_recipeorganizer.py +++ b/tests/test_recipeorganizer.py @@ -377,6 +377,51 @@ def testGetRestraints(self): self.assertEqual(2, len(res)) return + def test_register_calculator(self): + + class GCalc(Calculator): + + def __init__(self, name): + Calculator.__init__(self, name) + self.newParameter("A", 1.0) + self.newParameter("center", 0.0) + self.newParameter("width", 0.1) + return + + def __call__(self, x): + A = self.A.getValue() + c = self.center.getValue() + w = self.width.getValue() + return A * numpy.exp(-0.5 * ((x - c) / w) ** 2) + + # End class GCalc + + g = GCalc("g") + + self.m.register_calculator(g) + + x = numpy.arange(0.5, 10, 0.5) + self.m.x.setValue(x) + + self.m.g.center.setValue(3.0) + + self.assertTrue( + numpy.array_equal(numpy.exp(-0.5 * ((x - 3.0) / 0.1) ** 2), g(x)) + ) + + self.m.g.center.setValue(5.0) + + self.assertTrue( + numpy.array_equal(numpy.exp(-0.5 * ((x - 5.0) / 0.1) ** 2), g(x)) + ) + + # Use this in another equation + + eq = self.m.registerStringFunction("g/x - 1", "pdf") + self.assertTrue(numpy.array_equal(g(x) / x - 1, eq())) + + return + def testRegisterCalculator(self): class GCalc(Calculator): From ecec4b198b19ed3acf08dd690a9d38996a469cc0 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 6 Mar 2026 15:20:37 -0500 Subject: [PATCH 03/13] registerFunction deprecation in EquationFactory and RecipeOrganizer --- docs/examples/coreshellnp.py | 4 +- docs/examples/debyemodel.py | 6 +- docs/examples/npintensity.py | 2 +- docs/examples/npintensityII.py | 4 +- docs/examples/nppdfcrystal.py | 2 +- docs/examples/threedoublepeaks.py | 8 +- src/diffpy/srfit/equation/builder.py | 40 ++++++-- src/diffpy/srfit/fitbase/fitcontribution.py | 2 +- src/diffpy/srfit/fitbase/recipeorganizer.py | 96 ++++++++++++------- .../srfit/pdf/characteristicfunctions.py | 2 +- tests/test_builder.py | 6 +- tests/test_contribution.py | 4 +- tests/test_recipeorganizer.py | 6 +- 13 files changed, 118 insertions(+), 64 deletions(-) diff --git a/docs/examples/coreshellnp.py b/docs/examples/coreshellnp.py index afb2a339..430726b1 100644 --- a/docs/examples/coreshellnp.py +++ b/docs/examples/coreshellnp.py @@ -76,8 +76,8 @@ def makeRecipe(stru1, stru2, datname): # very little to the PDF. from diffpy.srfit.pdf.characteristicfunctions import shellCF, sphericalCF - contribution.registerFunction(sphericalCF, name="f_CdS") - contribution.registerFunction(shellCF, name="f_ZnS") + contribution.register_function(sphericalCF, name="f_CdS") + contribution.register_function(shellCF, name="f_ZnS") # Write the fitting equation. We want to sum the PDFs from each phase and # multiply it by a scaling factor. diff --git a/docs/examples/debyemodel.py b/docs/examples/debyemodel.py index 3ec7002d..cd2dcbbd 100644 --- a/docs/examples/debyemodel.py +++ b/docs/examples/debyemodel.py @@ -106,14 +106,14 @@ def makeRecipe(): contribution.set_profile(profile, xname="T") # We now need to create the fitting equation. We tell the FitContribution - # to use the 'debye' function defined below. The 'registerFunction' method + # to use the 'debye' function defined below. The 'register_function' method # will let us do this. Since we haven't told it otherwise, - # 'registerFunction' will extract the name of the function ('debye') and + # 'register_function' will extract the name of the function ('debye') and # the names of the arguments ('T', 'm', 'thetaD'). These arguments will # become Parameters of the FitContribution. Since we named the x-variable # 'T' above, the 'T' in the 'debye' equation will refer to this x-variable # whenever it is used. - contribution.registerFunction(debye) + contribution.register_function(debye) # Now we can create the fitting equation. We want to extend the 'debye' # equation by adding a vertical offset. We could wrap 'debye' in a new diff --git a/docs/examples/npintensity.py b/docs/examples/npintensity.py index fce0e7bd..23c23ffa 100644 --- a/docs/examples/npintensity.py +++ b/docs/examples/npintensity.py @@ -247,7 +247,7 @@ def gaussian(q, q0, width): # This registers the python function and extracts the name and creates # Parameters from the arguments. - contribution.registerFunction(gaussian) + contribution.register_function(gaussian) # Center the Gaussian so it is not truncated. contribution.q0.value = x[len(x) // 2] diff --git a/docs/examples/npintensityII.py b/docs/examples/npintensityII.py index 2791a1b3..c5683338 100644 --- a/docs/examples/npintensityII.py +++ b/docs/examples/npintensityII.py @@ -119,8 +119,8 @@ def gaussian(q, q0, width): * exp(-0.5 * ((q - q0) / width) ** 2) ) - contribution1.registerFunction(gaussian) - contribution2.registerFunction(gaussian) + contribution1.register_function(gaussian) + contribution2.register_function(gaussian) # Center the gaussian contribution1.q0.value = x[len(x) // 2] contribution2.q0.value = x[len(x) // 2] diff --git a/docs/examples/nppdfcrystal.py b/docs/examples/nppdfcrystal.py index a8e0dc9d..496a1104 100644 --- a/docs/examples/nppdfcrystal.py +++ b/docs/examples/nppdfcrystal.py @@ -59,7 +59,7 @@ def makeRecipe(ciffile, grdata): # Register the nanoparticle shape factor. from diffpy.srfit.pdf.characteristicfunctions import sphericalCF - pdfcontribution.registerFunction(sphericalCF, name="f") + pdfcontribution.register_function(sphericalCF, name="f") # Now we set up the fitting equation. pdfcontribution.set_equation("f * G") diff --git a/docs/examples/threedoublepeaks.py b/docs/examples/threedoublepeaks.py index d6c70202..960820c4 100644 --- a/docs/examples/threedoublepeaks.py +++ b/docs/examples/threedoublepeaks.py @@ -59,7 +59,7 @@ def makeRecipe(): def gaussian(t, mu, sig): return 1 / (2 * pi * sig**2) ** 0.5 * exp(-0.5 * ((t - mu) / sig) ** 2) - contribution.registerFunction(gaussian, name="peakshape") + contribution.register_function(gaussian, name="peakshape") def delta(t, mu): """Calculate a delta-function. @@ -70,7 +70,7 @@ def delta(t, mu): sig = t[1] - t[0] return gaussian(t, mu, sig) - contribution.registerFunction(delta) + contribution.register_function(delta) # Here is another one bkgdstr = "b0 + b1*t + b2*t**2 + b3*t**3 + b4*t**4 + b5*t**5 + b6*t**6" @@ -120,7 +120,7 @@ def peakloc(mu): l2 = 1.0 return 180 / pi * arcsin(pi / 180 * l2 * sin(mu) / l1) - recipe.registerFunction(peakloc) + recipe.register_function(peakloc) recipe.constrain(contribution.mu12, "peakloc(mu11)") recipe.constrain(contribution.mu22, "peakloc(mu21)") recipe.constrain(contribution.mu32, "peakloc(mu31)") @@ -134,7 +134,7 @@ def sig(sig0, dsig, mu): """Calculate the peak broadening with respect to position.""" return sig0 * (1 - dsig * mu**2) - recipe.registerFunction(sig) + recipe.register_function(sig) recipe.fix("mu") # Now constrain the peak widths to this recipe.sig0.value = 0.001 diff --git a/src/diffpy/srfit/equation/builder.py b/src/diffpy/srfit/equation/builder.py index 87a56b63..0c9ffafb 100644 --- a/src/diffpy/srfit/equation/builder.py +++ b/src/diffpy/srfit/equation/builder.py @@ -106,6 +106,16 @@ _builders = {} +EquationFactory_base = "diffpy.srfit.equation.builder.EquationFactory" +removal_version = "4.0.0" + +registerFunction_dep_msg = build_deprecation_message( + EquationFactory_base, + "registerFunction", + "register_function", + removal_version, +) + class EquationFactory(object): """A Factory for equations. @@ -208,23 +218,26 @@ def registerOperator(self, name, op): opbuilder = wrapOperator(name, op) return self.registerBuilder(name, opbuilder) - def registerFunction(self, name, func, argnames): + def register_function(self, name, func, argnames): """Register a named function with the factory. This will register a builder for the function. - Attributes + Parameters ---------- - name + name : str The name of the function - func - A callable python object - argnames + func : callable + The callable python object + argnames : list of str The argument names for func. If these names do not correspond to builders, then new constants with value 0 will be created for each name. - Returns the registered builder. + Returns + ------- + registered_builder : OperatorBuilder + The registered builder. """ for n in argnames: if n not in self.builders: @@ -234,8 +247,19 @@ def registerFunction(self, name, func, argnames): builder = self.builders[argname] argliteral = builder.literal opbuilder.literal.addLiteral(argliteral) + registered_builder = self.registerBuilder(name, opbuilder) + return registered_builder - return self.registerBuilder(name, opbuilder) + @deprecated(registerFunction_dep_msg) + def registerFunction(self, name, func, argnames): + """This function has been deprecated and will be removed in + version 4.0.0. + + Please use + diffpy.srfit.equation.builder.EquationFactory.register_function + instead. + """ + return self.register_function(name, func, argnames) def registerBuilder(self, name, builder): """Register builder in this module so it can be used in diff --git a/src/diffpy/srfit/fitbase/fitcontribution.py b/src/diffpy/srfit/fitbase/fitcontribution.py index 21a7aed9..cdb39862 100644 --- a/src/diffpy/srfit/fitbase/fitcontribution.py +++ b/src/diffpy/srfit/fitbase/fitcontribution.py @@ -283,7 +283,7 @@ def set_equation(self, eqstr, ns={}): eqstr A string representation of the equation. Any Parameter registered by addParameter or setProfile, or function - registered by setCalculator, registerFunction or + registered by setCalculator, register_function or registerStringFunction can be can be used in the equation by name. Other names will be turned into Parameters of this FitContribution. diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index c68e02ef..f3c27d57 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -77,6 +77,13 @@ removal_version, ) +registerFunction_deprecation_msg = build_deprecation_message( + recipeorganizer_base, + "registerFunction", + "register_function", + removal_version, +) + class RecipeContainer(Observable, Configurable, Validatable): """Base class for organizing pieces of a FitRecipe. @@ -603,52 +610,62 @@ def registerCalculator(self, f, argnames=None): """ return self.register_calculator(f, argnames=argnames) - def registerFunction(self, f, name=None, argnames=None): + def register_function(self, function, name=None, argnames=None): """Register a function so it can be used within equation strings. This creates a function with this class that can be used within string - equations. The resulting equation does not require the arguments to be + equations. The resulting equation does not require the arguments to be passed in the equation string, as this will be handled automatically. - Attributes + Parameters ---------- - f + function : callable The callable to register. If this is an Equation instance, then all that needs to be provided is a name. - name + name : str or None, optional The name of the function to be used in equations. If this is None (default), the method will try to determine the name of the function automatically. - argnames - The names of the arguments to f (list or None). + argnames : list or None, optional + The names of the arguments to 'function' (list or None). If this is None (default), then the argument names will be extracted from the function. - - Note that name and argnames can be extracted from regular python - functions (of type 'function'), bound class methods and callable + Note + ---- + name and argnames can be extracted from regular Python + functions (of type ), bound class methods, and callable classes. + Raises + ------ + TypeError + If name or argnames cannot be automatically extracted. + TypeError + If an automatically extracted name is ''. + ValueError + If function is an Equation object and name is None. - Raises TypeError if name or argnames cannot be automatically - extracted. - Raises TypeError if an automatically extracted name is ''. - Raises ValueError if f is an Equation object and name is None. - - Returns the callable Equation object. + Returns + ------- + equation_object : Equation + The callable Equation object. """ # If the function is an equation, we treat it specially. This is # required so that the objects observed by the root get observed if the # Equation is used within another equation. It is assumed that a plain # function is not observable. - if isinstance(f, Equation): + if isinstance(function, Equation): if name is None: - m = "Equation must be given a name" + m = ( + "The equation must be given a name. " + "Specify a name with the 'name' argument." + ) raise ValueError(m) - self._eqfactory.registerOperator(name, f) - return f + self._eqfactory.registerOperator(name, function) + return function # Introspection code if name is None or argnames is None: @@ -661,15 +678,17 @@ def registerFunction(self, f, name=None, argnames=None): offset = 0 # check regular functions - if inspect.isfunction(f): - fncode = f.__code__ + if inspect.isfunction(function): + fncode = function.__code__ # check class method - elif inspect.ismethod(f): - fncode = f.__func__.__code__ + elif inspect.ismethod(function): + fncode = function.__func__.__code__ offset = 1 # check functor - elif hasattr(f, "__call__") and hasattr(f.__call__, "__func__"): - fncode = f.__call__.__func__.__code__ + elif hasattr(function, "__call__") and hasattr( + function.__call__, "__func__" + ): + fncode = function.__call__.__func__.__code__ offset = 1 else: m = "Cannot extract name or argnames" @@ -697,18 +716,29 @@ def registerFunction(self, f, name=None, argnames=None): # Initialize and register from diffpy.srfit.fitbase.calculator import Calculator - if isinstance(f, Calculator): + if isinstance(function, Calculator): for pname in argnames: par = self.get(pname) - f.addLiteral(par) - self._eqfactory.registerOperator(name, f) + function.addLiteral(par) + self._eqfactory.registerOperator(name, function) else: - self._eqfactory.registerFunction(name, f, argnames) + self._eqfactory.register_function(name, function, argnames) # Now we can create the Equation and return it to the user. - eq = self._eqfactory.makeEquation(name) + equation_object = self._eqfactory.makeEquation(name) - return eq + return equation_object + + @deprecated(registerFunction_deprecation_msg) + def registerFunction(self, f, name=None, argnames=None): + """This function has been deprecated and will be removed in + version 4.0.0. + + Please use + diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.register_function + instead. + """ + return self.register_function(f, name=name, argnames=argnames) def registerStringFunction(self, fstr, name, ns={}): """Register a string function. @@ -747,7 +777,7 @@ def registerStringFunction(self, fstr, name, ns={}): # Register the equation as a callable function. argnames = eq.argdict.keys() - return self.registerFunction(eq, name, argnames) + return self.register_function(eq, name, argnames) def evaluateEquation(self, eqstr, ns={}): """Evaluate a string equation. diff --git a/src/diffpy/srfit/pdf/characteristicfunctions.py b/src/diffpy/srfit/pdf/characteristicfunctions.py index 9846e131..ca24dfc1 100644 --- a/src/diffpy/srfit/pdf/characteristicfunctions.py +++ b/src/diffpy/srfit/pdf/characteristicfunctions.py @@ -21,7 +21,7 @@ function and Gcryst(f) is the crystal PDF. These functions are meant to be imported and added to a FitContribution -using the 'registerFunction' method of that class. +using the 'register_function' method of that class. """ __all__ = [ diff --git a/tests/test_builder.py b/tests/test_builder.py index da1f56f6..2ac09fb4 100644 --- a/tests/test_builder.py +++ b/tests/test_builder.py @@ -97,7 +97,7 @@ def g2(v1): factory.registerArgument("v2", v2) factory.registerArgument("v3", v3) factory.registerArgument("v4", v4) - b = factory.registerFunction("g", g1, ["v1", "v2", "v3", "v4"]) + b = factory.register_function("g", g1, ["v1", "v2", "v3", "v4"]) # Now associate args with the wrapped function op = b.literal @@ -121,7 +121,7 @@ def g2(v1): assert round(abs(24 - eq1()), 7) == 0 # Now swap out the function - b = factory.registerFunction("g", g2, ["v1"]) + b = factory.register_function("g", g2, ["v1"]) op = b.literal assert op.operation == g2 assert v1 in op.args @@ -181,7 +181,7 @@ def testParseEquation(noObserversInGlobalBuilders): assert eq.args == [eq.sigma] # Equation with user-defined functions - factory.registerFunction("myfunc", eq, ["sigma"]) + factory.register_function("myfunc", eq, ["sigma"]) eq2 = factory.makeEquation("c*myfunc(sigma)") assert numpy.allclose(eq2(c=2, sigma=sigma), 2 * gaussian_test(x, sigma)) assert "sigma" in eq2.argdict diff --git a/tests/test_contribution.py b/tests/test_contribution.py index ef388b6b..58571655 100644 --- a/tests/test_contribution.py +++ b/tests/test_contribution.py @@ -192,11 +192,11 @@ def test_releaseOldEquations(self): self.assertEqual(2, len(fc._eqfactory.equations)) return - def test_registerFunction(self): + def test_register_function(self): """Ensure registered function works after second set_equation call.""" fc = self.fitcontribution - fc.registerFunction(_fsquare, name="fsquare") + fc.register_function(_fsquare, name="fsquare") fc.set_equation("fsquare") fc.x.setValue(5) self.assertEqual(25, fc.evaluate()) diff --git a/tests/test_recipeorganizer.py b/tests/test_recipeorganizer.py index cd55ccd4..c5bb29d9 100644 --- a/tests/test_recipeorganizer.py +++ b/tests/test_recipeorganizer.py @@ -476,7 +476,7 @@ def g1(A, c, w, x): def g2(A): return A + 1 - eq = self.m.registerFunction(g1, "g") + eq = self.m.register_function(g1, "g") for p in eq.args: self.assertTrue(p in self.m._parameters.values()) @@ -496,7 +496,7 @@ def g2(A): self.assertTrue(numpy.array_equal(eq() / x - 1, eq2())) # Make sure we can swap out "g". - self.m.registerFunction(g2, "g") + self.m.register_function(g2, "g") self.assertAlmostEqual(2.0, eq()) # Try a bound method @@ -508,7 +508,7 @@ def __call__(self): return 4.56 t = temp() - eq = self.m.registerFunction(t.eval, "eval") + eq = self.m.register_function(t.eval, "eval") self.assertAlmostEqual(1.23, eq()) # Now the callable From b0a714b0e785af1fc3e4066f8034fbff8f0f8720 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 6 Mar 2026 15:31:17 -0500 Subject: [PATCH 04/13] registerStringFunction deprecation --- docs/examples/npintensity.py | 2 +- docs/examples/npintensityII.py | 4 +- docs/examples/threedoublepeaks.py | 2 +- src/diffpy/srfit/fitbase/fitcontribution.py | 2 +- src/diffpy/srfit/fitbase/recipeorganizer.py | 59 +++++++++++++++------ tests/test_recipeorganizer.py | 14 ++--- 6 files changed, 55 insertions(+), 28 deletions(-) diff --git a/docs/examples/npintensity.py b/docs/examples/npintensity.py index 23c23ffa..e05d3fec 100644 --- a/docs/examples/npintensity.py +++ b/docs/examples/npintensity.py @@ -231,7 +231,7 @@ def makeRecipe(strufile, datname): # This creates a callable equation named "bkgd" within the FitContribution, # and turns the polynomial coefficients into Parameters. - contribution.registerStringFunction(bkgdstr, "bkgd") + contribution.register_string_function(bkgdstr, "bkgd") # We will create the broadening function that we need by creating a python # function and registering it with the FitContribution. diff --git a/docs/examples/npintensityII.py b/docs/examples/npintensityII.py index c5683338..19c9c8c7 100644 --- a/docs/examples/npintensityII.py +++ b/docs/examples/npintensityII.py @@ -105,8 +105,8 @@ def makeRecipe(strufile, datname1, datname2): bkgdstr = "b0 + b1*q + b2*q**2 + b3*q**3 + b4*q**4 + b5*q**5 + b6*q**6 +\ b7*q**7 +b8*q**8 + b9*q**9" - contribution1.registerStringFunction(bkgdstr, "bkgd") - contribution2.registerStringFunction(bkgdstr, "bkgd") + contribution1.register_string_function(bkgdstr, "bkgd") + contribution2.register_string_function(bkgdstr, "bkgd") # We will create the broadening function by registering a python function. pi = numpy.pi diff --git a/docs/examples/threedoublepeaks.py b/docs/examples/threedoublepeaks.py index 960820c4..4a3d7eb6 100644 --- a/docs/examples/threedoublepeaks.py +++ b/docs/examples/threedoublepeaks.py @@ -75,7 +75,7 @@ def delta(t, mu): # Here is another one bkgdstr = "b0 + b1*t + b2*t**2 + b3*t**3 + b4*t**4 + b5*t**5 + b6*t**6" - contribution.registerStringFunction(bkgdstr, "bkgd") + contribution.register_string_function(bkgdstr, "bkgd") # Now define our fitting equation. We will hardcode the peak ratios. contribution.set_equation( diff --git a/src/diffpy/srfit/fitbase/fitcontribution.py b/src/diffpy/srfit/fitbase/fitcontribution.py index cdb39862..d5af4709 100644 --- a/src/diffpy/srfit/fitbase/fitcontribution.py +++ b/src/diffpy/srfit/fitbase/fitcontribution.py @@ -284,7 +284,7 @@ def set_equation(self, eqstr, ns={}): A string representation of the equation. Any Parameter registered by addParameter or setProfile, or function registered by setCalculator, register_function or - registerStringFunction can be can be used in the equation + register_string_function can be can be used in the equation by name. Other names will be turned into Parameters of this FitContribution. ns diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index f3c27d57..6ebfd5d1 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -84,6 +84,13 @@ removal_version, ) +registerStringFunction_deprecation_msg = build_deprecation_message( + recipeorganizer_base, + "registerStringFunction", + "register_string_function", + removal_version, +) + class RecipeContainer(Observable, Configurable, Validatable): """Base class for organizing pieces of a FitRecipe. @@ -740,35 +747,41 @@ def registerFunction(self, f, name=None, argnames=None): """ return self.register_function(f, name=name, argnames=argnames) - def registerStringFunction(self, fstr, name, ns={}): + def register_string_function(self, function_str, name, func_params={}): """Register a string function. This creates a function with this class that can be used within string - equations. The resulting equation does not require the arguments to be + equations. The resulting equation does not require the arguments to be passed in the function string, as this will be handled automatically. - Attributes + Parameters ---------- - fstr + function_str : str A string equation to register. - name + name : str The name of the function to be used in equations. - ns + func_params : dict, optional A dictionary of Parameters, indexed by name, that are - used in fstr, but not part of the FitRecipe (default - {}). + used in function_str, but not part of the FitRecipe (default {}). + Raises + ------ + ValueError + If func_params uses a name that is already used for another + managed object. + ValueError + If the function name is the name of another managed object. - Raises ValueError if ns uses a name that is already used for another - managed object. - Raises ValueError if the function name is the name of another managed - object. - - Returns the callable Equation object. + Returns + ------- + equation_object : Equation + The callable Equation object. """ # Build the equation instance. - eq = equationFromString(fstr, self._eqfactory, ns=ns, buildargs=True) + eq = equationFromString( + function_str, self._eqfactory, ns=func_params, buildargs=True + ) eq.name = name # Register any new Parameters. @@ -777,7 +790,21 @@ def registerStringFunction(self, fstr, name, ns={}): # Register the equation as a callable function. argnames = eq.argdict.keys() - return self.register_function(eq, name, argnames) + equation_object = self.register_function( + eq, name=name, argnames=argnames + ) + return equation_object + + @deprecated(registerStringFunction_deprecation_msg) + def registerStringFunction(self, fstr, name, ns={}): + """This function has been deprecated and will be removed in + version 4.0.0. + + Please use + diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.register_string_function + instead. + """ + return self.register_string_function(fstr, name, func_params=ns) def evaluateEquation(self, eqstr, ns={}): """Evaluate a string equation. diff --git a/tests/test_recipeorganizer.py b/tests/test_recipeorganizer.py index c5bb29d9..a2821a9d 100644 --- a/tests/test_recipeorganizer.py +++ b/tests/test_recipeorganizer.py @@ -417,7 +417,7 @@ def __call__(self, x): # Use this in another equation - eq = self.m.registerStringFunction("g/x - 1", "pdf") + eq = self.m.register_string_function("g/x - 1", "pdf") self.assertTrue(numpy.array_equal(g(x) / x - 1, eq())) return @@ -462,7 +462,7 @@ def __call__(self, x): # Use this in another equation - eq = self.m.registerStringFunction("g/x - 1", "pdf") + eq = self.m.register_string_function("g/x - 1", "pdf") self.assertTrue(numpy.array_equal(g(x) / x - 1, eq())) return @@ -492,7 +492,7 @@ def g2(A): ) # Use this in another equation - eq2 = self.m.registerStringFunction("g/x - 1", "pdf") + eq2 = self.m.register_string_function("g/x - 1", "pdf") self.assertTrue(numpy.array_equal(eq() / x - 1, eq2())) # Make sure we can swap out "g". @@ -517,11 +517,11 @@ def __call__(self): return - def testRegisterStringFunction(self): + def test_register_string_function(self): """Test registering string functions in various ways.""" # Make an equation. - eq1 = self.m.registerStringFunction("x**2 + 3", "eq1") + eq1 = self.m.register_string_function("x**2 + 3", "eq1") eq1.x.setValue(0) for p in eq1.args: @@ -537,9 +537,9 @@ def testRegisterStringFunction(self): # Use eq1 in some equations # x**2 (x**2 + 3 - 3) - eq2 = self.m.registerStringFunction("eq1 - 3", "eq2") + eq2 = self.m.register_string_function("eq1 - 3", "eq2") # y**2 - eq3 = self.m.registerStringFunction("eq1(y) - 3", "eq3") + eq3 = self.m.register_string_function("eq1(y) - 3", "eq3") # Test these equations. self.assertEqual(0, eq2()) From 1d91a580455419904341179af8c5deaa9be2be5b Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 6 Mar 2026 16:34:03 -0500 Subject: [PATCH 05/13] evaluateEquation deprecation --- docs/examples/debyemodelII.py | 6 +-- docs/examples/npintensity.py | 2 +- docs/examples/npintensityII.py | 4 +- docs/examples/nppdfcrystal.py | 4 +- docs/examples/nppdfsas.py | 4 +- src/diffpy/srfit/fitbase/recipeorganizer.py | 55 ++++++++++++++++++--- tests/test_recipeorganizer.py | 7 +++ 7 files changed, 64 insertions(+), 18 deletions(-) diff --git a/docs/examples/debyemodelII.py b/docs/examples/debyemodelII.py index 5e82aaee..4817d11d 100644 --- a/docs/examples/debyemodelII.py +++ b/docs/examples/debyemodelII.py @@ -107,14 +107,14 @@ def plotResults(recipe): recipe.highT.profile.set_calculation_range(xmin="obs", xmax="obs") T = recipe.lowT.profile.x U = recipe.lowT.profile.y - # We can use a FitContribution's 'evaluateEquation' method to evaluate + # We can use a FitContribution's 'evaluate_equation' method to evaluate # expressions involving the Parameters and other aspects of the # FitContribution. Here we evaluate the fitting equation, which is always # accessed using the name "eq". We access it this way (rather than through # the Profile's ycalc attribute) because we changed the calculation range # above, and we therefore need to recalculate the profile. - lowU = recipe.lowT.evaluateEquation("eq") - highU = recipe.highT.evaluateEquation("eq") + lowU = recipe.lowT.evaluate_equation("eq") + highU = recipe.highT.evaluate_equation("eq") # Now we can plot this. import pylab diff --git a/docs/examples/npintensity.py b/docs/examples/npintensity.py index e05d3fec..b9d33075 100644 --- a/docs/examples/npintensity.py +++ b/docs/examples/npintensity.py @@ -342,7 +342,7 @@ def plotResults(recipe): Imeas = recipe.bucky.profile.y Icalc = recipe.bucky.profile.ycalc - bkgd = recipe.bucky.evaluateEquation("bkgd") + bkgd = recipe.bucky.evaluate_equation("bkgd") diff = Imeas - Icalc import pylab diff --git a/docs/examples/npintensityII.py b/docs/examples/npintensityII.py index 19c9c8c7..92e5e899 100644 --- a/docs/examples/npintensityII.py +++ b/docs/examples/npintensityII.py @@ -196,11 +196,11 @@ def plotResults(recipe): # Plot this for fun. I1 = recipe.bucky1.profile.y Icalc1 = recipe.bucky1.profile.ycalc - bkgd1 = recipe.bucky1.evaluateEquation("bkgd") + bkgd1 = recipe.bucky1.evaluate_equation("bkgd") diff1 = I1 - Icalc1 I2 = recipe.bucky2.profile.y Icalc2 = recipe.bucky2.profile.ycalc - bkgd2 = recipe.bucky2.evaluateEquation("bkgd") + bkgd2 = recipe.bucky2.evaluate_equation("bkgd") diff2 = I2 - Icalc2 offset = 1.2 * max(I2) * numpy.ones_like(I2) I1 += offset diff --git a/docs/examples/nppdfcrystal.py b/docs/examples/nppdfcrystal.py index 496a1104..95cca7e3 100644 --- a/docs/examples/nppdfcrystal.py +++ b/docs/examples/nppdfcrystal.py @@ -91,10 +91,10 @@ def plotResults(recipe): diffzero = -0.8 * max(g) * numpy.ones_like(g) diff = g - gcalc + diffzero - gcryst = recipe.pdf.evaluateEquation("G") + gcryst = recipe.pdf.evaluate_equation("G") gcryst /= recipe.scale.value - fr = recipe.pdf.evaluateEquation("f") + fr = recipe.pdf.evaluate_equation("f") fr *= max(g) / fr[0] import pylab diff --git a/docs/examples/nppdfsas.py b/docs/examples/nppdfsas.py index 2117ac58..69386a8b 100644 --- a/docs/examples/nppdfsas.py +++ b/docs/examples/nppdfsas.py @@ -156,10 +156,10 @@ def plotResults(recipe): diffzero = -0.8 * max(g) * numpy.ones_like(g) diff = g - gcalc + diffzero - gcryst = recipe.pdf.evaluateEquation("G") + gcryst = recipe.pdf.evaluate_equation("G") gcryst /= recipe.scale.value - fr = recipe.pdf.evaluateEquation("f") + fr = recipe.pdf.evaluate_equation("f") fr *= max(g) / fr[0] import pylab diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index 6ebfd5d1..8f322334 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -91,6 +91,13 @@ removal_version, ) +evaluateEquation_deprecation_msg = build_deprecation_message( + recipeorganizer_base, + "evaluateEquation", + "evaluate_equation", + removal_version, +) + class RecipeContainer(Observable, Configurable, Validatable): """Base class for organizing pieces of a FitRecipe. @@ -762,12 +769,12 @@ def register_string_function(self, function_str, name, func_params={}): The name of the function to be used in equations. func_params : dict, optional A dictionary of Parameters, indexed by name, that are - used in function_str, but not part of the FitRecipe (default {}). + used in `function_str`, but not part of the FitRecipe (default {}). Raises ------ ValueError - If func_params uses a name that is already used for another + If `func_params` uses a name that is already used for another managed object. ValueError If the function name is the name of another managed object. @@ -806,6 +813,43 @@ def registerStringFunction(self, fstr, name, ns={}): """ return self.register_string_function(fstr, name, func_params=ns) + def evaluate_equation(self, equation_str, func_params={}): + """Evaluate a string equation. + + This method takes a string representation of a mathematical equation + and evaluates it using the current values of the registered Parameters + in the FitRecipe. Additional parameters not part of the FitRecipe can + also be provided via the `func_params` dictionary. + + Parameters + ---------- + equation_str + The string equation to evaluate. The equation is evaluated at + the current value of the registered Parameters. + func_params : dict, optional + The dictionary of Parameters, indexed by name, that are + used in `equation_str`, but not part of the FitRecipe + (default `{}`). + + Returns + ------- + returned_value : float + The value of the evaluated equation. + + Raises + ------ + ValueError + If `func_params` uses a name that is already used for a + variable. + """ + eq = equationFromString(equation_str, self._eqfactory, func_params) + try: + returned_value = eq() + finally: + self._eqfactory.wipeout(eq) + return returned_value + + @deprecated(evaluateEquation_deprecation_msg) def evaluateEquation(self, eqstr, ns={}): """Evaluate a string equation. @@ -822,12 +866,7 @@ def evaluateEquation(self, eqstr, ns={}): Raises ValueError if ns uses a name that is already used for a variable. """ - eq = equationFromString(eqstr, self._eqfactory, ns) - try: - rv = eq() - finally: - self._eqfactory.wipeout(eq) - return rv + return self.evaluate_equation(eqstr, func_params=ns) def constrain(self, par, con, ns={}): """Constrain a parameter to an equation. diff --git a/tests/test_recipeorganizer.py b/tests/test_recipeorganizer.py index a2821a9d..46eabb1b 100644 --- a/tests/test_recipeorganizer.py +++ b/tests/test_recipeorganizer.py @@ -555,6 +555,13 @@ def test_register_string_function(self): return + def test_release_old_equations(self): + """Verify EquationFactory does not hold temporary equations.""" + self.m._new_parameter("x", 12) + self.assertEqual(36, self.m.evaluate_equation("3 * x")) + self.assertEqual(0, len(self.m._eqfactory.equations)) + return + def test_releaseOldEquations(self): """Verify EquationFactory does not hold temporary equations.""" self.m._new_parameter("x", 12) From 4ea52ec12262981654d91cac083c353cc328c52f Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 6 Mar 2026 16:41:48 -0500 Subject: [PATCH 06/13] isConstrained deprecation --- src/diffpy/srfit/fitbase/fitrecipe.py | 4 +- src/diffpy/srfit/fitbase/recipeorganizer.py | 65 +++++++++++++-------- src/diffpy/srfit/structure/sgconstraints.py | 8 +-- tests/test_recipeorganizer.py | 4 +- 4 files changed, 48 insertions(+), 33 deletions(-) diff --git a/src/diffpy/srfit/fitbase/fitrecipe.py b/src/diffpy/srfit/fitbase/fitrecipe.py index 3000aae4..b91b3bb8 100644 --- a/src/diffpy/srfit/fitbase/fitrecipe.py +++ b/src/diffpy/srfit/fitbase/fitrecipe.py @@ -195,7 +195,7 @@ class FitRecipe(_fitrecipe_interface, RecipeOrganizer): lambda self: [ v.name for v in self._parameters.values() - if not (self.is_free(v) or self.isConstrained(v)) + if not (self.is_free(v) or self.is_constrained(v)) ], doc="names of the fixed refinable variables", ) @@ -204,7 +204,7 @@ class FitRecipe(_fitrecipe_interface, RecipeOrganizer): [ v.value for v in self._parameters.values() - if not (self.is_free(v) or self.isConstrained(v)) + if not (self.is_free(v) or self.is_constrained(v)) ] ), doc="values of the fixed refinable variables", diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index 8f322334..a80d03ae 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -27,7 +27,6 @@ from functools import partial from itertools import chain, groupby -import six from numpy import inf from diffpy.srfit.equation import Equation @@ -98,6 +97,13 @@ removal_version, ) +isConstrained_deprecation_msg = build_deprecation_message( + recipeorganizer_base, + "isConstrained", + "is_constrained", + removal_version, +) + class RecipeContainer(Observable, Configurable, Validatable): """Base class for organizing pieces of a FitRecipe. @@ -851,20 +857,12 @@ def evaluate_equation(self, equation_str, func_params={}): @deprecated(evaluateEquation_deprecation_msg) def evaluateEquation(self, eqstr, ns={}): - """Evaluate a string equation. - - Attributes - ---------- - eqstr - A string equation to evaluate. The equation is evaluated at - the current value of the registered Parameters. - ns - A dictionary of Parameters, indexed by name, that are - used in fstr, but not part of the FitRecipe (default {}). - + """This function has been deprecated and will be removed in + version 4.0.0. - Raises ValueError if ns uses a name that is already used for a - variable. + Please use + diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.evaluate_equation + instead. """ return self.evaluate_equation(eqstr, func_params=ns) @@ -894,7 +892,7 @@ def constrain(self, par, con, ns={}): ns. Raises ValueError if par is marked as constant. """ - if isinstance(par, six.string_types): + if isinstance(par, str): name = par par = self.get(name) if par is None: @@ -906,7 +904,7 @@ def constrain(self, par, con, ns={}): if par.const: raise ValueError("The parameter '%s' is constant" % par) - if isinstance(con, six.string_types): + if isinstance(con, str): eqstr = con eq = equationFromString(con, self._eqfactory, ns) else: @@ -927,19 +925,36 @@ def constrain(self, par, con, ns={}): return - def isConstrained(self, par): + def is_constrained(self, parameter): """Determine if a Parameter is constrained in this object. - Attributes + Parameters ---------- - par + parameter : str or Parameter The name of a Parameter or a Parameter to check. + + Returns + ------- + bool + True if the Parameter is constrained in this object, False + otherwise. """ - if isinstance(par, six.string_types): - name = par - par = self.get(name) + if isinstance(parameter, str): + name = parameter + parameter = self.get(name) - return par in self._constraints + return parameter in self._constraints + + @deprecated(isConstrained_deprecation_msg) + def isConstrained(self, par): + """This function has been deprecated and will be removed in + version 4.0.0. + + Please use + diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.is_constrained + instead. + """ + return self.is_constrained(par) def unconstrain(self, *pars): """Unconstrain a Parameter. @@ -956,7 +971,7 @@ def unconstrain(self, *pars): """ update = False for par in pars: - if isinstance(par, six.string_types): + if isinstance(par, str): name = par par = self.get(name) @@ -1048,7 +1063,7 @@ def restrain(self, res, lb=-inf, ub=inf, sig=1, scaled=False, ns={}): Returns the Restraint object for use with the 'unrestrain' method. """ - if isinstance(res, six.string_types): + if isinstance(res, str): eqstr = res eq = equationFromString(res, self._eqfactory, ns) else: diff --git a/src/diffpy/srfit/structure/sgconstraints.py b/src/diffpy/srfit/structure/sgconstraints.py index 81c71de9..291bb33a 100644 --- a/src/diffpy/srfit/structure/sgconstraints.py +++ b/src/diffpy/srfit/structure/sgconstraints.py @@ -390,7 +390,7 @@ def _clear_constraints(self): for scatterer in scatterers: for par in [scatterer.x, scatterer.y, scatterer.z]: - if scatterer.isConstrained(par): + if scatterer.is_constrained(par): scatterer.unconstrain(par) par.setConst(False) @@ -407,7 +407,7 @@ def _clear_constraints(self): lattice.gamma, ] for par in latpars: - if lattice.isConstrained(par): + if lattice.is_constrained(par): lattice.unconstrain(par) par.setConst(False) @@ -417,14 +417,14 @@ def _clear_constraints(self): if isosymbol: par = scatterer.get(isosymbol) if par is not None: - if scatterer.isConstrained(par): + if scatterer.is_constrained(par): scatterer.unconstrain(par) par.setConst(False) for pname in adpsymbols: par = scatterer.get(pname) if par is not None: - if scatterer.isConstrained(par): + if scatterer.is_constrained(par): scatterer.unconstrain(par) par.setConst(False) diff --git a/tests/test_recipeorganizer.py b/tests/test_recipeorganizer.py index 46eabb1b..f7200557 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 testConstrain(self): + def test_constrain(self): """Test the constrain method.""" p1 = self.m._new_parameter("p1", 1) @@ -273,7 +273,7 @@ def testConstrain(self): self.assertTrue(p1.constrained) self.assertTrue(p1 in self.m._constraints) self.assertEqual(1, len(self.m._constraints)) - self.assertTrue(self.m.isConstrained(p1)) + self.assertTrue(self.m.is_constrained(p1)) p2.setValue(10) self.m._constraints[p1].update() From 1ed19da67a6fca608e228ac58fcbf0215b65c6e9 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 6 Mar 2026 16:44:09 -0500 Subject: [PATCH 07/13] replace par with parameter --- src/diffpy/srfit/fitbase/recipeorganizer.py | 107 ++++++++++---------- 1 file changed, 55 insertions(+), 52 deletions(-) diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index a80d03ae..fd71a512 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -203,9 +203,9 @@ def iterate_over_parameters(self, pattern="", recurse=True): print(f"{param.name}={param.value}") """ regexp = re.compile(pattern) - for par in list(self._parameters.values()): - if regexp.search(par.name): - yield par + for parameter in list(self._parameters.values()): + if regexp.search(parameter.name): + yield parameter if not recurse: return # Iterate over objects within the managed dictionaries. @@ -214,8 +214,10 @@ def iterate_over_parameters(self, pattern="", recurse=True): for m in managed: for obj in m.values(): if hasattr(obj, "iterate_over_parameters"): - for par in obj.iterate_over_parameters(pattern=pattern): - yield par + for parameter in obj.iterate_over_parameters( + pattern=pattern + ): + yield parameter return @deprecated(iterPars_deprecation_msg) @@ -274,11 +276,11 @@ def __dir__(self): def __setattr__(self, name, value): """Parameter access and object checking.""" if name in self._parameters: - par = self._parameters[name] + parameter = self._parameters[name] if isinstance(value, Parameter): - par.value = value.value + parameter.value = value.value else: - par.value = value + parameter.value = value return m = self.get(name) @@ -540,14 +542,14 @@ def _new_parameter(self, name, value, check=True): self._add_parameter(p, check) return p - def _add_parameter(self, par, check=True): + def _add_parameter(self, parameter, check=True): """Store a Parameter. Parameters added in this way are registered with the _eqfactory. Attributes ---------- - par + parameter The Parameter to be stored. check If True (default), a ValueError is raised a Parameter of @@ -560,13 +562,13 @@ def _add_parameter(self, par, check=True): """ # Store the Parameter - RecipeContainer._add_object(self, par, self._parameters, check) + RecipeContainer._add_object(self, parameter, self._parameters, check) # Register the Parameter - self._eqfactory.registerArgument(par.name, par) + self._eqfactory.registerArgument(parameter.name, parameter) return - def _remove_parameter(self, par): + def _remove_parameter(self, parameter): """Remove a parameter. This de-registers the Parameter with the _eqfactory. The @@ -575,10 +577,11 @@ def _remove_parameter(self, par): Note that constraints and restraints involving the Parameter are not modified. - Raises ValueError if par is not part of the RecipeOrganizer. + Raises ValueError if parameter is not part of the + RecipeOrganizer. """ - self._remove_object(par, self._parameters) - self._eqfactory.deRegisterBuilder(par.name) + self._remove_object(parameter, self._parameters) + self._eqfactory.deRegisterBuilder(parameter.name) return def register_calculator(self, calculator, argnames=None): @@ -610,10 +613,10 @@ def register_calculator(self, calculator, argnames=None): for pname in argnames: if pname not in self._eqfactory.builders: - par = self._new_parameter(pname, 0) + parameter = self._new_parameter(pname, 0) else: - par = self.get(pname) - calculator.addLiteral(par) + parameter = self.get(pname) + calculator.addLiteral(parameter) # Now return an equation object eq = self._eqfactory.makeEquation(calculator.name) @@ -738,8 +741,8 @@ def register_function(self, function, name=None, argnames=None): if isinstance(function, Calculator): for pname in argnames: - par = self.get(pname) - function.addLiteral(par) + parameter = self.get(pname) + function.addLiteral(parameter) self._eqfactory.registerOperator(name, function) else: self._eqfactory.register_function(name, function, argnames) @@ -798,8 +801,8 @@ def register_string_function(self, function_str, name, func_params={}): eq.name = name # Register any new Parameters. - for par in self._eqfactory.newargs: - self._add_parameter(par) + for parameter in self._eqfactory.newargs: + self._add_parameter(parameter) # Register the equation as a callable function. argnames = eq.argdict.keys() @@ -866,14 +869,14 @@ def evaluateEquation(self, eqstr, ns={}): """ return self.evaluate_equation(eqstr, func_params=ns) - def constrain(self, par, con, ns={}): + def constrain(self, parameter, con, ns={}): """Constrain a parameter to an equation. Note that only one constraint can exist on a Parameter at a time. Attributes ---------- - par + parameter The name of a Parameter or a Parameter to constrain. con A string representation of the constraint equation or a @@ -888,21 +891,21 @@ def constrain(self, par, con, ns={}): Raises ValueError if ns uses a name that is already used for a variable. - Raises ValueError if par is a string but not part of this object or in - ns. - Raises ValueError if par is marked as constant. + Raises ValueError if parameter is a string but not part of this + object or in ns. + Raises ValueError if parameter is marked as constant. """ - if isinstance(par, str): - name = par - par = self.get(name) - if par is None: - par = ns.get(name) + if isinstance(parameter, str): + name = parameter + parameter = self.get(name) + if parameter is None: + parameter = ns.get(name) - if par is None: + if parameter is None: raise ValueError("The parameter cannot be found") - if par.const: - raise ValueError("The parameter '%s' is constant" % par) + if parameter.const: + raise ValueError("The parameter '%s' is constant" % parameter) if isinstance(con, str): eqstr = con @@ -911,14 +914,14 @@ def constrain(self, par, con, ns={}): eq = Equation(root=con) eqstr = con.name - eq.name = "_constraint_%s" % par.name + eq.name = "_constraint_%s" % parameter.name # Make and store the constraint con = Constraint() - con.constrain(par, eq) + con.constrain(parameter, eq) # Store the equation string so it can be shown later. con.eqstr = eqstr - self._constraints[par] = con + self._constraints[parameter] = con # Our configuration changed self._update_configuration() @@ -946,7 +949,7 @@ def is_constrained(self, parameter): return parameter in self._constraints @deprecated(isConstrained_deprecation_msg) - def isConstrained(self, par): + def isConstrained(self, parameter): """This function has been deprecated and will be removed in version 4.0.0. @@ -954,7 +957,7 @@ def isConstrained(self, par): diffpy.srfit.fitbase.recipeorganizer.RecipeOrganizer.is_constrained instead. """ - return self.is_constrained(par) + return self.is_constrained(parameter) def unconstrain(self, *pars): """Unconstrain a Parameter. @@ -970,17 +973,17 @@ def unconstrain(self, *pars): Raises ValueError if the Parameter is not constrained. """ update = False - for par in pars: - if isinstance(par, str): - name = par - par = self.get(name) + for parameter in pars: + if isinstance(parameter, str): + name = parameter + parameter = self.get(name) - if par is None: + if parameter is None: raise ValueError("The parameter cannot be found") - if par in self._constraints: - self._constraints[par].unconstrain() - del self._constraints[par] + if parameter in self._constraints: + self._constraints[parameter].unconstrain() + del self._constraints[parameter] update = True if update: @@ -1213,13 +1216,13 @@ def _format_constraints(self): cdict = self._get_constraints() # Find each constraint and format the equation clines = [] - for par, con in cdict.items(): - loc = self._locate_managed_object(par) + for parameter, con in cdict.items(): + loc = self._locate_managed_object(parameter) if loc: locstr = ".".join(o.name for o in loc[1:]) clines.append("%s <-- %s" % (locstr, con.eqstr)) else: - clines.append("%s <-- %s" % (par.name, con.eqstr)) + clines.append("%s <-- %s" % (parameter.name, con.eqstr)) clines.sort(key=numstr) return clines From 99f4e143ed3da9deb667b498e6a7d7cf6f6a8baa Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 6 Mar 2026 16:54:37 -0500 Subject: [PATCH 08/13] getConstrainedPars deprecation, and write a test for it :) --- src/diffpy/srfit/fitbase/recipeorganizer.py | 30 +++++++++++++++++++-- tests/test_recipeorganizer.py | 12 +++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index fd71a512..b02d75cb 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -104,6 +104,13 @@ removal_version, ) +getConstrainedPars_deprecation_msg = build_deprecation_message( + recipeorganizer_base, + "getConstrainedPars", + "get_constrained_parmeters", + removal_version, +) + class RecipeContainer(Observable, Configurable, Validatable): """Base class for organizing pieces of a FitRecipe. @@ -996,6 +1003,26 @@ def unconstrain(self, *pars): return + def get_constrained_parmeters(self, recurse=False): + """Get a list of constrained managed Parameters in this object. + + Parameters + ---------- + recurse : bool, optional + If False (default), only constrained Parameters in + this object are returned. If True, constrained + Parameters in managed sub-objects are also included. + + Return + ------ + constrained_params : list + A list of constrained managed Parameters in this object. + """ + const = self._get_constraints(recurse) + constrained_params = list(const.keys()) + return constrained_params + + @deprecated(getConstrainedPars_deprecation_msg) def getConstrainedPars(self, recurse=False): """Get a list of constrained managed Parameters in this object. @@ -1005,8 +1032,7 @@ def getConstrainedPars(self, recurse=False): Recurse into managed objects and retrieve their constrained Parameters as well (default False). """ - const = self._get_constraints(recurse) - return const.keys() + return self.get_constrained_parmeters(recurse=recurse) def clearConstraints(self, recurse=False): """Clear all constraints managed by this organizer. diff --git a/tests/test_recipeorganizer.py b/tests/test_recipeorganizer.py index f7200557..e7366c48 100644 --- a/tests/test_recipeorganizer.py +++ b/tests/test_recipeorganizer.py @@ -270,6 +270,18 @@ def test_constrain(self): self.assertEqual(0, len(self.m._constraints)) self.m.constrain(p1, "2*p2") + actual_constrained_params = self.m.getConstrainedPars() + actual_constrained_params = [p.name for p in actual_constrained_params] + expected_constrained_params = [p1.name] + + assert actual_constrained_params == expected_constrained_params + + actual_constrained_params = self.m.get_constrained_parmeters() + actual_constrained_params = [p.name for p in actual_constrained_params] + expected_constrained_params = [p1.name] + + assert actual_constrained_params == expected_constrained_params + self.assertTrue(p1.constrained) self.assertTrue(p1 in self.m._constraints) self.assertEqual(1, len(self.m._constraints)) From 6aa1e73067178ec39c44caabaf364e8fe6843783 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 6 Mar 2026 16:56:47 -0500 Subject: [PATCH 09/13] small fix --- src/diffpy/srfit/fitbase/recipeorganizer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index b02d75cb..a46e77f0 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -1015,11 +1015,11 @@ def get_constrained_parmeters(self, recurse=False): Return ------ - constrained_params : list + constrained_params : list of Parameter A list of constrained managed Parameters in this object. """ const = self._get_constraints(recurse) - constrained_params = list(const.keys()) + constrained_params = const.keys() return constrained_params @deprecated(getConstrainedPars_deprecation_msg) From 29671add6929effabea46089cbe709458f2fac17 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 6 Mar 2026 17:09:00 -0500 Subject: [PATCH 10/13] clearConstraints deprecation, and write small test --- src/diffpy/srfit/fitbase/recipeorganizer.py | 38 +++++++++++++++------ tests/test_recipeorganizer.py | 20 +++++++++++ 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index a46e77f0..90a8b492 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -111,6 +111,13 @@ removal_version, ) +clearConstraints_deprecation_msg = build_deprecation_message( + recipeorganizer_base, + "clearConstraints", + "clear_all_constraints", + removal_version, +) + class RecipeContainer(Observable, Configurable, Validatable): """Base class for organizing pieces of a FitRecipe. @@ -1034,27 +1041,38 @@ def getConstrainedPars(self, recurse=False): """ return self.get_constrained_parmeters(recurse=recurse) - def clearConstraints(self, recurse=False): + def clear_all_constraints(self, recurse=False): """Clear all constraints managed by this organizer. - Attributes - ---------- - recurse - Recurse into managed objects and clear all constraints - found there as well. - - This removes constraints that are held in this organizer, no matter where the constrained parameters are from. + + Parameters + ---------- + recurse : bool, optional + If False (default), only constraints in this object + are cleared. If True, constraints in managed + sub-objects are also cleared. """ if self._constraints: self.unconstrain(*self._constraints) if recurse: for m in filter(_has_clear_constraints, self._iter_managed()): - m.clearConstraints(recurse) + m.clear_all_constraints(recurse) return + @deprecated(clearConstraints_deprecation_msg) + def clearConstraints(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_constraints + instead. + """ + return self.clear_all_constraints(recurse=recurse) + def restrain(self, res, lb=-inf, ub=inf, sig=1, scaled=False, ns={}): """Restrain an expression to specified bounds. @@ -1392,7 +1410,7 @@ def equationFromString( def _has_clear_constraints(msg): - return hasattr(msg, "clearConstraints") + return hasattr(msg, "clear_all_constraints") def _has_clear_restraints(msg): diff --git a/tests/test_recipeorganizer.py b/tests/test_recipeorganizer.py index e7366c48..43846a13 100644 --- a/tests/test_recipeorganizer.py +++ b/tests/test_recipeorganizer.py @@ -306,6 +306,26 @@ def test_constrain(self): p2.setValue(7) self.m._constraints[p1].update() self.assertEqual(7, p1.getValue()) + + self.m.clear_all_constraints() + actual_constrained_params = self.m.get_constrained_parmeters() + actual_constrained_params = [p.name for p in actual_constrained_params] + expected_constrained_params = [] + assert actual_constrained_params == expected_constrained_params + + # add constraint back and test the old function name `clearConstraints` + self.m.constrain(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] + assert actual_constrained_params == expected_constrained_params + + self.m.clearConstraints() + actual_constrained_params = self.m.get_constrained_parmeters() + actual_constrained_params = [p.name for p in actual_constrained_params] + expected_constrained_params = [] + assert actual_constrained_params == expected_constrained_params + return def testRestrain(self): From 6d6673c39590f5d61e64faef232c5771efcd4b1b Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 6 Mar 2026 17:29:42 -0500 Subject: [PATCH 11/13] clean up and fix variable names in restrain and constrain methods --- src/diffpy/srfit/fitbase/recipeorganizer.py | 140 +++++++++++--------- src/diffpy/srfit/structure/sgconstraints.py | 6 +- tests/test_recipeorganizer.py | 4 +- 3 files changed, 85 insertions(+), 65 deletions(-) diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index 90a8b492..9faef40f 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -883,37 +883,41 @@ def evaluateEquation(self, eqstr, ns={}): """ return self.evaluate_equation(eqstr, func_params=ns) - def constrain(self, parameter, con, ns={}): + def constrain(self, parameter, constraint_eq, params={}): """Constrain a parameter to an equation. Note that only one constraint can exist on a Parameter at a time. - Attributes + Parameters ---------- - parameter + parameter : str or Parameter The name of a Parameter or a Parameter to constrain. - con + constraint_eq : str or Equation A string representation of the constraint equation or a - Parameter to constrain to. A constraint equation must + Parameter to constrain to. A constraint equation must consist of numpy operators and "known" Parameters. - Parameters are known if they are in the ns argument, or if - they are managed by this object. - ns + Parameters are known if they are in the `params` + argument, or if they are managed by this object. + params : dict, optional A dictionary of Parameters, indexed by name, that are used - in the parameter, but not part of this object (default {}). - + in `parameter`, but not part of this object (default {}). - Raises ValueError if ns uses a name that is already used for a - variable. - Raises ValueError if parameter is a string but not part of this - object or in ns. - Raises ValueError if parameter is marked as constant. + Raises + ------ + ValueError + If `params` uses a name that is already used for a + variable. + ValueError + If `parameter` is a string but not part of this object or + in `params`. + ValueError + If `parameter` is marked as constant. """ if isinstance(parameter, str): name = parameter parameter = self.get(name) if parameter is None: - parameter = ns.get(name) + parameter = params.get(name) if parameter is None: raise ValueError("The parameter cannot be found") @@ -921,21 +925,21 @@ def constrain(self, parameter, con, ns={}): if parameter.const: raise ValueError("The parameter '%s' is constant" % parameter) - if isinstance(con, str): - eqstr = con - eq = equationFromString(con, self._eqfactory, ns) + if isinstance(constraint_eq, str): + eqstr = constraint_eq + eq = equationFromString(constraint_eq, self._eqfactory, params) else: - eq = Equation(root=con) - eqstr = con.name + eq = Equation(root=constraint_eq) + eqstr = constraint_eq.name eq.name = "_constraint_%s" % parameter.name # Make and store the constraint - con = Constraint() - con.constrain(parameter, eq) + constraint_eq = Constraint() + constraint_eq.constrain(parameter, eq) # Store the equation string so it can be shown later. - con.eqstr = eqstr - self._constraints[parameter] = con + constraint_eq.eqstr = eqstr + self._constraints[parameter] = constraint_eq # Our configuration changed self._update_configuration() @@ -1073,55 +1077,67 @@ def clearConstraints(self, recurse=False): """ return self.clear_all_constraints(recurse=recurse) - def restrain(self, res, lb=-inf, ub=inf, sig=1, scaled=False, ns={}): + def restrain( + self, param_or_eq, lb=-inf, ub=inf, sig=1, scaled=False, params={} + ): """Restrain an expression to specified bounds. - Attributes + Parameters ---------- - res - An equation string or Parameter to restrain. - lb - The lower bound on the restraint evaluation (default -inf). - ub - The lower bound on the restraint evaluation (default inf). - sig - The uncertainty on the bounds (default 1). - scaled - A flag indicating if the restraint is scaled (multiplied) - by the unrestrained point-average chi^2 (chi^2/numpoints) - (default False). - ns - A dictionary of Parameters, indexed by name, that are used - in the equation string, but not part of the RecipeOrganizer - (default {}). + param_or_eq : str or Parameter + The equation string or a Parameter object to restrain. + lb : 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). + sig : float, optional + The uncertainty associated with the bounds (default is 1). + 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 {}). + Returns + ------- + Restraint + The created Restraint object, which can be used with the + 'unrestrain' method. - The penalty is calculated as - (max(0, lb - 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. + Notes + ----- + The penalty is calculated as: + .. + (max(0, lb - val, val - ub) / sig) ** 2 - Raises ValueError if ns uses a name that is already used for a - Parameter. - Raises ValueError if res depends on a Parameter that is not part of - the RecipeOrganizer and that is not defined in ns. + where `val` is the value of the evaluated equation. + If `scaled` is True, this penalty is multiplied by + the average chi^2. - Returns the Restraint object for use with the 'unrestrain' method. + Raises + ------ + ValueError + If `func_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`. """ - - if isinstance(res, str): - eqstr = res - eq = equationFromString(res, self._eqfactory, ns) + if isinstance(param_or_eq, str): + eqstr = param_or_eq + eq = equationFromString(param_or_eq, self._eqfactory, params) else: - eq = Equation(root=res) - eqstr = res.name + eq = Equation(root=param_or_eq) + eqstr = param_or_eq.name # Make and store the restraint - res = Restraint(eq, lb, ub, sig, scaled) - res.eqstr = eqstr - self.addRestraint(res) - return res + param_or_eq = Restraint(eq, lb, ub, sig, scaled) + param_or_eq.eqstr = eqstr + self.addRestraint(param_or_eq) + return param_or_eq def addRestraint(self, res): """Add a Restraint instance to the RecipeOrganizer. diff --git a/src/diffpy/srfit/structure/sgconstraints.py b/src/diffpy/srfit/structure/sgconstraints.py index 291bb33a..8e1e198d 100644 --- a/src/diffpy/srfit/structure/sgconstraints.py +++ b/src/diffpy/srfit/structure/sgconstraints.py @@ -596,7 +596,9 @@ def _constrain_adps(self, positions): continue isoidx.append(j) scatterer = scatterers[j] - scatterer.constrain(isosymbol, isoname, ns=self._parameters) + scatterer.constrain( + isosymbol, isoname, params=self._parameters + ) fadp = g.UFormulas(adpnames) @@ -809,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, ns=ns) + scatterer.constrain(par, formula, params=ns) return diff --git a/tests/test_recipeorganizer.py b/tests/test_recipeorganizer.py index 43846a13..17b6a758 100644 --- a/tests/test_recipeorganizer.py +++ b/tests/test_recipeorganizer.py @@ -352,7 +352,9 @@ def testRestrain(self): # Check errors on unregistered parameters self.assertRaises(ValueError, self.m.restrain, "2*p3") - self.assertRaises(ValueError, self.m.restrain, "2*p2", ns={"p2": p3}) + self.assertRaises( + ValueError, self.m.restrain, "2*p2", params={"p2": p3} + ) return def testGetConstraints(self): From 6a299836780d6451886fb704cd20cf9fb3aecc3c Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 6 Mar 2026 17:58:10 -0500 Subject: [PATCH 12/13] minor docstring typos --- src/diffpy/srfit/fitbase/recipeorganizer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/diffpy/srfit/fitbase/recipeorganizer.py b/src/diffpy/srfit/fitbase/recipeorganizer.py index 9faef40f..495b3706 100644 --- a/src/diffpy/srfit/fitbase/recipeorganizer.py +++ b/src/diffpy/srfit/fitbase/recipeorganizer.py @@ -613,7 +613,7 @@ def register_calculator(self, calculator, argnames=None): calculator : Calculator object The Calculator to register. argnames : list or None, optional - The names of the arguments to calculator (list or None). + The names of the arguments to `calculator` (list or None). If this is None, then the argument names will be extracted from the function. """ @@ -665,13 +665,13 @@ def register_function(self, function, name=None, argnames=None): this is None (default), the method will try to determine the name of the function automatically. argnames : list or None, optional - The names of the arguments to 'function' (list or None). + The names of the arguments to `function` (list or None). If this is None (default), then the argument names will be extracted from the function. Note ---- - name and argnames can be extracted from regular Python + The `name` and `argnames` args can be extracted from regular Python functions (of type ), bound class methods, and callable classes. From 1c11b3196e3028103666c5083a9b53d6db9c60fe Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 6 Mar 2026 18:01:38 -0500 Subject: [PATCH 13/13] news --- news/recipeorg-dep1.rst | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 news/recipeorg-dep1.rst diff --git a/news/recipeorg-dep1.rst b/news/recipeorg-dep1.rst new file mode 100644 index 00000000..aff30369 --- /dev/null +++ b/news/recipeorg-dep1.rst @@ -0,0 +1,37 @@ +**Added:** + +* Added ``iterate_over_parameters`` method. +* Added ``register_calculator`` method. +* Added ``register_function`` method. +* Added ``register_string_function`` method. +* Added ``evaluate_equation`` method. +* Added ``is_constrained`` method. +* Added ``get_constrained_parameters`` method. +* Added ``clear_all_constraints`` method. + +**Changed:** + +* + +**Deprecated:** + +* Deprecated ``iterPars`` method. Use ``iterate_over_parameters`` instead. +* Deprecated ``registerCalculator`` method. Use ``register_calculator`` instead. +* Deprecated ``registerFunction`` method. Use ``register_function`` instead. +* Deprecated ``registerStringFunction`` method. Use ``register_string_function`` instead. +* Deprecated ``evaluateEquation`` method. Use ``evaluate_equation`` instead. +* Deprecated ``isConstrained`` method. Use ``is_constrained`` instead. +* Deprecated ``getConstrainedPars`` method. Use ``get_constrained_parameters`` instead. +* Deprecated ``clearConstraints`` method. Use ``clear_all_constraints`` instead. + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +*