# -*- coding: utf-8 -*-
#
# 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
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
""" Module implementing some useful operations over tensors and random variables """
import numpy as np
import inferpy.models
import tensorflow as tf
import collections
import six
[docs]def param_to_tf(x):
""" Transforms either a scalar or a random variable into a Tensor"""
if np.isscalar(x):
return tf.constant(x, dtype="float32")
elif isinstance(x, inferpy.models.RandomVariable):
return x.base_object
else:
raise ValueError("wrong input value in param_to_tf")
[docs]def case_states(var, d, default=None, exclusive=True, strict=False, name='case'):
""" Control flow operation depending of the outcome of a discrete variable.
Internally, the operation tensorflow.case is invoked. Unlike the tensorflow operation, this one
accepts InferPy variables as input parameters.
Args:
var: Control InferPy discrete random variable.
d : dictionary where the keys are each of the possible values of control variable
and the values are returning tensors for each case.
exclusive: True iff at most one case is allowed to evaluate to True.
name: name of the resulting tensor.
Return:
Tensor implementing the case operation. This is the output of the operation
tensorflow.case internally invoked.
"""
out_d = {}
if not isinstance(var, inferpy.models.RandomVariable):
var = inferpy.models.Deterministic(var)
def f(p): return tf.constant(p)
for s, p in six.iteritems(d):
out_d.update({tf.reduce_all(tf.equal(var.dist, tf.constant(s))): (lambda pp : lambda: f(pp))(p)})
return tf.case(out_d, default=default, exclusive=exclusive,strict=strict,name=name)
[docs]def case(d, default=None, exclusive=True, strict=False, name='case'):
""" Control flow operation depending of the outcome of a tensor. Any expression
in tensorflow giving as a result a boolean is allowed as condition.
Internally, the operation tensorflow.case is invoked. Unlike the tensorflow operation, this one
accepts InferPy variables as input parameters.
Args:
d : dictionary where the keys are the conditions (i.e. boolean tensor).
exclusive: True iff at most one case is allowed to evaluate to True.
name: name of the resulting tensor.
Return:
Tensor implementing the case operation. This is the output of the operation
tensorflow.case internally invoked.
"""
out_d = {}
def f(p): return tf.constant(p)
for c, p in six.iteritems(d):
out_d.update({tf.reduce_all(tf.equal(c.base_object, True)): (lambda pp : lambda: f(pp))(p)})
if default != None:
default = (lambda pp : lambda: f(pp))(default)
return tf.case(out_d, default=default, exclusive=exclusive,strict=strict,name=name)
[docs]def gather(
params,
indices,
validate_indices=None,
name=None,
axis=0 ):
""" Operation for selecting some of the items in a tensor.
Internally, the operation tensorflow.gather is invoked. Unlike the tensorflow operation, this one
accepts InferPy variables as input parameters.
Args:
params: A Tensor. The tensor from which to gather values. Must be at least rank axis + 1.
indices: A Tensor. Must be one of the following types: int32, int64. Index tensor. Must be in range
[0, params.shape[axis]).
axis: A Tensor. Must be one of the following types: int32, int64. The axis in params to gather indices
from. Defaults to the first dimension. Supports negative indexes.
name: A name for the operation (optional).
Return:
A Tensor. Has the same type as params.. This is the output of the operation
tensorflow.gather internally invoked.
"""
tf_params = params.base_object if isinstance(params, inferpy.models.RandomVariable)==True else params
tf_indices = indices.base_object if isinstance(indices, inferpy.models.RandomVariable) == True else indices
return tf.gather(tf_params, tf_indices, validate_indices, name, axis)
[docs]def matmul(
a,
b,
transpose_a=False,
transpose_b=False,
adjoint_a=False,
adjoint_b=False,
a_is_sparse=False,
b_is_sparse=False,
name=None):
""" Matrix multiplication.
Input objects may be tensors but also InferPy variables.
Args:
a: Tensor of type float16, float32, float64, int32, complex64, complex128 and rank > 1.
b: Tensor with same type and rank as a.
transpose_a: If True, a is transposed before multiplication.
transpose_b: If True, b is transposed before multiplication.
adjoint_a: If True, a is conjugated and transposed before multiplication.
adjoint_b: If True, b is conjugated and transposed before multiplication.
a_is_sparse: If True, a is treated as a sparse matrix.
b_is_sparse: If True, b is treated as a sparse matrix.
name: Name for the operation (optional).
Retruns:
An InferPy variable of type Deterministic encapsulating the resulting tensor
of the multiplications.
"""
res = inferpy.models.Deterministic()
a_shape = shape_to_list(a)
b_shape = shape_to_list(b)
if isinstance(a, inferpy.models.RandomVariable):
a = a.base_object
if isinstance(b, inferpy.models.RandomVariable):
b = b.base_object
a = a if len(a_shape) > 1 else tf.reshape(a, [1] + a_shape)
b = b if len(b_shape) > 1 else tf.reshape(b, [1] + b_shape)
res.base_object = tf.matmul(a, b, transpose_a, transpose_b, adjoint_a, adjoint_b, a_is_sparse, b_is_sparse, name)
return res
[docs]def dot(x,y):
""" Compute dot product between an InferPy or Tensor object. The number of batches N equal to 1
for one of them, and higher for the other one.
If necessarily, the order of the operands may be changed.
Args:
x: first operand. This could be an InferPy variable, a Tensor, a numpy object or a numeric Python list.
x: second operand. This could be an InferPy variable, a Tensor, a numpy object or a numeric Python list.
Retruns:
An InferPy variable of type Deterministic encapsulating the resulting tensor
of the multiplications.
"""
x_shape = shape_to_list(x)
y_shape = shape_to_list(y)
if len(x_shape) == 1 and len(y_shape)==2:
a = y
b = x
elif len(x_shape) == 2 and len(y_shape) == 1:
a = x
b = y
else:
raise ValueError("Wrong dimensions")
return matmul(a, b, transpose_b=True)
[docs]def shape_to_list(a):
""" Transforms the shape of an object into a list
Args:
a : object whose shape will be transformed. This could be an InferPy variable, a Tensor, a numpy object or a numeric Python list.
"""
if isinstance(a, inferpy.models.RandomVariable):
a_shape = a.shape
elif isinstance(a, np.ndarray):
a_shape = list(a.shape)
elif isinstance(a, list):
a_shape = list(np.shape(a))
elif isinstance(a, tf.Tensor):
a_shape = a._shape_as_list()
else:
raise ValueError("Wrong input type "+a)
return a_shape
[docs]def fix_shape(s):
""" Transforms a shape list into a standard InferPy shape format. """
ret = []
for i in range(0,len(s)):
if i in [0, len(s)-1] or s[i] != 1:
ret.append(s[i])
if len(ret) == 0:
return [1]
return ret