Exemplo n.º 1
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 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 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)
Exemplo n.º 2
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)