Revision aeb0ab472296c0298c2b007c30af2705a75a89f8 authored by ST John on 18 June 2019, 09:46:26 UTC, committed by ST John on 18 June 2019, 09:48:10 UTC
1 parent 4ad6260
Raw File
# Copyright 2016 James Hensman, Mark van der Wilk,
#                Valentine Svensson, alexggmatthews,
#                PabloLeon, fujiisoup
# Copyright 2017 Artem Artemev @awav
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# See the License for the specific language governing permissions and
# limitations under the License.

import pandas as pd
import tensorflow as tf

from .dataholders import DataHolder
from .parameter import Parameter
from .. import misc
from .. import settings
from ..core.autoflow import AutoFlow
from ..core.compilable import Build
from ..core.errors import GPflowError
from ..core.node import Node
from ..core.tensor_converter import TensorConverter

class Parameterized(Node):
    Parameterized object represents a set of computations over children nodes and
    one of the main purposes is to store these children node like objects.
    They can be parameters, data holders or even another parameterized objects.
    Parameterized object links to childrens via python object attributes, changing
    their parentable names.

    p = gpflow.Parameterized()
    # 'Parameterized'

    p.a = gpflow.Param(0)
    # 'Parameter'
    # ^^^ This is explained by the fact that the parameter is
    #     constructed before assignement.

    All parameters, data holders and other parameterized objects which are created
    inside parameterized __init__ method will be built in compliant build order of
    the parameterized object which was initiating construction.

    class Demo(gpflow.Parameterized):
        def __init__(self):
            self.a = gpflow.Param(0)

    demo = Demo()
    # 'Demo'

    # 'Demo/a'


    * Empty parameterized object, in other words without any node like attributes,
      always has status `Build.YES`.
    * If assignee object has been built, right before assign operation, its tensor
      name will not change its name according to new tree structure.

    :param name: Parentable name of the object. Class name is used, when name is None.

    def __init__(self, name=None):
        super(Parameterized, self).__init__(name=name)
        self._prior_tensor = None

    def _children(self):
        allowed = lambda x: self._is_param_like(x) and x is not self.parent
        children = {n: v for n, v in self.__dict__.items() if allowed(v)}
        return children

    def _store_child(self, name, child):
        object.__setattr__(self, name, child)

    def _remove_child(self, name, child):
        object.__delattr__(self, name)

    def params(self):
        for key, param in sorted(self.__dict__.items()):
            if not key.startswith('_') and Parameterized._is_param_like(param):
                yield param

    def _non_empty_params(self):
        for param in self.params:
            if isinstance(param, Parameterized) and param.empty:
            yield param

    def empty(self):
        parameters = bool(list(self.parameters))
        data_holders = bool(list(self.data_holders))
        return not (parameters or data_holders)

    def parameters(self):
        for param in self.params:
            if isinstance(param, Parameterized):
                for sub_param in param.parameters:
                    yield sub_param
            elif not isinstance(param, DataHolder):
                yield param

    def data_holders(self):
        for param in self.params:
            if isinstance(param, Parameterized):
                for sub_param in param.data_holders:
                    yield sub_param
            elif isinstance(param, DataHolder):
                yield param

    def trainable_parameters(self):
        for parameter in self.parameters:
            if parameter.trainable:
                yield parameter

    def trainable_tensors(self):
        return [param.parameter_tensor for param in self.trainable_parameters]

    def prior_tensor(self):
        return self._prior_tensor

    def feeds(self):
        total_feeds = {}
        for data_holder in self.data_holders:
            holder_feeds = data_holder.feeds
            if holder_feeds is not None:
        return total_feeds

    def initializables(self):
        def get_initializables(param_gen, inits):
            for param in param_gen:
                tensors = param.initializables
                if tensors is not None:
                    inits += tensors

        inits = []
        get_initializables(self.parameters, inits)
        get_initializables(self.data_holders, inits)
        return inits

    def initializable_feeds(self):
        def get_initializable_feeds(param_gen, feeds):
            for param in param_gen:
                param_feeds = param.initializable_feeds
                if param_feeds is not None:

        feeds = {}
        get_initializable_feeds(self.parameters, feeds)
        get_initializable_feeds(self.data_holders, feeds)
        return feeds

    def graph(self):
        for param in self.params:
            if param.graph is not None:
                return param.graph
        return None

    def trainable(self):
        for parameter in self.parameters:
            if parameter.trainable:
                return True
        return False

    def trainable(self, value):

    def fix_shape(self, parameters=True, data_holders=True):
        if parameters:
            for parameter in self.parameters:
        if data_holders:
            for data_holder in self.data_holders:

    def assign(self, values, session=None, force=True):
        if not isinstance(values, (dict, pd.Series)):
            raise ValueError('Input values must be either dictionary or panda '
                             'Series data structure.')
        if isinstance(values, pd.Series):
            values = values.to_dict()
        params = {param.pathname: param for param in self.parameters}
        val_keys = set(values.keys())
        if not val_keys.issubset(params.keys()):
            keys_not_found = val_keys.difference(params.keys())
            raise ValueError('Input values are not coherent with parameters. '
                             'These keys are not found: {}.'.format(keys_not_found))
        prev_values = {}
        for key in val_keys:
                param = params[key]
                prev_value = param.read_value().copy()
                param.assign(values[key], session=session, force=force)
                prev_values[key] = prev_value
            except (GPflowError, ValueError):
                for rkey, rvalue in prev_values.items():
                    params[rkey].assign(rvalue, session=session, force=True)

    def anchor(self, session):
        if not isinstance(session, tf.Session):
            raise ValueError('TensorFlow session expected when anchoring.')
        for parameter in self.trainable_parameters:

    def read_trainables(self, session=None):
        return {param.pathname: param.read_value(session)
                for param in self.trainable_parameters}

    def read_values(self, session=None):
        return {param.pathname: param.read_value(session)
                for param in self.parameters}

    def is_built(self, graph):
        if not isinstance(graph, tf.Graph):
            raise ValueError('TensorFlow graph expected for checking build status.')
        statuses = set([param.is_built(graph) for param in self._non_empty_params])
        if Build.NOT_COMPATIBLE_GRAPH in statuses:
            return Build.NOT_COMPATIBLE_GRAPH
        elif Build.NO in statuses:
            return Build.NO
        elif self.prior_tensor is None and list(self.parameters):
            return Build.NO
        return Build.YES

    def set_trainable(self, value):
        if not isinstance(value, bool):
            raise ValueError('Boolean value expected.')
        for param in self.params:
            if not isinstance(param, DataHolder):

    def as_pandas_table(self):
        df = None
        for parameter in self.parameters:
            if isinstance(parameter, DataHolder):
            param_table = parameter.as_pandas_table()
            df = df.append(param_table) if df is not None else param_table
        return df

    def _is_param_like(value):
        return isinstance(value, (Parameter, Parameterized))

    def _tensor_mode_parameter(obj):
        if isinstance(obj, Parameter):
            if isinstance(obj, DataHolder):
                return obj.parameter_tensor
            return obj.constrained_tensor

    def _clear(self):
        self._prior_tensor = None
        for param in self.params:
            param._clear()  # pylint: disable=W0212

    def _build(self):
        for param in self.params:
        priors = []
        for param in self.params:
            if not isinstance(param, DataHolder):
                if isinstance(param, Parameterized) and param.prior_tensor is None:
        self._prior_tensor = self._build_prior(priors)

    def _build_prior(self, prior_tensors):
        Build a tf expression for the prior by summing all child-parameter priors.
        # TODO(@awav): What prior must represent empty list of parameters?
        if not prior_tensors:
            return tf.constant(0, dtype=settings.float_type)
        return tf.add_n(prior_tensors, name='prior')

    def _get_node(self, name):
        return getattr(self, name)

    def _update_node(self, name, value):
        param = self._get_node(name)
        if Parameterized._is_param_like(value):
            if param is not value:
                self._replace_node(name, param, value)
        elif isinstance(param, Parameter) and misc.is_valid_param_value(value):
            msg = '"{0}" type cannot be assigned to "{1}".'
            raise ValueError(msg.format(type(value), name))

    def _replace_node(self, name, old, new):
        self._unset_child(name, old)
        self._set_node(name, new)

    def _set_node(self, name, value):
        if not self.empty and self.is_built_coherence(value.graph) is Build.YES:
            raise GPflowError('Tensors for this object are already built and cannot be modified.')
        self._set_child(name, value)

    def __getattribute__(self, name):
        attr = misc.get_attribute(self, name)
        if isinstance(attr, Parameter) and TensorConverter.tensor_mode(self):
            return Parameterized._tensor_mode_parameter(attr)
        return attr

    def __setattr__(self, name, value):
        if name.startswith('_'):
            object.__setattr__(self, name, value)

        if self.root is value:
            raise ValueError('Cannot be assigned as parameter to itself.')

        if name in self.__dict__.keys():
            assignee_param = getattr(self, name)
            if Parameterized._is_param_like(assignee_param):
                self._update_node(name, value)

        if Parameterized._is_param_like(value):
            self._set_node(name, value)

        object.__setattr__(self, name, value)

    def __str__(self):
        return str(self.as_pandas_table())

    def _repr_html_(self):
        return self.as_pandas_table()._repr_html_()
back to top