from __future__ import absolute_import, division, print_function, unicode_literals
import six
import re
import fileinput
import sys
import collections
import chaospy as cp
__all__ = ["Parameters", "Parameter"]
[docs]class Parameter(object):
"""
Parameter object, contains name of parameter, value of parameter and distribution of parameter.
Parameters
----------
name: str
Name of the parameter.
value: float, int, None
The fixed value of the parameter. If you give a parameter a distribution,
in most cases you do not need to give it a fixed value.
distribution: {None, Chaospy distribution, Function that returns a Chaospy distribution}, optional
The distribution of the parameter. A parameter is considered uncertain
if it has a distribution.
Defaults to None.
Attributes
----------
name: str
Name of the parameter.
value: float, int
The value of the parameter.
distribution : uncertainpy.Parameter.distribution
The distribution of the parameter. A parameter is considered uncertain
if it has a distribution.
"""
def __init__(self, name, value=None, distribution=None):
self.name = name
self.value = value
self._distribution = None
self.distribution = distribution
@property
def distribution(self):
"""
A Chaospy distribution or a function that returns a Chaospy distribution.
If None the parameter has no distribution and is not considered uncertain.
Parameters
----------
distribution: {None, Chaospy distribution, callable that returns a Chaospy distribution}, optional
The distribution of the parameter, used if the parameter is uncertain
If it is a callable that returns a Chaospy distribution, the
function sends `value` value to the function.
Defaults to None.
Returns
-------
distribution: {Chaospy distribution, None}
The distribution of the parameter, if None the
parameter has no distribution and is not considered uncertain.
"""
return self._distribution
@distribution.setter
def distribution(self, new_distribution):
if new_distribution is None:
self._distribution = None
elif isinstance(new_distribution, cp.Distribution):
self._distribution = new_distribution
elif hasattr(new_distribution, '__call__'):
if self.value is None:
raise ValueError("The value of this parameter is None. A function cannot be created with new_distribution(self.value).")
self._distribution = new_distribution(self.value)
if not isinstance(self._distribution, cp.Distribution):
raise TypeError("Function new_distribution does not return a Chaospy distribution")
else:
raise TypeError("new_distribution is neither a function nor a Chaospy distribution")
[docs] def set_parameter_file(self, filename, value):
"""
Set parameters to given value in a parameter file.
Search `filename` for occurrences of ``name = number``
and replace ``number`` with `value`.
Parameters
----------
filename: str
Name of file.
value: float, int
New value to set in parameter file.
"""
search_string = r"(\A|\b)(" + self.name + r")(\s*=\s*)((([+-]?\d+[.]?\d*)|([+-]?\d*[.]?\d+))([eE][+-]?\d+)*)($|\b)"
pattern = re.compile(search_string)
for line in fileinput.input(filename, inplace=True):
sys.stdout.write(pattern.sub(r"\g<1>\g<2>\g<3>" + str(value), line))
[docs] def reset_parameter_file(self, filename):
"""
Set all parameters to the original value in the parameter file, `filename`.
Parameters
----------
filename: str
Name of file.
"""
if self.value is None:
raise ValueError("The value of this parameter is None. The parameter file cannot be reset.")
self.set_parameter_file(filename, self.value)
[docs] def __str__(self):
"""
Return a readable string describing the parameter.
Returns
-------
str
A string containing ``name``, ``value``, and if a parameter is uncertain.
"""
if self.distribution is None:
uncertain = ""
else:
uncertain = " - Uncertain"
return "{parameter}: {value}{uncertain}".format(parameter=self.name, value=self.value, uncertain=uncertain)
[docs]class Parameters(collections.MutableMapping):
"""
A collection of parameters.
Has all standard dictionary methods implemented, such as items, value,
contains and similar implemented. As such, behaves as an ordered dictionary.
Parameters
----------
parameters: {dict {name: parameter_object}, dict of {name: value or Chaospy distribution}, ...], list of Parameter instances, list [[name, value or Chaospy distribution], ...], list [[name, value, Chaospy distribution or callable that returns a Chaospy distribution],...],}
List or dictionary of the parameters that should be created.
On the form ``parameters =``
* ``{name_1: parameter_object_1, name: parameter_object_2, ...}``
* ``{name_1: value_1 or Chaospy distribution, name_2: value_2 or Chaospy distribution, ...}``
* ``[parameter_object_1, parameter_object_2, ...]``,
* ``[[name_1, value_1 or Chaospy distribution], ...]``.
* ``[[name_1, value_1, Chaospy distribution or callable that returns a Chaospy distribution], ...]``
distribution: {None, multivariate Chaospy distribution}, optional
A multivariate distribution of all parameters, if it exists, it is used
instead of individual distributions.
Defaults to None.
Attributes
----------
parameters: dict
A dictionary of parameters with ``name`` as key and Parameter object as value.
distribution: {None, multivariate Chaospy distribution}, optional
A multivariate distribution of all parameters, if it exists, it is used
instead of individual distributions. Defaults to None.
Notes
-----
Both parameter values and parameter distributions must be set if
uncertainpy.UncertaintyQuantification.quantify is run with single=True,
meaning the uncertainty quantification should be performed with only one
uncertain parameter at the time.
See Also
--------
uncertainpy.Parameter
"""
def __init__(self, parameters={}, distribution=None):
self.parameters = collections.OrderedDict()
self.distribution = distribution
try:
# Handle dict
if isinstance(parameters, dict):
for parameter in parameters:
if isinstance(parameters[parameter], Parameter):
self.parameters[parameter] = parameters[parameter]
else:
if isinstance(parameters[parameter], cp.Distribution):
self.parameters[parameter] = Parameter(parameter, distribution=parameters[parameter])
else:
self.parameters[parameter] = Parameter(parameter, value=parameters[parameter])
else:
# Handle lists
for parameter in parameters:
if isinstance(parameter, Parameter):
self.parameters[parameter.name] = parameter
else:
if len(parameter) == 2:
if isinstance(parameter[1], cp.Distribution):
self.parameters[parameter[0]] = Parameter(parameter[0], distribution=parameter[1])
else:
self.parameters[parameter[0]] = Parameter(parameter[0], value=parameter[1])
else:
self.parameters[parameter[0]] = Parameter(*parameter)
except TypeError as error:
msg = "Input to parameters is on the wrong format."
if not error.args:
error.args = ("",)
error.args = error.args + (msg,)
raise
[docs] def __getitem__(self, name):
"""
Return Parameter object with `name`.
Parameters
----------
name: str
Name of parameter.
Returns
-------
Parameter object
The parameter object with `name`.
"""
return self.parameters[name]
[docs] def __iter__(self):
"""
Iterate over the parameter objects.
Yields
------
Parameter object
A parameter object.
"""
return iter(self.parameters.values())
[docs] def __str__(self):
"""
Convert all parameters to a readable string.
Returns
-------
str
A readable string of all parameter objects.
"""
result = ""
for name in self.parameters.keys():
result += str(self.parameters[name]) + "\n"
return result.strip()
[docs] def __len__(self):
"""
Get the number of parameters.
Returns
-------
int
The number of parameters.
"""
return len(self.parameters)
[docs] def __setitem__(self, name, parameter):
"""
Set parameter with `name`.
Parameters
----------
name: str
Name of parameter.
parameter: Parameter object
The parameter object of `name`.
"""
if not isinstance(parameter, Parameter):
raise ValueError("parameter must be an instance of Parameter")
self.parameters[name] = parameter
[docs] def __delitem__(self, name):
"""
Delete parameter with `name`.
Parameters
----------
name: str
Name of parameter.
"""
del self.parameters[name]
[docs] def set_distribution(self, parameter, distribution):
"""
Set the distribution of a parameter.
Parameters
----------
parameter: str
Name of parameter.
distribution: {None, Chaospy distribution, Function that returns a Chaospy distribution}
The distribution of the parameter.
"""
self.parameters[parameter].distribution = distribution
[docs] def set_all_distributions(self, distribution):
"""
Set the distribution of all parameters.
Parameters
----------
distribution: {None, Chaospy distribution, Function that returns a Chaospy distribution}
The distribution of the parameter.
"""
for parameter in self.parameters:
self.parameters[parameter].distribution = distribution
[docs] def get_from_uncertain(self, attribute="name"):
"""
Return attributes from uncertain parameters.
Return a list of attributes (``name``, ``value``, or ``distribution``) from
each uncertain parameters (parameters that have a distribution).
Parameters
----------
attribute: {"name", "value", "distribution"}, optional
The name of the attribute to be returned from each uncertain parameter.
Default is `name`.
Returns
-------
list
List containing the `attribute` of each uncertain parameters.
"""
items = []
for parameter in self.parameters.values():
if parameter.distribution is not None:
items.append(getattr(parameter, attribute))
return items
[docs] def get(self, attribute="name", parameter_names=None):
"""
Return attributes from all parameters.
Return a list of attributes (``name``, ``value``, or ``distribution``) from
each parameters (parameters that have a distribution).
Parameters
----------
attribute: {"name", "value", "distribution"}, optional
The name of the attribute to be returned from each uncertain parameter. Default is `name`.
parameter_names: {None, list, str}, optional
A list of all parameters of which attribute should be returned,
or a string for a single parameter.
If None, the attribute all parameters are returned.
Default is None.
Returns
-------
list
List containing the `attribute` of each parameters.
"""
if parameter_names is None:
parameter_names = self.parameters.keys()
if isinstance(parameter_names, six.string_types):
parameter_names = [parameter_names]
return_parameters = []
for parameter_name in parameter_names:
return_parameters.append(self.parameters[parameter_name])
return [getattr(parameter, attribute) for parameter in return_parameters]
[docs] def set_parameters_file(self, filename, parameters):
"""
Set listed parameters to their value in a parameter file.
For each parameter listed in `parameters`, search `filename` for occurrences of
``parameter_name = number`` and replace ``number`` with value of that parameter.
Parameters
----------
filename: str
Name of file.
parameters: list
List of parameter names.
"""
for parameter in parameters:
self.parameters[parameter].set_parameter_file(filename, parameters[parameter])
[docs] def reset_parameter_file(self, filename):
"""
Set all parameters to their value in a parameter file.
For all parameters, search `filename` for occurrences of
``parameter_name = number`` and replace ``number`` with value of that parameter.
Parameters
----------
filename: str
Name of file.
"""
for parameter in self.parameters:
self.parameters[parameter].set_parameter_file(filename, self.parameters[parameter].value)