"""Definitions for the `Parameter` class."""
import numpy as np
from collections import OrderedDict
from mosfit.modules.module import Module
from mosfit.utils import listify
# Important: Only define one ``Module`` class per file.
[docs]class Parameter(Module):
"""Model parameter that can either be free or fixed."""
def __init__(self, **kwargs):
"""Initialize module."""
super(Parameter, self).__init__(**kwargs)
self._fixed = kwargs.get('fixed', False)
self._fixed_by_user = False
self._max_value = kwargs.get('max_value', None)
self._min_value = kwargs.get('min_value', None)
if (self._min_value is not None and self._max_value is not None and
self._min_value == self._max_value):
self._printer.message('min_max_same', [self._name], warning=True)
self._value = self._min_value
self._min_value, self._max_value = None, None
self._fixed = True
self._fixed_by_user = True
if self._min_value is None or self._max_value is None:
self._fixed = True
self._fixed_by_user = True
self._value = kwargs.get('value', None)
self._log = kwargs.get('log', False)
self._latex = kwargs.get('latex', self._name)
self._derived_keys = listify(kwargs.get('derived_keys', [])) + [
'reference_' + self._name]
if (self._log and self._min_value is not None and
self._max_value is not None):
if self._min_value <= 0.0 or self._max_value <= 0.0:
raise ValueError(
'Parameter with log prior cannot have range values <= 0!')
self._min_value = np.log(self._min_value)
self._max_value = np.log(self._max_value)
self._reference_value = None
self._clipped_warning = False
[docs] def fix_value(self, value):
"""Fix value of parameter."""
self._max_value = None
self._min_value = None
self._value = value
self._fixed = True
self._fixed_by_user = True
[docs] def is_log(self):
"""Return if `Parameter`'s value is stored as log10(value)."""
return self._log
[docs] def latex(self):
"""Return the LaTeX representation of the parameter."""
return self._latex
[docs] def lnprior_pdf(self, x):
"""Evaluate natural log of probability density function."""
return 0.0
[docs] def prior_icdf(self, u):
"""Evaluate inverse cumulative density function."""
return u
[docs] def value(self, f):
"""Return the value of the parameter in parameter's units."""
if np.any(np.isnan(f)):
raise ValueError('NaN fraction passed to parameter.')
value = np.clip(f *
(self._max_value - self._min_value) + self._min_value,
self._min_value, self._max_value)
if self._log:
value = np.exp(value)
return value
[docs] def fraction(self, value, clip=True):
"""Return fraction given a parameter's value."""
if self._log:
value = np.log(value)
f = (value - self._min_value) / (self._max_value - self._min_value)
if clip:
of = f
f = np.clip(f, 0.0, 1.0)
if f != of and not self._clipped_warning:
self._clipped_warning = True
self._printer.message(
'parameter_clipped', [self._name], warning=True)
return f
[docs] def get_derived_keys(self):
"""Return list of keys that should be generated by this parameter."""
return self._derived_keys
[docs] def process(self, **kwargs):
"""Process module.
Initialize a parameter based upon either a fixed value or a
distribution, if one is defined.
"""
if self._fixed:
# If this parameter is not free and is already set, then skip
if self._name in kwargs:
return {}
value = self._value
else:
value = self.value(kwargs['fraction'])
output = OrderedDict([[self._name, value]])
if self._reference_value is not None:
output['reference_' + self._name] = self._reference_value
return output
[docs] def receive_requests(self, **requests):
"""Receive requests from other ``Module`` objects."""
# Get the first value in the requests dictionary.
req_keys = list(requests.keys())
if req_keys:
self._reference_value = requests.get(req_keys[0], None)