IAM

OPENSOURCEFAN STUDYING
STUDYINGCOMPUTERSCIENCEANDMATH COMPUTERSCIENCE

Check out the latest superpixel benchmark — Superpixel Benchmark (2016) — and let me know your opinion! @david_stutz
19thFEBRUARY2017

SNIPPET

Following the Theano documentation, this snippet illustrates the creation of a new Theano type, namely the Double type. Based on this type, the add operation is implemented. Originally, I intended this as a quick tutorial on how to define more complex types with differentiable operations. However, as also discussed here, this turned out to be more involved than expected.

double.py
"""
Creating a double type for theano following http://deeplearning.net/software/theano/extending/type.html.
Also see for discussion: http://stackoverflow.com/questions/41858327/how-to-define-custom-theano-types-allowing-differentiation
"""

import theano

class Double(theano.gof.Type):
    """
    Double type for theano.
    """

    dtype = 'float64'

    def filter(self, value, strict = False, allow_downcast = None):
        """
        Cast to double.
        """

        if strict:
            # we need to return a type, but if the value is incompatible raise an exception
            if isinstance(value, float):
                return value
            else:
                raise TypeError('Expected a float!')
        elif allow_downcast:
            return float(value)
        else:
            value_float = float(value)
            if value_float == value:
                return value_float
            else:
                raise TypeError('The double type cannot be accurately represent %s of type %s' % (value, type(value)))

    def values_eq_approx(self, value_a, value_b, tolerance = 1e-6):
        """
        Check whether values are approximately equal.
        """

        return abs(value_a - value_b) / (abs(value_a) + abs(value_b)) < tolerance

    def ones_like(self, model, dtype):
        print(model)
        print(dtype)
        return double('1')

    # using this method causes the type to not be hashable anymore?! - strange ...
    #def __eq__(self, value):
        """
        To allow using ==.
        """
        #return type(self) is Double and type(value) is Double

# is already used in the operations below
double = Double()

class DoubleAddOp(theano.Op):
    """
    Add doubles.
    """

    __props__ = ()

    def make_node(self, x, y):
        """
        Define output variables.
        """

        # check input types
        if isinstance(x, (int, float)):
            x = theano.gof.Constant(double, x)
        if isinstance(y, (int, float)):
            y = theano.gof.Constant(double, y)

        if x.type != double or y.type != double:
            raise TypeError('DoubleAddOp only works on doubles.')

        return theano.gof.Apply(self, [x, y], [double()])

    def perform(self, node, inputs, output_storage):
        """
        Perform the addition.
        """

        x = inputs[0]
        y = inputs[1]
        z = output_storage[0]
        z[0] = x + y

    def infer_shape(self, node, input_shapes):
        """
        Used to infer the shape before performing the operation.
        """

        return [input_shapes[0]]

    def grad(self, inputs, output_grads):
        """
        Compute the gradients w.r.t. both inputs.
        """

        return [output_grads[0]*1, output_grads[0]*1]

    def __str__(self):
        """
        Identification.
        """

        return 'DoubleAddOp'

dadd = DoubleAddOp()
double_tests.py
"""
Tests for the double type and its operations.
"""

import theano
import random
import unittest
from double import double, dadd

class TestDoubleOps(unittest.TestCase):
    """
    Test operations on doubles.
    """

    # update to the latest Theano if the test cause unclosed files errors
    def test_DoubleAddOpPerform(self):
        x = double('x')
        y = double('y')
        z = dadd(x, y)
        f = theano.function([x, y], z)

        for i in range(100):
            x_value = random.random()
            y_value = random.random()
            self.assertAlmostEqual(f(x_value, y_value), x_value + y_value)

    def test_DoubleAddOpGrad(self):
        x = double('x')
        y = double('y')
        z = dadd(x, y)
        gx = theano.grad(z, x)
        gy = theano.grad(z, y)
        f = theano.function([x, y], [gx, gy])

        for i in range(100):
            x_value = random.random()
            y_value = random.random()

            gx_value, gy_value = f(x_value, y_value)
            self.assertAlmostEqual(gx_value, 1)
            self.assertAlmostEqual(gy_value, 1)

if __name__ == '__main__':
    unittest.main()

What is your opinion on the code snippet? Is it working? Let me know your thoughts in the comments below or using the following platforms: