Exemple #1
0
    def __init__(self, borrow=None, force_floatX=False, context=None):
        """
        Arguments
        ---------

        borrow : tuple of objects
            If an object in this tuple is encountered while tracing the
            function, then its symbolic representation will alias that object's
            memory location. This means that *inplace* operations on the Python
            (likely NumPy) object will affect the symbolic function.

        force_floatX : bool
            If True, floats and float NumPy ndarrays will be cast to the dtype
            specified at theano.config.floatX when forming symbolic shared
            variables, if they do not have it already. Objects in `borrowable`
            are never cast.

        """
        if context is None:
            self.context = Context(borrowable=utils.as_seq(borrow, tuple),
                                   force_floatX=force_floatX)
        elif isinstance(context, Context):
            self.context = context
        else:
            raise TypeError(
                'Received unrecognized Context: {0}'.format(context))
Exemple #2
0
def test_recalculate():
    x = np.zeros(3)
    c = Context()
    assert compute_stuff(x) == 12
    y = c.call(compute_stuff, (x,))
    assert y == 12

    f = recalculate_fn(c, y, x)
    assert f(x) == 12
    assert f(x + 1) != 12
Exemple #3
0
def test_recalculate():
    x = np.zeros(3)
    c = Context()
    assert compute_stuff(x) == 12
    y = c.call(compute_stuff, (x, ))
    assert y == 12

    f = recalculate_fn(c, y, x)
    assert f(x) == 12
    assert f(x + 1) != 12
Exemple #4
0
def test_loop():
    # test that non-data-dependent loops are unrolled properly

    x = np.zeros(3)
    c = Context()
    y = c.call(repeat_double, (x, 4))

    f = recalculate_fn(c, y, x)
    y2 = f(x + 1)
    assert np.all(y == 0)
    assert np.all(y2 == 16)
Exemple #5
0
def test_loop():
    # test that non-data-dependent loops are unrolled properly

    x = np.zeros(3)
    c = Context()
    y = c.call(repeat_double, (x, 4))

    f = recalculate_fn(c, y, x)
    y2 = f(x + 1)
    assert np.all(y == 0)
    assert np.all(y2 == 16)
Exemple #6
0
def test_grad():
    x = np.zeros(3)

    c = Context()
    y = c.call(compute_stuff, (x,))
    assert id(x) in c.svars
    assert id(y) in c.svars
    dy_dx_fn = grad_fn(c, y, x)

    assert np.all(dy_dx_fn(x + 0) == 8)
    assert np.all(dy_dx_fn(x + 1) == 16)
    assert np.all(dy_dx_fn(x + 2) == 24)
Exemple #7
0
def test_grad():
    x = np.zeros(3)

    c = Context()
    y = c.call(compute_stuff, (x, ))
    assert id(x) in c.svars
    assert id(y) in c.svars
    dy_dx_fn = grad_fn(c, y, x)

    assert np.all(dy_dx_fn(x + 0) == 8)
    assert np.all(dy_dx_fn(x + 1) == 16)
    assert np.all(dy_dx_fn(x + 2) == 24)
Exemple #8
0
    def __init__(self,
                 borrow=None,
                 force_floatX=False,
                 context=None):
        """
        Arguments
        ---------

        borrow : tuple of objects
            If an object in this tuple is encountered while tracing the
            function, then its symbolic representation will alias that object's
            memory location. This means that *inplace* operations on the Python
            (likely NumPy) object will affect the symbolic function.

        force_floatX : bool
            If True, floats and float NumPy ndarrays will be cast to the dtype
            specified at theano.config.floatX when forming symbolic shared
            variables, if they do not have it already. Objects in `borrowable`
            are never cast.

        """
        if context is None:
            self.context = Context(borrowable=utils.as_seq(borrow, tuple),
                                   force_floatX=force_floatX)
        elif isinstance(context, Context):
            self.context = context
        else:
            raise TypeError(
                'Received unrecognized Context: {0}'.format(context))
Exemple #9
0
    def __init__(self,
                 pyfn,
                 context=None,
                 force_floatX=False,
                 borrowable=None,
                 ignore=None,
                 infer_updates=False,
                 escape_on_error=False):
        """
        Arguments
        ---------

        borrow : tuple of objects
            If an object in this tuple is encountered while tracing the
            function, then its symbolic representation will alias that object's
            memory location. This means that *inplace* operations on the Python
            (likely NumPy) object will affect the symbolic function.

        """

        if context is None:
            context = Context(borrowable=utils.as_seq(borrowable, tuple),
                              ignore=utils.as_seq(ignore, tuple),
                              force_floatX=force_floatX,
                              infer_updates=infer_updates,
                              escape_on_error=escape_on_error)
        assert isinstance(context, Context)
        self.context = context

        if isinstance(pyfn, Symbolic):
            pyfn = pyfn.pyfn
        self._pyfn = pyfn

        self._symfn = self.context.recompile(self.pyfn)
