Source code for diffpy.srfit.fitbase.recipeorganizer

#!/usr/bin/env python
##############################################################################
#
# diffpy.srfit      by DANSE Diffraction group
#                   Simon J. L. Billinge
#                   (c) 2008 The Trustees of Columbia University
#                   in the City of New York.  All rights reserved.
#
# File coded by:    Chris Farrow
#
# See AUTHORS.txt for a list of people who contributed.
# See LICENSE_DANSE.txt for license information.
#
##############################################################################
"""Base classes and tools for constructing a FitRecipe.

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.
"""

__all__ = ["RecipeContainer", "RecipeOrganizer", "equationFromString"]

import re
from collections import OrderedDict
from functools import partial
from itertools import chain, groupby

import six
from numpy import inf

from diffpy.srfit.equation import Equation
from diffpy.srfit.equation.builder import EquationFactory
from diffpy.srfit.fitbase.configurable import Configurable
from diffpy.srfit.fitbase.constraint import Constraint
from diffpy.srfit.fitbase.parameter import Parameter
from diffpy.srfit.fitbase.restraint import Restraint
from diffpy.srfit.fitbase.validatable import Validatable
from diffpy.srfit.interface import _recipeorganizer_interface
from diffpy.srfit.util import _DASHEDLINE
from diffpy.srfit.util import sortKeyForNumericString as numstr
from diffpy.srfit.util.nameutils import validateName
from diffpy.srfit.util.observable import Observable


