def run(self, script,data): for k,v in self.stateData.items(): data[k] = v data['stateData'] = self.stateData interpreter = Interpreter(data) interpreter.eval(script,0,True) for k,v in interpreter.symtable.items(): if type(v) in [str,int,float,bool]: #print 'saving {} in stateData'.format(k) self.stateData[k] = v interpreter = None
def evaluate_dice_list(self): """ Evaluates each dice in the dice list, constructs and evaluates final string """ if self.verbosity: print(f"--> evaluate_dice_list") self.eval_expression = "" for i in range(len(self.dice_list)): if isinstance(self.dice_list[i], Die): if self.verbosity: print(f"dice_list[{i}] is Die") roll_result = self.dice_list[i].evaluate() if self.verbosity: print(f"roll_result: {roll_result}") self.eval_expression += str(roll_result) else: if self.verbosity: print(f"dice_list[{i}] is not Die") self.eval_expression += self.dice_list[i] # Evaluate the constructed string aeval = Interpreter() self.result = aeval.eval(self.eval_expression) if self.verbosity: print(f"result: {self.result}")
def test_eval(): aeval = Interpreter() expr_str = "r[0]**4 * r[1]**2 +" * 10000 + '2*r[2]' with DeepRecursionCtx(): expr = aeval.parse(expr_str) aeval.symtable['r'] = [1, 2, 3] res = aeval.eval(expr) assert not math.isnan(res)
async def calc(self, ctx, *, expression: str): """ Evaluates a math expression. """ terp = Interpreter() result = terp.eval(expression) if result != '' and result is not None: await ctx.send(result) else: await ctx.send('Empty result.')
def eval(self, expr, lineno=0, show_errors=True): res = OrigInterpreter.eval(self, expr, lineno=lineno, show_errors=show_errors) if len(self.error) > 0: for err in self.error: t, v, tb = err.exc_info raise t, v, tb return res
class Python(Plugin): def on_attach(self, config): self.env = Interpreter() def on_message(self, message): # also text starts with '>>>' will be interpreted as python if message['text'].startswith('>>>'): return self.py(message['text'][12:].strip()) @command def py(self, *args): """evaluates python code ex) !py 1+1 """ # XXX: parser unwraps double quote of outside of argument line = u' '.join(args) print self.env.eval(line) return unicode(self.env.eval(line))
class ModelReferenceSystem(Tree): def __init__(self): self.expressions = Expression() self.interpreter = Interpreter() super().__init__() def create_node(self, name, parent=None): if parent is not None: id = "{id}_{name}".format(id=parent, name=name) else: id = name print(name, id, parent) mrs_node = MRS_Node(self, name, id) return self.add_node(mrs_node, parent) def eval_topo(self, topo): for idx, elt in enumerate(topo): ast_executable_extended = "{id}={expr}".format(id=elt, expr=self.get_node(elt).execute_str) print(idx, ast_executable_extended) self.interpreter.eval(ast_executable_extended) #print(self.interpreter.symtable) return self.interpreter.symtable
def operator_expr(self, expr): """Evaluate an expression composed of single-mode operators. See ``Mode.OPERATORS`` for the full list of supported operators. Args: expr (str): String representation of the operator expression to evaluate. Returns: ``qutip.Qobj``: Evaluated operator expression. """ symbols = {name: getattr(self, name) for name in self.OPERATORS} symtable = make_symbol_table(use_numpy=True, **symbols) aeval = Interpreter(symtable=symtable) return aeval.eval(expr)
def safeEval(expr, scope): scope = dict(**scope) for name in scope: if getattr(scope[name], "wrapped", False) is True: scope[name] = scope[name](scope) interpreter = Interpreter( usersyms=scope, use_numpy=False, minimal=False ) res = interpreter.eval(expr, show_errors=False) if interpreter.error: raise interpreter.error return res
from py_expression_eval import Parser parser = Parser() valor = parser.parse('2 * 3').evaluate({}) print(valor) from asteval import Interpreter aeval = Interpreter() txt = """nmax = 1e8 a = sqrt(arange(nmax)) """ valor = aeval.eval(txt) print(valor)
class Parameters(dict): """A dictionary of Parameter objects. It should contain all Parameter objects that are required to specify a fit model. All minimization and Model fitting routines in lmfit will use exactly one Parameters object, typically given as the first argument to the objective function. All keys of a Parameters() instance must be strings and valid Python symbol names, so that the name must match ``[a-z_][a-z0-9_]*`` and cannot be a Python reserved word. All values of a Parameters() instance must be Parameter objects. A Parameters() instance includes an `asteval` Interpreter used for evaluation of constrained Parameters. Parameters() support copying and pickling, and have methods to convert to and from serializations using json strings. """ def __init__(self, asteval=None, usersyms=None): """ Arguments --------- asteval : :class:`asteval.Interpreter`, optional Instance of the `asteval.Interpreter` to use for constraint expressions. If None (default), a new interpreter will be created. **Warning: deprecated**, use `usersyms` if possible! usersyms : dict, optional Dictionary of symbols to add to the :class:`asteval.Interpreter` (default is None). """ super().__init__(self) self._asteval = asteval if asteval is None: self._asteval = Interpreter() else: msg = ("The use of the 'asteval' argument for the Parameters class" " was deprecated in lmfit v0.9.12 and will be removed in a " "later release. Please use the 'usersyms' argument instead!") warnings.warn(FutureWarning(msg)) self._asteval = asteval _syms = {} _syms.update(SCIPY_FUNCTIONS) if usersyms is not None: _syms.update(usersyms) for key, val in _syms.items(): self._asteval.symtable[key] = val def copy(self): """Parameters.copy() should always be a deepcopy.""" return self.__deepcopy__(None) def update(self, other): """Update values and symbols with another Parameters object.""" if not isinstance(other, Parameters): raise ValueError("'%s' is not a Parameters object" % other) self.add_many(*other.values()) for sym in other._asteval.user_defined_symbols(): self._asteval.symtable[sym] = other._asteval.symtable[sym] return self def __copy__(self): """Parameters.copy() should always be a deepcopy.""" return self.__deepcopy__(None) def __deepcopy__(self, memo): """Implementation of Parameters.deepcopy(). The method needs to make sure that `asteval` is available and that all individual Parameter objects are copied. """ _pars = self.__class__(asteval=None) # find the symbols that were added by users, not during construction unique_symbols = {key: self._asteval.symtable[key] for key in self._asteval.user_defined_symbols()} _pars._asteval.symtable.update(unique_symbols) # we're just about to add a lot of Parameter objects to the newly parameter_list = [] for key, par in self.items(): if isinstance(par, Parameter): param = Parameter(name=par.name, value=par.value, min=par.min, max=par.max) param.vary = par.vary param.brute_step = par.brute_step param.stderr = par.stderr param.correl = par.correl param.init_value = par.init_value param.expr = par.expr param.user_data = par.user_data parameter_list.append(param) _pars.add_many(*parameter_list) return _pars def __setitem__(self, key, par): """Set items of Parameters object.""" if key not in self: if not valid_symbol_name(key): raise KeyError("'%s' is not a valid Parameters name" % key) if par is not None and not isinstance(par, Parameter): raise ValueError("'%s' is not a Parameter" % par) dict.__setitem__(self, key, par) par.name = key par._expr_eval = self._asteval self._asteval.symtable[key] = par.value def __add__(self, other): """Add Parameters objects.""" if not isinstance(other, Parameters): raise ValueError("'%s' is not a Parameters object" % other) out = deepcopy(self) out.add_many(*other.values()) for sym in other._asteval.user_defined_symbols(): if sym not in out._asteval.symtable: out._asteval.symtable[sym] = other._asteval.symtable[sym] return out def __iadd__(self, other): """Add/assign Parameters objects.""" self.update(other) return self def __array__(self): """Convert Parameters to array.""" return array([float(k) for k in self.values()]) def __reduce__(self): """Reduce Parameters instance such that it can be pickled.""" # make a list of all the parameters params = [self[k] for k in self] # find the symbols from _asteval.symtable, that need to be remembered. sym_unique = self._asteval.user_defined_symbols() unique_symbols = {key: deepcopy(self._asteval.symtable[key]) for key in sym_unique} return self.__class__, (), {'unique_symbols': unique_symbols, 'params': params} def __setstate__(self, state): """Unpickle a Parameters instance. Parameters ---------- state : dict state['unique_symbols'] is a dictionary containing symbols that need to be injected into `_asteval.symtable`. state['params'] is a list of Parameter instances to be added. """ # first update the Interpreter symbol table. This needs to be done # first because Parameter's early in the list may depend on later # Parameter's. This leads to problems because add_many eventually leads # to a Parameter value being retrieved with _getval, which, if the # dependent value hasn't already been added to the symtable, leads to # an Error. Another way of doing this would be to remove all the expr # from the Parameter instances before they get added, then to restore # them. symtab = self._asteval.symtable for key, val in state['unique_symbols'].items(): if key not in symtab: symtab[key] = val # then add all the parameters self.add_many(*state['params']) def __repr__(self): """__repr__ from OrderedDict.""" if not self: return '%s()' % (self.__class__.__name__,) return '%s(%r)' % (self.__class__.__name__, list(self.items())) def eval(self, expr): """Evaluate a statement using the `asteval` Interpreter. Parameters ---------- expr : str An expression containing parameter names and other symbols recognizable by the `asteval` Interpreter. Returns ------- float The result of evaluating the expression. """ return self._asteval.eval(expr) def update_constraints(self): """Update all constrained parameters. This method ensures that dependencies are evaluated as needed. """ requires_update = {name for name, par in self.items() if par._expr is not None} updated_tracker = set(requires_update) def _update_param(name): """Update a parameter value, including setting bounds. For a constrained parameter (one with an `expr` defined), this first updates (recursively) all parameters on which the parameter depends (using the 'deps' field). """ par = self.__getitem__(name) if par._expr_eval is None: par._expr_eval = self._asteval for dep in par._expr_deps: if dep in updated_tracker: _update_param(dep) self._asteval.symtable[name] = par.value updated_tracker.discard(name) for name in requires_update: _update_param(name) def pretty_repr(self, oneline=False): """Return a pretty representation of a Parameters class. Parameters ---------- oneline : bool, optional If True prints a one-line parameters representation (default is False). Returns ------- s: str Parameters representation. """ if oneline: return self.__repr__() s = "Parameters({\n" for key in self.keys(): s += " '%s': %s, \n" % (key, self[key]) s += " })\n" return s def pretty_print(self, oneline=False, colwidth=8, precision=4, fmt='g', columns=['value', 'min', 'max', 'stderr', 'vary', 'expr', 'brute_step']): """Pretty-print of parameters data. Parameters ---------- oneline : bool, optional If True prints a one-line parameters representation (default is False). colwidth : int, optional Column width for all columns specified in `columns` (default is 8). precision : int, optional Number of digits to be printed after floating point (default is 4). fmt : {'g', 'e', 'f'}, optional Single-character numeric formatter. Valid values are: `'g'` floating point and exponential (default), `'e'` exponential, or `'f'` floating point. columns : :obj:`list` of :obj:`str`, optional List of :class:`Parameter` attribute names to print (default is to show all attributes). """ if oneline: print(self.pretty_repr(oneline=oneline)) return name_len = max(len(s) for s in self) allcols = ['name'] + columns title = '{:{name_len}} ' + len(columns) * ' {:>{n}}' print(title.format(*allcols, name_len=name_len, n=colwidth).title()) numstyle = '{%s:>{n}.{p}{f}}' # format for numeric columns otherstyles = dict(name='{name:<{name_len}} ', stderr='{stderr!s:>{n}}', vary='{vary!s:>{n}}', expr='{expr!s:>{n}}', brute_step='{brute_step!s:>{n}}') line = ' '.join([otherstyles.get(k, numstyle % k) for k in allcols]) for name, values in sorted(self.items()): pvalues = {k: getattr(values, k) for k in columns} pvalues['name'] = name # stderr is a special case: it is either numeric or None (i.e. str) if 'stderr' in columns and pvalues['stderr'] is not None: pvalues['stderr'] = (numstyle % '').format( pvalues['stderr'], n=colwidth, p=precision, f=fmt) elif 'brute_step' in columns and pvalues['brute_step'] is not None: pvalues['brute_step'] = (numstyle % '').format( pvalues['brute_step'], n=colwidth, p=precision, f=fmt) print(line.format(name_len=name_len, n=colwidth, p=precision, f=fmt, **pvalues)) def _repr_html_(self): """Return a HTML representation of parameters data.""" return params_html_table(self) def add(self, name, value=None, vary=True, min=-inf, max=inf, expr=None, brute_step=None): """Add a Parameter. Parameters ---------- name : str or Parameter If ``name`` refers to a Parameter object it will be added directly to the Parameters instance, otherwise a new Parameter object with name ``string`` is created before adding it. In both cases, ``name`` must match ``[a-z_][a-z0-9_]*`` and cannot be a Python reserved word. value : float, optional Numerical Parameter value, typically the *initial value*. vary : bool, optional Whether the Parameter is varied during a fit (default is True). min : float, optional Lower bound for value (default is ``-numpy.inf``, no lower bound). max : float, optional Upper bound for value (default is ``numpy.inf``, no upper bound). expr : str, optional Mathematical expression used to constrain the value during the fit (default is None). brute_step : float, optional Step size for grid points in the `brute` method (default is None). Examples -------- >>> params = Parameters() >>> params.add('xvar', value=0.50, min=0, max=1) >>> params.add('yvar', expr='1.0 - xvar') which is equivalent to: >>> params = Parameters() >>> params['xvar'] = Parameter(name='xvar', value=0.50, min=0, max=1) >>> params['yvar'] = Parameter(name='yvar', expr='1.0 - xvar') """ if isinstance(name, Parameter): self.__setitem__(name.name, name) else: self.__setitem__(name, Parameter(value=value, name=name, vary=vary, min=min, max=max, expr=expr, brute_step=brute_step)) def add_many(self, *parlist): """Add many parameters, using a sequence of tuples. Parameters ---------- *parlist : :obj:`sequence` of :obj:`tuple` or Parameter A sequence of tuples, or a sequence of `Parameter` instances. If it is a sequence of tuples, then each tuple must contain at least a `name`. The order in each tuple must be ``(name, value, vary, min, max, expr, brute_step)``. Examples -------- >>> params = Parameters() # add with tuples: (NAME VALUE VARY MIN MAX EXPR BRUTE_STEP) >>> params.add_many(('amp', 10, True, None, None, None, None), ... ('cen', 4, True, 0.0, None, None, None), ... ('wid', 1, False, None, None, None, None), ... ('frac', 0.5)) # add a sequence of Parameters >>> f = Parameter('par_f', 100) >>> g = Parameter('par_g', 2.) >>> params.add_many(f, g) """ __params = [] for par in parlist: if not isinstance(par, Parameter): par = Parameter(*par) __params.append(par) par._delay_asteval = True self.__setitem__(par.name, par) for para in __params: para._delay_asteval = False def valuesdict(self): """Return an ordered dictionary of parameter values. Returns ------- dict A dictionary of :attr:`name`::attr:`value` pairs for each Parameter. """ return {p.name: p.value for p in self.values()} def dumps(self, **kws): """Represent Parameters as a JSON string. Parameters ---------- **kws : optional Keyword arguments that are passed to `json.dumps`. Returns ------- str JSON string representation of Parameters. See Also -------- dump, loads, load, json.dumps """ params = [p.__getstate__() for p in self.values()] sym_unique = self._asteval.user_defined_symbols() unique_symbols = {key: encode4js(deepcopy(self._asteval.symtable[key])) for key in sym_unique} return json.dumps({'unique_symbols': unique_symbols, 'params': params}, **kws) def loads(self, s, **kws): """Load Parameters from a JSON string. Parameters ---------- **kws : optional Keyword arguments that are passed to `json.loads`. Returns ------- Parameters Updated Parameters from the JSON string. Notes ----- Current Parameters will be cleared before loading the data from the JSON string. See Also -------- dump, dumps, load, json.loads """ self.clear() tmp = json.loads(s, **kws) unique_symbols = {key: decode4js(tmp['unique_symbols'][key]) for key in tmp['unique_symbols']} state = {'unique_symbols': unique_symbols, 'params': []} for parstate in tmp['params']: _par = Parameter(name='') _par.__setstate__(parstate) state['params'].append(_par) self.__setstate__(state) return self def dump(self, fp, **kws): """Write JSON representation of Parameters to a file-like object. Parameters ---------- fp : file-like object An open and `.write()`-supporting file-like object. **kws : optional Keyword arguments that are passed to `dumps`. Returns ------- int Return value from `fp.write()`: the number of characters written. See Also -------- dumps, load, json.dump """ return fp.write(self.dumps(**kws)) def load(self, fp, **kws): """Load JSON representation of Parameters from a file-like object. Parameters ---------- fp : file-like object An open and `.read()`-supporting file-like object. **kws : optional Keyword arguments that are passed to `loads`. Returns ------- Parameters Updated Parameters loaded from `fp`. See Also -------- dump, loads, json.load """ return self.loads(fp.read(), **kws)
class ExpressionModel(Model): idvar_missing = "No independent variable found in\n %s" idvar_notfound = "Cannot find independent variables '%s' in\n %s" no_prefix = "ExpressionModel does not support `prefix` argument" def __init__(self, expr, independent_vars=None, init_script=None, nan_policy='raise', **kws): """Generate a model from user-supplied expression. Parameters ---------- expr : str Mathematical expression for model. independent_vars : list of str or None, optional Variable names to use as independent variables. init_script : str or None, optional Initial script to run in asteval interpreter. nan_policy : str, optional How to handle NaN and missing values in data. Must be one of: 'raise' (default), 'propagate', or 'omit'. See Notes below. missing : str, optional Synonym for 'nan_policy' for backward compatibility. **kws : optional Keyword arguments to pass to :class:`Model`. Notes ----- 1. each instance of ExpressionModel will create and using its own version of an asteval interpreter. 2. prefix is **not supported** for ExpressionModel. 3. nan_policy sets what to do when a NaN or missing value is seen in the data. Should be one of: - 'raise' : Raise a ValueError (default) - 'propagate' : do nothing - 'omit' : (was 'drop') drop missing data 4. The `missing` argument is deprecated in lmfit 0.9.8 and will be removed in a later version. Use `nan_policy` instead, as it is consistent with the Minimizer class. """ # create ast evaluator, load custom functions self.asteval = Interpreter() for name in lineshapes.functions: self.asteval.symtable[name] = getattr(lineshapes, name, None) if init_script is not None: self.asteval.eval(init_script) # save expr as text, parse to ast, save for later use self.expr = expr.strip() self.astcode = self.asteval.parse(self.expr) # find all symbol names found in expression sym_names = get_ast_names(self.astcode) if independent_vars is None and 'x' in sym_names: independent_vars = ['x'] if independent_vars is None: raise ValueError(self.idvar_missing % (self.expr)) # determine which named symbols are parameter names, # try to find all independent variables idvar_found = [False] * len(independent_vars) param_names = [] for name in sym_names: if name in independent_vars: idvar_found[independent_vars.index(name)] = True elif name not in param_names and name not in self.asteval.symtable: param_names.append(name) # make sure we have all independent parameters if not all(idvar_found): lost = [] for ix, found in enumerate(idvar_found): if not found: lost.append(independent_vars[ix]) lost = ', '.join(lost) raise ValueError(self.idvar_notfound % (lost, self.expr)) kws['independent_vars'] = independent_vars if 'prefix' in kws: raise Warning(self.no_prefix) def _eval(**kwargs): for name, val in kwargs.items(): self.asteval.symtable[name] = val return self.asteval.run(self.astcode) kws["nan_policy"] = nan_policy super(ExpressionModel, self).__init__(_eval, **kws) # set param names here, and other things normally # set in _parse_params(), which will be short-circuited. self.independent_vars = independent_vars self._func_allargs = independent_vars + param_names self._param_names = param_names self._func_haskeywords = True self.def_vals = {} def __repr__(self): """TODO: docstring in magic method.""" return "<lmfit.ExpressionModel('%s')>" % (self.expr) def _parse_params(self): """Over-write ExpressionModel._parse_params with `pass`. This prevents normal parsing of function for parameter names. """ pass
def eval(self, expr, lineno=0, show_errors=False): return OrigInterpreter.eval(self, expr, lineno=lineno, show_errors=show_errors)
class Parameters(OrderedDict): """An ordered dictionary of all the Parameter objects required to specify a fit model. All minimization and Model fitting routines in lmfit will use exactly one Parameters object, typically given as the first argument to the objective function. All keys of a Parameters() instance must be strings and valid Python symbol names, so that the name must match ``[a-z_][a-z0-9_]*`` and cannot be a Python reserved word. All values of a Parameters() instance must be Parameter objects. A Parameters() instance includes an asteval interpreter used for evaluation of constrained Parameters. Parameters() support copying and pickling, and have methods to convert to and from serializations using json strings. """ def __init__(self, asteval=None, usersyms=None, *args, **kwds): """ Arguments --------- asteval : :class:`asteval.Interpreter`, optional Instance of the asteval Interpreter to use for constraint expressions. If None, a new interpreter will be created. Warning: *deprecated, use usersyms if possible* usersyms : dictionary of symbols to add to the :class:`asteval.Interpreter`. *args : optional Arguments. **kwds : optional Keyword arguments. """ super(Parameters, self).__init__(self) self._asteval = asteval if self._asteval is None: self._asteval = Interpreter() _syms = {} _syms.update(SCIPY_FUNCTIONS) if usersyms is not None: _syms.update(usersyms) for key, val in _syms.items(): self._asteval.symtable[key] = val self.update(*args, **kwds) def copy(self): """Parameters.copy() should always be a deepcopy.""" return self.__deepcopy__(None) def __copy__(self): """Parameters.copy() should always be a deepcopy.""" return self.__deepcopy__(None) def __deepcopy__(self, memo): """Implementation of Parameters.deepcopy(). The method needs to make sure that asteval is available and that all individual Parameter objects are copied. """ _pars = Parameters(asteval=None) # find the symbols that were added by users, not during construction unique_symbols = {key: self._asteval.symtable[key] for key in self._asteval.user_defined_symbols()} _pars._asteval.symtable.update(unique_symbols) # we're just about to add a lot of Parameter objects to the newly parameter_list = [] for key, par in self.items(): if isinstance(par, Parameter): param = Parameter(name=par.name, value=par.value, min=par.min, max=par.max) param.vary = par.vary param.brute_step = par.brute_step param.stderr = par.stderr param.correl = par.correl param.init_value = par.init_value param.expr = par.expr param.user_data = par.user_data parameter_list.append(param) _pars.add_many(*parameter_list) return _pars def __setitem__(self, key, par): """TODO: add magic method docstring.""" if key not in self: if not valid_symbol_name(key): raise KeyError("'%s' is not a valid Parameters name" % key) if par is not None and not isinstance(par, Parameter): raise ValueError("'%s' is not a Parameter" % par) OrderedDict.__setitem__(self, key, par) par.name = key par._expr_eval = self._asteval self._asteval.symtable[key] = par.value def __add__(self, other): """Add Parameters objects.""" if not isinstance(other, Parameters): raise ValueError("'%s' is not a Parameters object" % other) out = deepcopy(self) params = other.values() out.add_many(*params) return out def __iadd__(self, other): """Add/assign Parameters objects.""" if not isinstance(other, Parameters): raise ValueError("'%s' is not a Parameters object" % other) params = other.values() self.add_many(*params) return self def __array__(self): """Convert Parameters to array.""" return array([float(k) for k in self.values()]) def __reduce__(self): """Reduce Parameters instance such that it can be pickled.""" # make a list of all the parameters params = [self[k] for k in self] # find the symbols from _asteval.symtable, that need to be remembered. sym_unique = self._asteval.user_defined_symbols() unique_symbols = {key: deepcopy(self._asteval.symtable[key]) for key in sym_unique} return self.__class__, (), {'unique_symbols': unique_symbols, 'params': params} def __setstate__(self, state): """Unpickle a Parameters instance. Parameters ---------- state : dict state['unique_symbols'] is a dictionary containing symbols that need to be injected into _asteval.symtable state['params'] is a list of Parameter instances to be added """ # first update the Interpreter symbol table. This needs to be done # first because Parameter's early in the list may depend on later # Parameter's. This leads to problems because add_many eventually leads # to a Parameter value being retrieved with _getval, which, if the # dependent value hasn't already been added to the symtable, leads to # an Error. Another way of doing this would be to remove all the expr # from the Parameter instances before they get added, then to restore # them. self._asteval.symtable.update(state['unique_symbols']) # then add all the parameters self.add_many(*state['params']) def eval(self, expr): """Evaluate a statement using the asteval Interpreter. Parameters ---------- expr : string An expression containing parameter names and other symbols recognizable by the asteval Interpreter. Returns ------- The result of the expression. """ return self._asteval.eval(expr) def update_constraints(self): """Update all constrained parameters, checking that dependencies are evaluated as needed.""" requires_update = {name for name, par in self.items() if par._expr is not None} updated_tracker = set(requires_update) def _update_param(name): """Update a parameter value, including setting bounds. For a constrained parameter (one with an `expr` defined), this first updates (recursively) all parameters on which the parameter depends (using the 'deps' field). """ par = self.__getitem__(name) if par._expr_eval is None: par._expr_eval = self._asteval for dep in par._expr_deps: if dep in updated_tracker: _update_param(dep) self._asteval.symtable[name] = par.value updated_tracker.discard(name) for name in requires_update: _update_param(name) def pretty_repr(self, oneline=False): """Return a pretty representation of a Parameters class. Parameters ---------- oneline : bool, optional If True prints a one-line parameters representation (default is False). Returns ------- s: str Parameters representation. """ if oneline: return super(Parameters, self).__repr__() s = "Parameters({\n" for key in self.keys(): s += " '%s': %s, \n" % (key, self[key]) s += " })\n" return s def pretty_print(self, oneline=False, colwidth=8, precision=4, fmt='g', columns=['value', 'min', 'max', 'stderr', 'vary', 'expr', 'brute_step']): """Pretty-print of parameters data. Parameters ---------- oneline : bool, optional If True prints a one-line parameters representation (default is False). colwidth : int, optional Column width for all columns specified in :attr:`columns`. precision : int, optional Number of digits to be printed after floating point. fmt : {'g', 'e', 'f'}, optional Single-character numeric formatter. Valid values are: 'f' floating point, 'g' floating point and exponential, or 'e' exponential. columns : :obj:`list` of :obj:`str`, optional List of :class:`Parameter` attribute names to print. """ if oneline: print(self.pretty_repr(oneline=oneline)) return name_len = max(len(s) for s in self) allcols = ['name'] + columns title = '{:{name_len}} ' + len(columns) * ' {:>{n}}' print(title.format(*allcols, name_len=name_len, n=colwidth).title()) numstyle = '{%s:>{n}.{p}{f}}' # format for numeric columns otherstyles = dict(name='{name:<{name_len}} ', stderr='{stderr!s:>{n}}', vary='{vary!s:>{n}}', expr='{expr!s:>{n}}', brute_step='{brute_step!s:>{n}}') line = ' '.join([otherstyles.get(k, numstyle % k) for k in allcols]) for name, values in sorted(self.items()): pvalues = {k: getattr(values, k) for k in columns} pvalues['name'] = name # stderr is a special case: it is either numeric or None (i.e. str) if 'stderr' in columns and pvalues['stderr'] is not None: pvalues['stderr'] = (numstyle % '').format( pvalues['stderr'], n=colwidth, p=precision, f=fmt) elif 'brute_step' in columns and pvalues['brute_step'] is not None: pvalues['brute_step'] = (numstyle % '').format( pvalues['brute_step'], n=colwidth, p=precision, f=fmt) print(line.format(name_len=name_len, n=colwidth, p=precision, f=fmt, **pvalues)) def _repr_html_(self): """Returns a HTML representation of parameters data.""" return params_html_table(self) def add(self, name, value=None, vary=True, min=-inf, max=inf, expr=None, brute_step=None): """Add a Parameter. Parameters ---------- name : str Name of parameter. Must match ``[a-z_][a-z0-9_]*`` and cannot be a Python reserved word. value : float, optional Numerical Parameter value, typically the *initial value*. vary : bool, optional Whether the Parameter is varied during a fit (default is True). min : float, optional Lower bound for value (default is `-numpy.inf`, no lower bound). max : float, optional Upper bound for value (default is `numpy.inf`, no upper bound). expr : str, optional Mathematical expression used to constrain the value during the fit. brute_step : float, optional Step size for grid points in the `brute` method. Examples -------- >>> params = Parameters() >>> params.add('xvar', value=0.50, min=0, max=1) >>> params.add('yvar', expr='1.0 - xvar') which is equivalent to: >>> params = Parameters() >>> params['xvar'] = Parameter(name='xvar', value=0.50, min=0, max=1) >>> params['yvar'] = Parameter(name='yvar', expr='1.0 - xvar') """ if isinstance(name, Parameter): self.__setitem__(name.name, name) else: self.__setitem__(name, Parameter(value=value, name=name, vary=vary, min=min, max=max, expr=expr, brute_step=brute_step)) def add_many(self, *parlist): """Add many parameters, using a sequence of tuples. Parameters ---------- parlist : :obj:`sequence` of :obj:`tuple` or :class:`Parameter` A sequence of tuples, or a sequence of `Parameter` instances. If it is a sequence of tuples, then each tuple must contain at least the name. The order in each tuple must be `(name, value, vary, min, max, expr, brute_step)`. Examples -------- >>> params = Parameters() # add with tuples: (NAME VALUE VARY MIN MAX EXPR BRUTE_STEP) >>> params.add_many(('amp', 10, True, None, None, None, None), ... ('cen', 4, True, 0.0, None, None, None), ... ('wid', 1, False, None, None, None, None), ... ('frac', 0.5)) # add a sequence of Parameters >>> f = Parameter('par_f', 100) >>> g = Parameter('par_g', 2.) >>> params.add_many(f, g) """ for para in parlist: if isinstance(para, Parameter): self.__setitem__(para.name, para) else: param = Parameter(*para) self.__setitem__(param.name, param) def valuesdict(self): """Return an ordered dictionary of parameter values. Returns ------- OrderedDict An ordered dictionary of :attr:`name`::attr:`value` pairs for each Parameter. """ return OrderedDict((p.name, p.value) for p in self.values()) def dumps(self, **kws): """Represent Parameters as a JSON string. Parameters ---------- **kws : optional Keyword arguments that are passed to `json.dumps()`. Returns ------- str JSON string representation of Parameters. See Also -------- dump(), loads(), load(), json.dumps() """ params = [p.__getstate__() for p in self.values()] sym_unique = self._asteval.user_defined_symbols() unique_symbols = {key: encode4js(deepcopy(self._asteval.symtable[key])) for key in sym_unique} return json.dumps({'unique_symbols': unique_symbols, 'params': params}, **kws) def loads(self, s, **kws): """Load Parameters from a JSON string. Parameters ---------- **kws : optional Keyword arguments that are passed to `json.loads()`. Returns ------- :class:`Parameters` Updated Parameters from the JSON string. Notes ----- Current Parameters will be cleared before loading the data from the JSON string. See Also -------- dump(), dumps(), load(), json.loads() """ self.clear() tmp = decode4js(json.loads(s, **kws)) state = {'unique_symbols': tmp['unique_symbols'], 'params': []} for parstate in tmp['params']: _par = Parameter() _par.__setstate__(parstate) state['params'].append(_par) self.__setstate__(state) return self def dump(self, fp, **kws): """Write JSON representation of Parameters to a file-like object. Parameters ---------- fp : file-like object An open and ``.write()``-supporting file-like object. **kws : optional Keyword arguments that are passed to `dumps()`. Returns ------- None or int Return value from `fp.write()`. None for Python 2.7 and the number of characters written in Python 3. See Also -------- dump(), load(), json.dump() """ return fp.write(self.dumps(**kws)) def load(self, fp, **kws): """Load JSON representation of Parameters from a file-like object. Parameters ---------- fp : file-like object An open and ``.read()``-supporting file-like object. **kws : optional Keyword arguments that are passed to `loads()`. Returns ------- :class:`Parameters` Updated Parameters loaded from `fp`. See Also -------- dump(), loads(), json.load() """ return self.loads(fp.read(), **kws)