Exemple #10
0
def test_low_integer_constants():
    one = 2 - 1
    # CPython re-uses ids of low integer constants
    # which kind of plays hell with the id-tracking done in the Context object
    assert one is 1
    # the current implementation crashes here because the addition creates a
    # shadow for the constant `1`, which then gets picked up by the axis
    # argument, and causes Theano to barf because axis can't be a symbolic
    # variable.
    r = Context().call(lambda x: (1 + x).sum(axis=1), (np.ones((2, 3)), ))
    assert np.allclose(r, [6, 6])
Exemple #11
0
class Symbolic(object):
    def __init__(self,
                 borrow=None,
                 force_floatX=False,
                 context=None):
        """
        Arguments
        ---------

        borrow : tuple of objects
            If an object in this tuple is encountered while tracing the
            function, then its symbolic representation will alias that object's
            memory location. This means that *inplace* operations on the Python
            (likely NumPy) object will affect the symbolic function.

        force_floatX : bool
            If True, floats and float NumPy ndarrays will be cast to the dtype
            specified at theano.config.floatX when forming symbolic shared
            variables, if they do not have it already. Objects in `borrowable`
            are never cast.

        """
        if context is None:
            self.context = Context(borrowable=utils.as_seq(borrow, tuple),
                                   force_floatX=force_floatX)
        elif isinstance(context, Context):
            self.context = context
        else:
            raise TypeError(
                'Received unrecognized Context: {0}'.format(context))

    @property
    def s_vars(self):
        return self.context.svars

    def trace(self, fn, *args, **kwargs):
        fn_copy = copy_function(fn)
        clean_args, clean_kwargs = clean_int_args(*args, **kwargs)
        return self.context.call(fn_copy, clean_args, clean_kwargs)

    def get_theano_variables(self, inputs=None, outputs=None):
        """
        Returns a dict containing inputs, outputs and graph corresponding to
        the Theano version of the pyfn.
        """
        inputs = utils.as_seq(inputs, tuple)
        sym_inputs = [self.get_symbolic(x) for x in inputs]

        outputs = utils.as_seq(outputs, tuple)
        sym_outputs = [self.get_symbolic(x) for x in outputs]

        # get symbolic inputs corresponding to shared inputs in s_inputs
        s_memo = OrderedDict((arg, arg.type())
                             for arg in utils.flat_from_doc(sym_inputs))
        theano_inputs = tuple(s_memo.values())

        # get new graph, replacing shared inputs with symbolic ones
        graph = theano.gof.graph.clone_get_equiv(
            theano.gof.graph.inputs(sym_outputs),
            sym_outputs,
            memo=s_memo.copy())

        # get symbolic outputs
        theano_outputs = tuple([graph[o] for o in sym_outputs])

        return theano_inputs, theano_outputs, graph

    def compile_function(self,
                         inputs=None,
                         outputs=None):

        fn_inputs, fn_outputs, graph = self.get_theano_variables(inputs,
                                                                 outputs)

        if len(fn_outputs) == 1:
            fn_outputs = fn_outputs[0]

        # compile function
        fn = theano.function(inputs=fn_inputs,
                             outputs=fn_outputs,
                             on_unused_input='ignore')

        return fn

    def compile_gradient(self,
                         inputs=None,
                         outputs=None,
                         wrt=None,
                         reduction=None):

        fn_inputs, fn_outputs, graph = self.get_theano_variables(inputs,
                                                                 outputs)
        wrt = utils.as_seq(wrt)

        if reduction in ['sum', 'max', 'mean', 'min', 'prod', 'std', 'var']:
            reduction = getattr(theano.tensor, reduction)
        if callable(reduction):
            if 'numpy' in reduction.__module__:
                reduction = getattr(theano.tensor, reduction.__name__)
            fn_outputs = [reduction(o) for o in fn_outputs]

        if np.any([o.ndim != 0 for o in fn_outputs]):
            raise TypeError('Gradient requires either scalar outputs or a '
                            'reduction that returns a scalar.')

        # get wrt variables. If none were specified, use inputs.
        if len(wrt) == 0:
            wrt = [i for i in fn_inputs]
        else:
            wrt = [graph[self.get_symbolic(w)] for w in wrt]

        grads = utils.flat_from_doc([tt.grad(o, wrt=wrt) for o in fn_outputs])

        if len(grads) == 1:
            grads = grads[0]

        # compile function
        fn = theano.function(inputs=fn_inputs,
                             outputs=grads,
                             on_unused_input='ignore')

        return fn

    def get_symbolic(self, x):
        """
        Retrieve the symbolic version of x.

        x : python object or string or Theano variable

        If x is an object, it must have been traced by the Symbolic class.
        If x is a string, it must have been tagged with
            autodiff.functions.tag() or have been placed in s_vars by
            other means.
        If x is a Theano variable, it must be in the s_vars dict.
        """
        if isinstance(x, basestring):
            if x in self.s_vars:
                return self.s_vars[x]
            else:
                raise ValueError(
                    'Requested the symbolic variable of tag `{0}`'
                    ', but `{0}` was not traced.'.format(x))

        elif isinstance(x, (tt.TensorConstant,
                            tt.TensorVariable,
                            tt.sharedvar.SharedVariable)):
            return x

        elif id(x) in self.s_vars:
            return self.s_vars[id(x)]
        else:
            raise ValueError(
                'Requested the symbolic variable shadowing object {0}'
                ', but it was not traced.'.format(repr(x)))
