Пример #1
0
    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
Пример #2
0
    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}")
Пример #3
0
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)
Пример #4
0
 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.')
Пример #5
0
 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
Пример #6
0
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))
Пример #7
0
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
Пример #8
0
    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)
Пример #9
0
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
Пример #10
0
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)
Пример #11
0
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)
Пример #12
0
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
Пример #13
0
 def eval(self, expr, lineno=0, show_errors=False):
     return OrigInterpreter.eval(self,
                                 expr,
                                 lineno=lineno,
                                 show_errors=show_errors)
Пример #14
0
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)