def broadcast_like(value, template, fgraph, dtype=None): """ Return a Variable with the same shape and dtype as the template, filled by broadcasting value through it. `value` will be cast as necessary. """ value = T.as_tensor_variable(value) if value.type == template.type: return value if template not in fgraph.variables: raise NotImplementedError('broadcast_like currently requires the ' 'template Variable to be in the fgraph already') if hasattr(fgraph, 'shape_feature'): new_shape = fgraph.shape_feature.shape_of[template] else: new_shape = template.shape if dtype is None: dtype = template.dtype rval = T.alloc(T.cast(value, dtype), *new_shape) # the template may have 1s in its shape without being broadcastable if rval.broadcastable != template.broadcastable: rval = T.unbroadcast(rval, *[i for i in xrange(rval.ndim) if rval.broadcastable[i] and not template.broadcastable[i]]) assert rval.type.dtype == dtype if rval.type.broadcastable != template.broadcastable: raise AssertionError("rval.type.broadcastable is " + str(rval.type.broadcastable) + " but template.broadcastable is" + str(template.broadcastable)) return rval
def local_0_dot_x(node): if not isinstance(node.op, T.Dot): return False x = node.inputs[0] y = node.inputs[1] replace = False try: if get_scalar_constant_value(x) == 0: replace = True except NotScalarConstantError: pass try: if get_scalar_constant_value(y) == 0: replace = True except NotScalarConstantError: pass if replace: constant_zero = T.constant(0, dtype=node.outputs[0].type.dtype) if x.ndim == 2 and y.ndim == 2: constant_zero = assert_(constant_zero, T.eq(x.shape[1], y.shape[0])) return [T.alloc(constant_zero, x.shape[0], y.shape[1])] elif x.ndim == 1 and y.ndim == 2: constant_zero = assert_(constant_zero, T.eq(x.shape[0], y.shape[0])) return [T.alloc(constant_zero, y.shape[1])] elif x.ndim == 2 and y.ndim == 1: constant_zero = assert_(constant_zero, T.eq(x.shape[1], y.shape[0])) return [T.alloc(constant_zero, x.shape[0])] elif x.ndim == 1 and y.ndim == 1: constant_zero = assert_(constant_zero, T.eq(x.shape[0], y.shape[0])) return [constant_zero] else: _logger.warning("Optimization Warning: " "Optimization theano/opt.py:local_0_dot_x Found " "that it could apply, but was not implemented " "for dot product with these input types:\n" "(%s, %s)", x.type, y.type)
def local_alloc_dimshuffle(node): """ If a dimshuffle is inside an alloc and only adds dimension to the left, remove it. Alloc(DimShuffle(x), ...) - > Alloc(x, ...) """ if isinstance(node.op, T.Alloc): input_ = node.inputs[0] if input_.owner and isinstance(input_.owner.op, DimShuffle): # check if it only adds dimension to the left new_order = input_.owner.op.new_order expected_new_order = ('x',) * (input_.ndim - input_.owner.inputs[0].ndim) + \ tuple(range(input_.owner.inputs[0].ndim)) if new_order != expected_new_order: return False return [T.alloc(input_.owner.inputs[0], *node.inputs[1:])] return False
def local_dimshuffle_alloc(node): """ If an alloc is inside a dimshuffle which only adds dimension to the left, scrap the dimshuffle and adds 1 into the alloc dimshuffle{x, 0, 1}(alloc([3 4], 3, 2) => alloc([3 4], 1, 3, 2) """ if isinstance(node.op, DimShuffle) and node.inputs[0].owner: input_ = node.inputs[0] if isinstance(input_.owner.op, T.Alloc): # check if it only adds dimension to the left new_order = node.op.new_order expected_new_order = ('x',) * (len(new_order) - input_.ndim) + \ tuple(range(input_.ndim)) if new_order != expected_new_order: return False # count numbers of 'x' nb_new_dims = len(new_order) - input_.ndim new_shape_input = (1,) * nb_new_dims + tuple(input_.owner.inputs[1:]) return [T.alloc(input_.owner.inputs[0], *new_shape_input)] return False
def local_dimshuffle_alloc(node): """ If an alloc is inside a dimshuffle which only adds dimension to the left, scrap the dimshuffle and adds 1 into the alloc dimshuffle{x, 0, 1}(alloc([3 4], 3, 2) => alloc([3 4], 1, 3, 2) """ if isinstance(node.op, DimShuffle) and node.inputs[0].owner: input_ = node.inputs[0] if isinstance(input_.owner.op, T.Alloc): # check if it only adds dimension to the left new_order = node.op.new_order expected_new_order = ('x',) * (len(new_order) - input_.ndim) + \ tuple(range(input_.ndim)) if new_order != expected_new_order: return False # count numbers of 'x' nb_new_dims = len(new_order) - input_.ndim new_shape_input = (1, ) * nb_new_dims + tuple( input_.owner.inputs[1:]) return [T.alloc(input_.owner.inputs[0], *new_shape_input)] return False
def repeat(x, repeats, axis=None): """Repeat elements of an array. It returns an array which has the same shape as `x`, except along the given axis. The axis is used to speficy along which axis to repeat values. By default, use the flattened input array, and return a flat output array. The number of repetitions for each element is `repeat`. `repeats` is broadcasted to fit the length of the given `axis`. Parameters ---------- x Input data, tensor variable. repeats int, scalar or tensor variable axis : int, optional See Also -------- tensor.tile .. versionadded:: 0.6 """ repeats = basic.as_tensor_variable(repeats) if repeats.ndim > 1: raise ValueError("The dimension of repeats should not exceed 1.") if repeats.ndim == 1 and not repeats.broadcastable[0]: return RepeatOp(axis=axis)(x, repeats) else: if repeats.ndim == 1: repeats = repeats[0] if x.dtype == "uint64": raise TypeError("theano.tensor.repeat don't support dtype uint64") if axis is None: axis = 0 x = x.flatten() else: if axis >= x.ndim: raise ValueError("Axis should not exceed x.ndim-1.") if axis < 0: axis = x.ndim + axis shape = [x.shape[i] for i in range(x.ndim)] # shape_ is the shape of the intermediate tensor which has # an additional dimension comparing to x. We use alloc to # allocate space for this intermediate tensor to replicate x # along that additional dimension. shape_ = shape[:] shape_.insert(axis + 1, repeats) # shape is now the shape of output, where shape[axis] becomes # shape[axis]*repeats. shape[axis] = shape[axis] * repeats # dims_ is the dimension of that intermediate tensor. dims_ = list(np.arange(x.ndim)) dims_.insert(axis + 1, "x") # After the original tensor is duplicated along the additional # dimension, we reshape it to the expected output shape, and # return the output z. z = basic.alloc(x.dimshuffle(*dims_), *shape_).reshape(shape) return z
f = theano.function([g], host_from_gpu(g)) fv = f(gv) assert np.all(fv == av) def gpu_alloc_expected(x, *shp): g = gpuarray.empty(shp, dtype=x.dtype, context=get_context(test_ctx_name)) g[:] = x return g GpuAllocTester = makeTester( name="GpuAllocTester", # The +1 is there to allow the lift to the GPU. op=lambda *args: alloc(*args) + 1, gpu_op=GpuAlloc(test_ctx_name), cases=dict( correct01=(rand(), np.int32(7)), # just gives a DeepCopyOp with possibly wrong results on the CPU # correct01_bcast=(rand(1), np.int32(7)), correct02=(rand(), np.int32(4), np.int32(7)), correct12=(rand(7), np.int32(4), np.int32(7)), correct13=(rand(7), np.int32(2), np.int32(4), np.int32(7)), correct23=(rand(4, 7), np.int32(2), np.int32(4), np.int32(7)), bad_shape12=(rand(7), np.int32(7), np.int32(5)), ) )
f = theano.function([g], host_from_gpu(g)) fv = f(gv) assert np.all(fv == av) def gpu_alloc_expected(x, *shp): g = gpuarray.empty(shp, dtype=x.dtype, context=get_context(test_ctx_name)) g[:] = x return g TestGpuAlloc = makeTester( name="GpuAllocTester", # The +1 is there to allow the lift to the GPU. op=lambda *args: alloc(*args) + 1, gpu_op=GpuAlloc(test_ctx_name), cases=dict( correct01=(rand(), np.int32(7)), # just gives a DeepCopyOp with possibly wrong results on the CPU # correct01_bcast=(rand(1), np.int32(7)), correct02=(rand(), np.int32(4), np.int32(7)), correct12=(rand(7), np.int32(4), np.int32(7)), correct13=(rand(7), np.int32(2), np.int32(4), np.int32(7)), correct23=(rand(4, 7), np.int32(2), np.int32(4), np.int32(7)), bad_shape12=(rand(7), np.int32(7), np.int32(5)), ), ) class TestGPUAlloc(TestAlloc):