Source code for binf

"""
Future Binf stuff goes here. Currently, the major focus is to redesign the
Universe and access to atoms, molecules etc. Also Posterior and BinfSampler
will be redesigned at some point.
"""
__version__ = '2.0.0'

from abc import ABCMeta, abstractmethod

import numpy
import numpy as np, sys

from csb.statistics.pdf.parameterized import AbstractParameter


[docs]class AbstractBinfNamedCallable(object): __metaclass__ = ABCMeta def __init__(self, name): """ Class defining the interface functions taking named and typed arguments ('variables') :param name: some unique name for this object :type name: str """ self._name = name self._variables = set() self._differentiable_variables = set() self._var_param_types = {} self._original_variables = set()
[docs] def _set_original_variables(self): """ Stores the original set of named variables this object takes """ self._original_variables.update(self.variables)
[docs] def _register_variable(self, name, differentiable=False): """ Registers a variable so that later on it can be fixed or checked whether it has been passed to the __call__ method :param name: name of the new variable :type name: str :param differentiable: True for variables you might at one point want to take the gradient w.r.t. This is probably deprecated. :type differentiable: bool """ if type(name) != str: raise ValueError('Variable name must be a string, not ' + type(name)) elif name in self._variables: raise ValueError('Variable name \"' + name + '\" must be unique') else: self._variables.add(name) if differentiable: self._differentiable_variables.add(name)
[docs] def _delete_variable(self, name): """ Removes a variable from the set of registed variables :param name: name of the variable to be removed :type name: str """ if name in self._variables: self._variables.remove(name) if name in self.differentiable_variables: self._differentiable_variables.remove(name) else: raise ValueError('\"' + name + '\": unknown variable name')
@property def variables(self): """ Returns the set of currently registed variables :returns: set of currently registered variables :rtype: set """ return self._variables @property def differentiable_variables(self): """ Returns the set of currently registered variables this object implements the gradient w.r.t :returns: set of variables this object can be differentiated w.r.t. :rtype: set """ return self._differentiable_variables @property def name(self): """ Returns the name of this object """ return self._name def __call__(self, **variables): """ Evaluates the function described by this object :returns: function value :rtype: unknown """ if len(variables) != len(self.variables): msg = 'Function called with {}' + \ 'arguments instead of {}!'.format(len(variables), len(self.variables)) raise ValueError(msg) self._complete_variables(variables) result = self._evaluate(**variables) return result
[docs] @abstractmethod def _evaluate(self, **variables): r""" In this method, the actual function evaluation takes place The variables argument holds values for both fixed and unfixed variables; the implementation in this method thus does not depend on whether the set of originally passed variables equals the set of original variables :param \**variables: list of variable name / value pairs """ pass
[docs] def _check_differentiability(self, **variables): """ Checks whether this object can be differentiated w.r.t. specific variables """ if len(variables.viewkeys() & set(self._differentiable_variables)) == 0: msg = 'Function cannot be differentiated w.r.t.' + \ 'any of the variables '+variables.keys() raise ValueError(msg)
[docs] def _evaluate_gradient(self, **variables): r""" In this method, the actual gradient evaluation takes place The variables argument holds values for both fixed and unfixed variables; the implementation in this method thus does not depend on whether the set of originally passed variables equals the set of original variables :param \**variables: list of variable name / value pairs """ raise NotImplementedError
[docs] def gradient(self, **variables): r""" This function will be called from outside to evaluate the gradient of the function (or a related one, e.g., log-probability) represented by this object :param \**variables: list of variable name / value pairs :returns: gradient of the function represented by this object :rtype: :class:`numpy.ndarray` """ if len(variables) != len(self.variables): msg = 'Function called with {}' + \ 'arguments instead of {}!'.format(len(variables), len(self.variables)) raise ValueError(msg) self._complete_variables(variables) result = self._evaluate_gradient(**variables) return result
[docs] @abstractmethod def _complete_variables(self, variables): """ If needed, updates the dictionary of variables passed to this object when calling evaluate() with values for fixed variables """ pass
[docs] def _get_variables_intersection(self, test_variables): """ Returns the intersection of the variables stored in the argument with the set of currently registered variables """ return {k: v for k, v in test_variables.items() if k in self.variables}
@property def var_param_types(self): ''' Empty by default, but for a conditional PDF to be built, one has to add AbstractParameter subclasses suiting the variables. ''' return self._var_param_types.copy()
[docs] def update_var_param_types(self, **values): """ Updates the dictionary holding the types of registered variables """ self._var_param_types.update(**values)
[docs] def fix_variables(self, **fixed_vars): """ Sets ('fixes') specific variables to values given as keyword arguments by removing these variables from the list of registered variables and registering corresponding parameters to this object. """ for v in fixed_vars: if v in self.variables: self._delete_variable(v) self._register(v) if v in self.var_param_types: self[v] = self.var_param_types[v](fixed_vars[v], v) else: msg = 'Parameter type for variable "{}" not defined'.format(v) raise ValueError(msg) else: msg = '{} is not a variable of {}'.format(self.__repr__(), v) raise ValueError(msg)
# @abstractmethod # def fix_variables(self, **fixed_vars): # """ # Sets ('fixes') specific variables to values given as keyword # arguments # """ # pass
[docs]class ArrayParameter(AbstractParameter):
[docs] def _validate(self, value): try: return numpy.array(value) except(TypeError, ValueError): raise ParameterValueError(self.name, value)