[docs] class RecipeContainer(Observable, Configurable, Validatable): """Base class for organizing pieces of a FitRecipe. RecipeContainers are hierarchical organizations of Parameters and other RecipeContainers. This class provides attribute-access to these contained objects. Parameters and other RecipeContainers can be found within the hierarchy with the _locateManagedObject method. A RecipeContainer can manage dictionaries for that store various objects. These dictionaries can be added to the RecipeContainer using the _manage method. RecipeContainer methods that add, remove or retrieve objects will work with any managed dictionary. This makes it easy to add new types of objects to be contained by a RecipeContainer. By default, the RecipeContainer is configured to manage an OrderedDict of Parameter objects. RecipeContainer is an Observable, and observes its managed objects and Parameters. This allows hierarchical calculation elements, such as ProfileGenerator, to detect changes in Parameters and Restraints on which it may depend. Attributes ---------- name A name for this RecipeContainer. Names should be unique within a RecipeContainer and should be valid attribute names. _parameters A managed OrderedDict of contained Parameters. __managed A list of managed dictionaries. This is used for attribute access, addition and removal. _configobjs A set of configurable objects that must know of configuration changes within this object. Properties ---------- names Variable names (read only). See getNames. values Variable values (read only). See getValues. """ names = property(lambda self: self.getNames()) values = property(lambda self: self.getValues()) def __init__(self, name): Observable.__init__(self) Configurable.__init__(self) validateName(name) self.name = name self._parameters = OrderedDict() self.__managed = [] self._manage(self._parameters) return def _manage(self, d): """Manage a dictionary of objects. This adds the dictionary to the __managed list. Dictionaries in __managed are used for attribute access, addition, and removal. """ self.__managed.append(d) return def _iterManaged(self): """Get iterator over managed objects.""" return chain(*(d.values() for d in self.__managed))
[docs] def iterPars(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). """ regexp = re.compile(pattern) for par in list(self._parameters.values()): if regexp.search(par.name): yield par if not recurse: return # Iterate over objects within the managed dictionaries. managed = self.__managed[:] managed.remove(self._parameters) for m in managed: for obj in m.values(): if hasattr(obj, "iterPars"): for par in obj.iterPars(pattern=pattern): yield par return
def __iter__(self): """Iterate over top-level parameters.""" return iter(self._parameters.values()) def __len__(self): """Get number of top-level parameters.""" return len(self._parameters) def __getitem__(self, idx): """Get top-level parameters by index.""" # need to wrap this in a list for python 3 compatibility. return list(self._parameters.values())[idx] def __getattr__(self, name): """Gives access to the contained objects as attributes.""" arg = self.get(name) if arg is None: raise AttributeError(name) return arg # Ensure there is no __dir__ override in the base class. assert ( getattr(Observable, "__dir__", None) is getattr(Configurable, "__dir__", None) is getattr(Validatable, "__dir__", None) is getattr(object, "__dir__", None) ) def __dir__(self): """Return sorted list of attributes for this object.""" rv = set(dir(type(self))) rv.update(self.__dict__) # self.get fetches looks up for items in all managed dictionaries. # Add keys from each dictionary in self.__managed. rv.update(*self.__managed) rv = sorted(rv) return rv # Needed by __setattr__ _parameters = OrderedDict() __managed = [] def __setattr__(self, name, value): """Parameter access and object checking.""" if name in self._parameters: par = self._parameters[name] if isinstance(value, Parameter): par.value = value.value else: par.value = value return m = self.get(name) if m is not None: raise AttributeError("Cannot set '%s'" % name) super(RecipeContainer, self).__setattr__(name, value) return def __delattr__(self, name): """Delete parameters with del. This does not allow deletion of non-parameters, as this may require configuration changes that are not yet handled in a general way. """ if name in self._parameters: self._removeParameter(self._parameters[name]) return m = self.get(name) if m is not None: raise AttributeError("Cannot delete '%s'" % name) super(RecipeContainer, self).__delattr__(name) return
[docs] def get(self, name, default=None): """Get a managed object.""" for d in self.__managed: arg = d.get(name) if arg is not None: return arg return default
[docs] def getNames(self): """Get the names of managed parameters.""" return [p.name for p in self._parameters.values()]
[docs] def getValues(self): """Get the values of managed parameters.""" return [p.value for p in self._parameters.values()]
def _addObject(self, obj, d, check=True): """Add an object to a managed dictionary. Attributes ---------- obj The object to be stored. d The managed dictionary to store the object in. check If True (default), a ValueError is raised an object of the given name already exists. Raises ValueError if the object has no name. Raises ValueError if the object has the same name as some other managed object. """ # Check name if not obj.name: message = "%s has no name" % obj.__class__.__name__ raise ValueError(message) # Check for extant object in d with same name oldobj = d.get(obj.name) if check and oldobj is not None: message = "%s with name '%s' already exists" % ( obj.__class__.__name__, obj.name, ) raise ValueError(message) # Check for object with same name in other dictionary. if oldobj is None and self.get(obj.name) is not None: message = "Non-%s with name '%s' already exists" % ( obj.__class__.__name__, obj.name, ) raise ValueError(message) # Detach the old object, if there is one if oldobj is not None: oldobj.removeObserver(self._flush) # Add the object d[obj.name] = obj # Observe the object obj.addObserver(self._flush) # Store this as a configurable object self._storeConfigurable(obj) return def _removeObject(self, obj, d): """Remove an object from a managed dictionary. Raises ValueError if obj is not part of the dictionary. """ if obj not in d.values(): m = "'%s' is not part of the %s" % (obj, self.__class__.__name__) raise ValueError(m) del d[obj.name] obj.removeObserver(self._flush) return def _locateManagedObject(self, obj): """Find the location a managed object within the hierarchy. Attributes ---------- obj The object to find. Returns a list of objects. The first member of the list is this object, and each subsequent member is a sub-object of the previous one. The last entry in the list is obj. If obj cannot be found, the list is empty. """ loc = [self] # This handles the case that an object is asked to locate itself. if obj is self: return loc for m in self._iterManaged(): # Check locally for the object if m is obj: loc.append(obj) return loc # Check within managed objects if hasattr(m, "_locateManagedObject"): subloc = m._locateManagedObject(obj) if subloc: return loc + subloc return [] def _flush(self, other): """Invalidate cached state. This will force any observer to invalidate its state. By default this does nothing. """ self.notify(other) return def _validate(self): """Validate my state. This validates that contained Parameters and managed objects are valid. Raises AttributeError if validation fails. """ iterable = chain(self.__iter__(), self._iterManaged()) self._validateOthers(iterable) return
# End class RecipeContainer
[docs] class RecipeOrganizer(_recipeorganizer_interface, RecipeContainer): """Extended base class for organizing pieces of a FitRecipe. This class extends RecipeContainer by organizing constraints and Restraints, as well as Equations that can be used in Constraint and Restraint equations. These constraints and Restraints can be placed at any level and a flattened list of them can be retrieved with the _getConstraints and _getRestraints methods. Attributes ---------- name A name for this organizer. Names should be unique within a RecipeOrganizer and should be valid attribute names. _calculators A managed dictionary of Calculators, indexed by name. _parameters A managed OrderedDict of contained Parameters. _constraints A dictionary of Constraints, indexed by the constrained Parameter. Constraints can be added using the 'constrain' method. _restraints A set of Restraints. Restraints can be added using the 'restrain' method. _eqfactory A diffpy.srfit.equation.builder.EquationFactory instance that is used create Equations from string. Properties ---------- names Variable names (read only). See getNames. values Variable values (read only). See getValues. Raises ValueError if the name is not a valid attribute identifier """ def __init__(self, name): RecipeContainer.__init__(self, name) self._restraints = set() self._constraints = {} self._eqfactory = EquationFactory() self._calculators = {} self._manage(self._calculators) return # Parameter management def _newParameter(self, name, value, check=True): """Add a new Parameter to the container. This creates a new Parameter and adds it to the container using the _addParameter method. Returns the Parameter. """ p = Parameter(name, value) self._addParameter(p, check) return p def _addParameter(self, par, check=True): """Store a Parameter. Parameters added in this way are registered with the _eqfactory. Attributes ---------- par The Parameter to be stored. check If True (default), a ValueError is raised a Parameter of the specified name has already been inserted. Raises ValueError if the Parameter has no name. Raises ValueError if the Parameter has the same name as a contained RecipeContainer. """ # Store the Parameter RecipeContainer._addObject(self, par, self._parameters, check) # Register the Parameter self._eqfactory.registerArgument(par.name, par) return def _removeParameter(self, par): """Remove a parameter. This de-registers the Parameter with the _eqfactory. The Parameter will remain part of built equations. Note that constraints and restraints involving the Parameter are not modified. Raises ValueError if par is not part of the RecipeOrganizer. """ self._removeObject(par, self._parameters) self._eqfactory.deRegisterBuilder(par.name) return
[docs] def registerCalculator(self, f, argnames=None): """Register a Calculator so it can be used within equation strings. A Calculator is an elaborate function that can organize Parameters. This creates a function with this class that can be used within string equations. The resulting equation can be used in a string with arguments like a function or without, in which case the values of the Parameters created from argnames will be be used to compute the value. Attributes ---------- f The Calculator to register. argnames The names of the arguments to f (list or None). If this is None, then the argument names will be extracted from the function. """ self._eqfactory.registerOperator(f.name, f) self._addObject(f, self._calculators) # Register arguments of the calculator if argnames is None: fncode = f.__call__.__func__.__code__ argnames = list(fncode.co_varnames) argnames = argnames[1 : fncode.co_argcount] for pname in argnames: if pname not in self._eqfactory.builders: par = self._newParameter(pname, 0) else: par = self.get(pname) f.addLiteral(par) # Now return an equation object eq = self._eqfactory.makeEquation(f.name) return eq
[docs] def registerFunction(self, f, 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 passed in the equation string, as this will be handled automatically. Attributes ---------- f The callable to register. If this is an Equation instance, then all that needs to be provided is a name. name 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). 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 classes. Raises TypeError if name or argnames cannot be automatically extracted. Raises TypeError if an automatically extracted name is '<lambda>'. Raises ValueError if f is an Equation object and name is None. Returns 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 name is None: m = "Equation must be given a name" raise ValueError(m) self._eqfactory.registerOperator(name, f) return f # Introspection code if name is None or argnames is None: import inspect fncode = None # This will let us offset the argument list to eliminate 'self' offset = 0 # check regular functions if inspect.isfunction(f): fncode = f.__code__ # check class method elif inspect.ismethod(f): fncode = f.__func__.__code__ offset = 1 # check functor elif hasattr(f, "__call__") and hasattr(f.__call__, "__func__"): fncode = f.__call__.__func__.__code__ offset = 1 else: m = "Cannot extract name or argnames" raise ValueError(m) # Extract the name if name is None: name = fncode.co_name if name == "<lambda>": m = "You must supply a name name for a lambda function" raise ValueError(m) # Extract the arguments if argnames is None: argnames = list(fncode.co_varnames) argnames = argnames[offset : fncode.co_argcount] # End introspection code # Make missing Parameters for pname in argnames: if pname not in self._eqfactory.builders: self._newParameter(pname, 0) # Initialize and register from diffpy.srfit.fitbase.calculator import Calculator if isinstance(f, Calculator): for pname in argnames: par = self.get(pname) f.addLiteral(par) self._eqfactory.registerOperator(name, f) else: self._eqfactory.registerFunction(name, f, argnames) # Now we can create the Equation and return it to the user. eq = self._eqfactory.makeEquation(name) return eq
[docs] def registerStringFunction(self, fstr, name, ns={}): """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 passed in the function string, as this will be handled automatically. Attributes ---------- fstr A string equation to register. name The name of the function to be used in equations. ns A dictionary of Parameters, indexed by name, that are used in fstr, but not part of the FitRecipe (default {}). 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. """ # Build the equation instance. eq = equationFromString(fstr, self._eqfactory, ns=ns, buildargs=True) eq.name = name # Register any new Parameters. for par in self._eqfactory.newargs: self._addParameter(par) # Register the equation as a callable function. argnames = eq.argdict.keys() return self.registerFunction(eq, name, argnames)
[docs] 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 {}). 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
[docs] def constrain(self, par, con, ns={}): """Constrain a parameter to an equation. Note that only one constraint can exist on a Parameter at a time. Attributes ---------- par The name of a Parameter or a Parameter to constrain. con A string representation of the constraint equation or a 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 A dictionary of Parameters, indexed by name, that are used in the 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 par is a string but not part of this object or in ns. Raises ValueError if par is marked as constant. """ if isinstance(par, six.string_types): name = par par = self.get(name) if par is None: par = ns.get(name) if par is None: raise ValueError("The parameter cannot be found") if par.const: raise ValueError("The parameter '%s' is constant" % par) if isinstance(con, six.string_types): eqstr = con eq = equationFromString(con, self._eqfactory, ns) else: eq = Equation(root=con) eqstr = con.name eq.name = "_constraint_%s" % par.name # Make and store the constraint con = Constraint() con.constrain(par, eq) # Store the equation string so it can be shown later. con.eqstr = eqstr self._constraints[par] = con # Our configuration changed self._updateConfiguration() return
[docs] def isConstrained(self, par): """Determine if a Parameter is constrained in this object. Attributes ---------- par The name of a Parameter or a Parameter to check. """ if isinstance(par, six.string_types): name = par par = self.get(name) return par in self._constraints
[docs] def unconstrain(self, *pars): """Unconstrain a Parameter. This removes any constraints on a Parameter. Attributes ---------- *pars The names of Parameters or Parameters to unconstrain. Raises ValueError if the Parameter is not constrained. """ update = False for par in pars: if isinstance(par, six.string_types): name = par par = self.get(name) if par is None: raise ValueError("The parameter cannot be found") if par in self._constraints: self._constraints[par].unconstrain() del self._constraints[par] update = True if update: # Our configuration changed self._updateConfiguration() else: raise ValueError("The parameter is not constrained") return
[docs] def getConstrainedPars(self, recurse=False): """Get a list of constrained managed Parameters in this object. Attributes ---------- recurse Recurse into managed objects and retrieve their constrained Parameters as well (default False). """ const = self._getConstraints(recurse) return const.keys()
[docs] def clearConstraints(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. """ if self._constraints: self.unconstrain(*self._constraints) if recurse: for m in filter(_has_clear_constraints, self._iterManaged()): m.clearConstraints(recurse) return
[docs] def restrain(self, res, lb=-inf, ub=inf, sig=1, scaled=False, ns={}): """Restrain an expression to specified bounds. Attributes ---------- 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 {}). 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. 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. Returns the Restraint object for use with the 'unrestrain' method. """ if isinstance(res, six.string_types): eqstr = res eq = equationFromString(res, self._eqfactory, ns) else: eq = Equation(root=res) eqstr = res.name # Make and store the restraint res = Restraint(eq, lb, ub, sig, scaled) res.eqstr = eqstr self.addRestraint(res) return res
[docs] def addRestraint(self, res): """Add a Restraint instance to the RecipeOrganizer. Attributes ---------- res A Restraint instance. """ self._restraints.add(res) # Our configuration changed. Notify observers. self._updateConfiguration() return
[docs] def unrestrain(self, *ress): """Remove a Restraint from the RecipeOrganizer. Attributes ---------- *ress Restraints returned from the 'restrain' method or added with the 'addRestraint' method. """ update = False restuple = tuple(self._restraints) for res in ress: if res in restuple: self._restraints.remove(res) update = True if update: # Our configuration changed self._updateConfiguration() return
[docs] def clearRestraints(self, recurse=False): """Clear all restraints. Attributes ---------- recurse Recurse into managed objects and clear all restraints found there as well. """ self.unrestrain(*self._restraints) if recurse: for msg in filter(_has_clear_restraints, self._iterManaged()): msg.clearRestraints(recurse) return
def _getConstraints(self, recurse=True): """Get the constrained Parameters for this and managed sub-objects.""" constraints = {} if recurse: for m in filter(_has_get_constraints, self._iterManaged()): constraints.update(m._getConstraints(recurse)) constraints.update(self._constraints) return constraints def _getRestraints(self, recurse=True): """Get the Restraints for this and embedded ParameterSets. This returns a set of Restraint objects. """ restraints = set(self._restraints) if recurse: for m in filter(_has_get_restraints, self._iterManaged()): restraints.update(m._getRestraints(recurse)) return restraints def _validate(self): """Validate my state. This performs RecipeContainer validations. This validates contained Restraints and Constraints. Raises AttributeError if validation fails. """ RecipeContainer._validate(self) iterable = chain(self._restraints, self._constraints.values()) self._validateOthers(iterable) return # For printing the configured recipe to screen def _formatManaged(self, prefix=""): """Format hierarchy of managed parameters for showing. Parameters ---------- prefix : str The leading string to be prefixed to each parameter name. Returns ------- list List of formatted lines, one per each Parameter. """ lines = [] formatstr = "{:<W}{}" # Format own parameters. if self._parameters: w0 = max(len(n) for n in self._parameters) w1 = ((w0 + len(prefix) + 1) // 4 + 1) * 4 fmt = formatstr.replace("W", str(w1)) lines.extend( fmt.format(prefix + n, p.value) for n, p in self._parameters.items() ) # Recurse into managed objects. for obj in self._iterManaged(): if hasattr(obj, "_formatManaged"): oprefix = prefix + obj.name + "." tlines = obj._formatManaged(prefix=oprefix) lines.extend([""] if lines and tlines else []) lines.extend(tlines) return lines def _formatConstraints(self): """Format constraints for showing. This collects constraints on all levels of the hierarchy and displays them with respect to this level. Returns ------- list List of formatted lines displaying the defined constraints. Return empty list when no constraints were defined. """ cdict = self._getConstraints() # Find each constraint and format the equation clines = [] for par, con in cdict.items(): loc = self._locateManagedObject(par) 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.sort(key=numstr) return clines def _formatRestraints(self): """Format restraints for showing. This collects restraints on all levels of the hierarchy and displays them with respect to this level. Returns ------- list List of formatted lines displaying the defined restraints. Return empty list when no restraints were defined. """ rset = self._getRestraints() rlines = [] for res in rset: line = "%s: lb = %f, ub = %f, sig = %f, scaled = %s" % ( res.eqstr, res.lb, res.ub, res.sig, res.scaled, ) rlines.append(line) rlines.sort(key=numstr) return rlines
[docs] def show(self, pattern="", textwidth=78): """Show the configuration hierarchy on the screen. This will print out a summary of all contained objects. Parameters ---------- pattern : str, optional Limit output to only those parameters that match this regular expression (match all by default). textwidth : int, optional Trim formatted lines at this text width to avoid folding at the screen width. Do not trim when negative or 0. """ regexp = re.compile(pattern) _pmatch_with_re = partial(_pmatch, regexp=regexp) # Show sub objects and their parameters lines = [] tlines = self._formatManaged() if tlines: lines.extend(["Parameters", _DASHEDLINE]) linesok = filter(_pmatch_with_re, tlines) lastnotblank = False # squeeze repeated blank lines for lastnotblank, g in groupby(linesok, bool): lines.extend(g if lastnotblank else [""]) # remove trailing blank line if not lastnotblank: lines.pop(-1) # FIXME - parameter names in equations not particularly informative # Show constraints cmatch = regexp.search tlines = self._formatConstraints() if tlines: if lines: lines.append("") lines.extend(["Constraints", _DASHEDLINE]) lines.extend(filter(cmatch, tlines)) # FIXME - parameter names in equations not particularly informative # Show restraints tlines = self._formatRestraints() if tlines: if lines: lines.append("") lines.extend(["Restraints", _DASHEDLINE]) lines.extend(filter(_pmatch_with_re, tlines)) # Determine effective text width tw. tw = textwidth if (textwidth is not None and textwidth > 0) else None # Avoid outputting "\n" when there is no output. if lines: print("\n".join(s[:tw] for s in lines)) return
# End RecipeOrganizer
[docs] def equationFromString( eqstr, factory, ns={}, buildargs=False, argclass=Parameter, argkw={} ): """Make an equation from a string. Attributes ---------- eqstr 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 An EquationFactory instance. ns A dictionary of Parameters indexed by name that are used in the eqstr but not already defined in the factory (default {}). buildargs 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 Class to use when creating new Arguments (default Parameter). The class constructor must accept the 'name' key word. argkw Key word dictionary to pass to the argclass constructor (default {}). Raises ValueError if ns uses a name that is already defined in the factory. Raises ValueError if the equation has undefined parameters. """ defined = set(factory.builders.keys()) # Check if ns overloads any parameters. if defined.intersection(ns.keys()): raise ValueError("ns contains defined names") # Register the ns parameters in the equation factory for name, arg in ns.items(): factory.registerArgument(name, arg) eq = factory.makeEquation(eqstr, buildargs, argclass, argkw) # Clean the ns parameters for name in ns: factory.deRegisterBuilder(name) return eq
def _has_clear_constraints(msg): return hasattr(msg, "clearConstraints") def _has_clear_restraints(msg): return hasattr(msg, "clearRestraints") def _has_get_restraints(msg): return hasattr(msg, "_getRestraints") def _has_get_constraints(msg): return hasattr(msg, "_getConstraints") def _pmatch(inp_str, regexp): parts = inp_str.split(None, 1) return len(parts) < 2 or regexp.search(parts[0])