Exemple #12
0
class Symbolic(object):
    def __init__(self, borrow=None, force_floatX=False, context=None):
        """
        Arguments
        ---------

        borrow : tuple of objects
            If an object in this tuple is encountered while tracing the
            function, then its symbolic representation will alias that object's
            memory location. This means that *inplace* operations on the Python
            (likely NumPy) object will affect the symbolic function.

        force_floatX : bool
            If True, floats and float NumPy ndarrays will be cast to the dtype
            specified at theano.config.floatX when forming symbolic shared
            variables, if they do not have it already. Objects in `borrowable`
            are never cast.

        """
        if context is None:
            self.context = Context(borrowable=utils.as_seq(borrow, tuple),
                                   force_floatX=force_floatX)
        elif isinstance(context, Context):
            self.context = context
        else:
            raise TypeError(
                'Received unrecognized Context: {0}'.format(context))

    @property
    def s_vars(self):
        return self.context.svars

    def trace(self, fn, *args, **kwargs):
        fn_copy = copy_function(fn)
        clean_args, clean_kwargs = clean_int_args(*args, **kwargs)
        return self.context.call(fn_copy, clean_args, clean_kwargs)

    def get_theano_variables(self, inputs=None, outputs=None):
        """
        Returns a dict containing inputs, outputs and graph corresponding to
        the Theano version of the pyfn.
        """
        inputs = utils.as_seq(inputs, tuple)
        sym_inputs = [self.get_symbolic(x) for x in inputs]

        outputs = utils.as_seq(outputs, tuple)
        sym_outputs = [self.get_symbolic(x) for x in outputs]

        # get symbolic inputs corresponding to shared inputs in s_inputs
        s_memo = OrderedDict(
            (arg, arg.type()) for arg in utils.flat_from_doc(sym_inputs))
        theano_inputs = tuple(s_memo.values())

        # get new graph, replacing shared inputs with symbolic ones
        graph = theano.gof.graph.clone_get_equiv(
            theano.gof.graph.inputs(sym_outputs),
            sym_outputs,
            memo=s_memo.copy())

        # get symbolic outputs
        theano_outputs = tuple([graph[o] for o in sym_outputs])

        return theano_inputs, theano_outputs, graph

    def compile_function(self, inputs=None, outputs=None):

        fn_inputs, fn_outputs, graph = self.get_theano_variables(
            inputs, outputs)

        if len(fn_outputs) == 1:
            fn_outputs = fn_outputs[0]

        # compile function
        fn = theano.function(inputs=fn_inputs,
                             outputs=fn_outputs,
                             on_unused_input='ignore')

        return fn

    def compile_gradient(self,
                         inputs=None,
                         outputs=None,
                         wrt=None,
                         reduction=None):

        fn_inputs, fn_outputs, graph = self.get_theano_variables(
            inputs, outputs)
        wrt = utils.as_seq(wrt)

        if reduction in ['sum', 'max', 'mean', 'min', 'prod', 'std', 'var']:
            reduction = getattr(theano.tensor, reduction)
        if callable(reduction):
            if 'numpy' in reduction.__module__:
                reduction = getattr(theano.tensor, reduction.__name__)
            fn_outputs = [reduction(o) for o in fn_outputs]

        if np.any([o.ndim != 0 for o in fn_outputs]):
            raise TypeError('Gradient requires either scalar outputs or a '
                            'reduction that returns a scalar.')

        # get wrt variables. If none were specified, use inputs.
        if len(wrt) == 0:
            wrt = [i for i in fn_inputs]
        else:
            wrt = [graph[self.get_symbolic(w)] for w in wrt]

        grads = utils.flat_from_doc([tt.grad(o, wrt=wrt) for o in fn_outputs])

        if len(grads) == 1:
            grads = grads[0]

        # compile function
        fn = theano.function(inputs=fn_inputs,
                             outputs=grads,
                             on_unused_input='ignore')

        return fn

    def get_symbolic(self, x):
        """
        Retrieve the symbolic version of x.

        x : python object or string or Theano variable

        If x is an object, it must have been traced by the Symbolic class.
        If x is a string, it must have been tagged with
            autodiff.functions.tag() or have been placed in s_vars by
            other means.
        If x is a Theano variable, it must be in the s_vars dict.
        """
        if isinstance(x, basestring):
            if x in self.s_vars:
                return self.s_vars[x]
            else:
                raise ValueError('Requested the symbolic variable of tag `{0}`'
                                 ', but `{0}` was not traced.'.format(x))

        elif isinstance(x, (tt.TensorConstant, tt.TensorVariable,
                            tt.sharedvar.SharedVariable)):
            return x

        elif id(x) in self.s_vars:
            return self.s_vars[id(x)]
        else:
            raise ValueError(
                'Requested the symbolic variable shadowing object {0}'
                ', but it was not traced.'.format(repr(x)))