提交 91c7197c authored 作者: Pascal Lamblin's avatar Pascal Lamblin

PEP8/flake8 fixes in tensor/basic.py

Remaining errors are lines too long in comments or C code.
上级 55693fb0
...@@ -7,7 +7,6 @@ import warnings ...@@ -7,7 +7,6 @@ import warnings
from itertools import izip from itertools import izip
import numpy import numpy
from copy import copy as python_copy
import theano import theano
from theano.configparser import config from theano.configparser import config
...@@ -26,7 +25,7 @@ from theano.gof.utils import hashtype ...@@ -26,7 +25,7 @@ from theano.gof.utils import hashtype
from theano import compile, printing from theano import compile, printing
from theano.printing import pprint, min_informative_str from theano.printing import pprint, min_informative_str
#For history #For history
from theano.compile import Rebroadcast, Shape, shape, SpecifyShape, specify_shape from theano.compile import Rebroadcast, Shape, shape
# We use these exceptions as well. # We use these exceptions as well.
...@@ -138,7 +137,8 @@ def as_tensor_variable(x, name=None, ndim=None): ...@@ -138,7 +137,8 @@ def as_tensor_variable(x, name=None, ndim=None):
:Exceptions: :Exceptions:
- `ValueError`: raised if an `Apply` with more then one output is fetched - `ValueError`: raised if an `Apply` with more then one output is fetched
- `AsTensorError`: raised if `x` cannot be converted to a TensorType Variable - `AsTensorError`: raised if `x` cannot be converted to a TensorType
Variable
""" """
if hasattr(x, '_as_TensorVariable'): if hasattr(x, '_as_TensorVariable'):
...@@ -166,11 +166,12 @@ def as_tensor_variable(x, name=None, ndim=None): ...@@ -166,11 +166,12 @@ def as_tensor_variable(x, name=None, ndim=None):
if (x.type.ndim > ndim): if (x.type.ndim > ndim):
# strip off leading broadcastable dimensions # strip off leading broadcastable dimensions
first_non_broadcastable = [idx for idx in range(x.ndim) first_non_broadcastable = [idx for idx in range(x.ndim)
if x.broadcastable[idx] == False][0] if not x.broadcastable[idx]][0]
x = x.dimshuffle(range(x.ndim)[first_non_broadcastable:]) x = x.dimshuffle(range(x.ndim)[first_non_broadcastable:])
if x.ndim > ndim: if x.ndim > ndim:
raise ValueError( raise ValueError(
'TensorType could not be cast to have %i dimensions' % ndim, x.type 'TensorType could not be cast to have %i dimensions'
% ndim, x.type
) )
return x return x
elif (x.type.ndim < ndim): elif (x.type.ndim < ndim):
...@@ -251,9 +252,9 @@ class NumpyAutocaster(object): ...@@ -251,9 +252,9 @@ class NumpyAutocaster(object):
return numpy.asarray(x) return numpy.asarray(x)
elif config.cast_policy == 'numpy+floatX': elif config.cast_policy == 'numpy+floatX':
rval = numpy.asarray(x) rval = numpy.asarray(x)
if (rval.dtype == 'float64' and # numpy wants float64 if ((rval.dtype == 'float64' and # numpy wants float64
config.floatX == 'float32' and # but we prefer float32 config.floatX == 'float32' and # but we prefer float32
not hasattr(x, 'dtype')): # and `x` was not typed not hasattr(x, 'dtype'))): # and `x` was not typed
rval = theano._asarray(rval, dtype='float32') rval = theano._asarray(rval, dtype='float32')
return rval return rval
...@@ -275,9 +276,9 @@ class NumpyAutocaster(object): ...@@ -275,9 +276,9 @@ class NumpyAutocaster(object):
# unsafe downcast of float64 variables when config.floatX == 'float32' # unsafe downcast of float64 variables when config.floatX == 'float32'
# recall: float is numpy.float # recall: float is numpy.float
if (isinstance(x, float) and if ((isinstance(x, float) and
config.floatX in self.dtypes and config.floatX in self.dtypes and
config.floatX == 'float32'): config.floatX == 'float32')):
return theano._asarray(x, dtype='float32') return theano._asarray(x, dtype='float32')
...@@ -313,7 +314,7 @@ class autocast_float_as(object): ...@@ -313,7 +314,7 @@ class autocast_float_as(object):
For example: For example:
>>> with autocast_float_as('float32') as _dummy: >>> with autocast_float_as('float32') as _dummy:
>>> assert (fvector() + 1.1).dtype == 'float32' # temporary downcasting ... assert (fvector() + 1.1).dtype == 'float32' # temporary downcasting
>>> assert (fvector() + 1.1).dtype == 'float64' # back to default behaviour >>> assert (fvector() + 1.1).dtype == 'float64' # back to default behaviour
This class might be convenient in some code, but it definitely This class might be convenient in some code, but it definitely
...@@ -395,9 +396,9 @@ def constant_or_value(x, rtype, name=None, ndim=None, dtype=None): ...@@ -395,9 +396,9 @@ def constant_or_value(x, rtype, name=None, ndim=None, dtype=None):
try: try:
if rtype is TensorConstant: if rtype is TensorConstant:
rval = rtype( rval = rtype(
TensorType(dtype=x_.dtype, broadcastable=bcastable), TensorType(dtype=x_.dtype, broadcastable=bcastable),
x_.copy(), x_.copy(),
name=name) name=name)
return rval return rval
else: else:
# leave the shape out of the type # leave the shape out of the type
...@@ -407,17 +408,17 @@ def constant_or_value(x, rtype, name=None, ndim=None, dtype=None): ...@@ -407,17 +408,17 @@ def constant_or_value(x, rtype, name=None, ndim=None, dtype=None):
raise TypeError("Could not convert %s to TensorType" % x, type(x)) raise TypeError("Could not convert %s to TensorType" % x, type(x))
constant_cache = {}
def constant(x, name=None, ndim=None, dtype=None): def constant(x, name=None, ndim=None, dtype=None):
ret = constant_or_value(x, rtype=TensorConstant, name=name, ndim=ndim, ret = constant_or_value(x, rtype=TensorConstant, name=name, ndim=ndim,
dtype=dtype) dtype=dtype)
#We create a small cache of frequently used constant. # We create a small cache of frequently used constant.
#This speed up the Merge optimization for big graph. # This speed up the Merge optimization for big graph.
#We want to cache all scalar to don't merge as frequently constants. # We want to cache all scalar to don't merge as frequently constants.
#But we don't want to cache too much stuff # But we don't want to cache too much stuff
#So we cache integer with dtype [u]int and float where the value is between -10 and 10 # So we cache integer with dtype [u]int and float where the value is
#We want to cache all broadcast pattern for scalar. # between -10 and 10
# We want to cache all broadcast pattern for scalar.
if not constant.enable: if not constant.enable:
return ret return ret
sig = ret.signature() sig = ret.signature()
...@@ -431,6 +432,7 @@ def constant(x, name=None, ndim=None, dtype=None): ...@@ -431,6 +432,7 @@ def constant(x, name=None, ndim=None, dtype=None):
return constant_cache.get(sig, ret) return constant_cache.get(sig, ret)
constant.enable = True constant.enable = True
constant_cache = {}
def _obj_is_wrappable_as_tensor(x): def _obj_is_wrappable_as_tensor(x):
...@@ -520,8 +522,9 @@ def numpy_scalar(data): ...@@ -520,8 +522,9 @@ def numpy_scalar(data):
""" """
# handle case where data is numpy.array([]) # handle case where data is numpy.array([])
if data.ndim > 0 and (len(data.shape) == 0 or if (data.ndim > 0 and
__builtins__['max'](data.shape) == 0): (len(data.shape) == 0 or
__builtins__['max'](data.shape) == 0)):
assert numpy.all(numpy.array([]) == data) assert numpy.all(numpy.array([]) == data)
raise EmptyConstantError() raise EmptyConstantError()
try: try:
...@@ -539,6 +542,8 @@ get_scalar_constant_value_elemwises = ( ...@@ -539,6 +542,8 @@ get_scalar_constant_value_elemwises = (
scal.LT, scal.GT, scal.LE, scal.GE, scal.LT, scal.GT, scal.LE, scal.GE,
scal.Sub, scal.Add, scal.Mod, scal.Mul, scal.Sub, scal.Add, scal.Mod, scal.Mul,
scal.IntDiv, scal.TrueDiv, scal.Minimum, scal.Maximum) scal.IntDiv, scal.TrueDiv, scal.Minimum, scal.Maximum)
def get_scalar_constant_value(orig_v, elemwise=True): def get_scalar_constant_value(orig_v, elemwise=True):
"""return the constant scalar(0-D) value underlying variable `v` """return the constant scalar(0-D) value underlying variable `v`
...@@ -557,8 +562,8 @@ def get_scalar_constant_value(orig_v, elemwise=True): ...@@ -557,8 +562,8 @@ def get_scalar_constant_value(orig_v, elemwise=True):
v = orig_v v = orig_v
while True: while True:
if v is None: if v is None:
# None is not a scalar (and many uses of this function seem to depend # None is not a scalar (and many uses of this function seem
# on passing it None) # to depend on passing it None)
raise NotScalarConstantError() raise NotScalarConstantError()
if isinstance(v, (numpy.integer, int, float)): if isinstance(v, (numpy.integer, int, float)):
...@@ -591,7 +596,7 @@ def get_scalar_constant_value(orig_v, elemwise=True): ...@@ -591,7 +596,7 @@ def get_scalar_constant_value(orig_v, elemwise=True):
elif isinstance(v.owner.op, scal.ScalarOp): elif isinstance(v.owner.op, scal.ScalarOp):
if isinstance(v.owner.op, scal.Second): if isinstance(v.owner.op, scal.Second):
# We don't need both input to be constant for second # We don't need both input to be constant for second
shape, val = v.owner.inputs shp, val = v.owner.inputs
v = val v = val
continue continue
if isinstance(v.owner.op, get_scalar_constant_value_elemwises): if isinstance(v.owner.op, get_scalar_constant_value_elemwises):
...@@ -603,46 +608,49 @@ def get_scalar_constant_value(orig_v, elemwise=True): ...@@ -603,46 +608,49 @@ def get_scalar_constant_value(orig_v, elemwise=True):
elif elemwise and isinstance(v.owner.op, Elemwise): elif elemwise and isinstance(v.owner.op, Elemwise):
if isinstance(v.owner.op.scalar_op, scal.Second): if isinstance(v.owner.op.scalar_op, scal.Second):
# We don't need both input to be constant for second # We don't need both input to be constant for second
shape, val = v.owner.inputs shp, val = v.owner.inputs
v = val v = val
continue continue
elif isinstance(v.owner.op.scalar_op, elif isinstance(v.owner.op.scalar_op,
get_scalar_constant_value_elemwises): get_scalar_constant_value_elemwises):
const = [get_scalar_constant_value(i) for i in v.owner.inputs] const = [get_scalar_constant_value(i)
for i in v.owner.inputs]
ret = [[None]] ret = [[None]]
v.owner.op.perform(v.owner, const, ret) v.owner.op.perform(v.owner, const, ret)
return ret[0][0] return ret[0][0]
elif isinstance(v.owner.op, theano.tensor.subtensor.Subtensor) and v.ndim == 0: elif (isinstance(v.owner.op, theano.tensor.subtensor.Subtensor)
and v.ndim == 0):
if isinstance(v.owner.inputs[0], TensorConstant): if isinstance(v.owner.inputs[0], TensorConstant):
cdata = tuple(v.owner.op.get_constant_idx(v.owner.inputs)) cdata = tuple(v.owner.op.get_constant_idx(v.owner.inputs))
try: try:
return v.owner.inputs[0].data.__getitem__(cdata) return v.owner.inputs[0].data.__getitem__(cdata)
except IndexError: except IndexError:
raise IndexError( raise IndexError(
str(tuple(v.owner.op.idx_list)) + str(tuple(v.owner.op.idx_list)) +
" is not a valid index into " + " is not a valid index into " +
str(v.owner.inputs[0].data)) str(v.owner.inputs[0].data))
# The index list 'idx_list' should have length the same # The index list 'idx_list' should have length the same
# shape as the input. # shape as the input.
# TODO: implement the case where we take a scalar in a matrix # TODO: implement the case where we take a scalar in a matrix
assert len(v.owner.op.idx_list) == v.owner.inputs[0].ndim assert len(v.owner.op.idx_list) == v.owner.inputs[0].ndim
# Needed to make better graph in this test in theano/tensor/tests: # Needed to make better graph in this test in
# test_sharedvar.py:test_shared_options.test_specify_shape_partial # theano/tensor/tests/test_sharedvar.py:
if (v.owner.inputs[0].owner and # test_shared_options.test_specify_shape_partial
isinstance(v.owner.inputs[0].owner.op, Join) and if ((v.owner.inputs[0].owner and
len(v.owner.op.idx_list) == 1): isinstance(v.owner.inputs[0].owner.op, Join) and
len(v.owner.op.idx_list) == 1)):
# Ensure the Join is joining only scalar variables (so that # Ensure the Join is joining only scalar variables (so that
# the constant value can be found at the same index as the one # the constant value can be found at the same index as the
# used in the sub-tensor). # one used in the sub-tensor).
if python_all(var.ndim == 0 for var in if python_all(var.ndim == 0 for var in
v.owner.inputs[0].owner.inputs[1:]): v.owner.inputs[0].owner.inputs[1:]):
idx = v.owner.op.idx_list[0] idx = v.owner.op.idx_list[0]
if isinstance(idx, gof.Type): if isinstance(idx, gof.Type):
idx = get_scalar_constant_value(v.owner.inputs[1]) idx = get_scalar_constant_value(v.owner.inputs[1])
# Note the '+ 1' is because the first argument to Join is the # Note the '+ 1' is because the first argument to Join
# axis. # is the axis.
ret = v.owner.inputs[0].owner.inputs[idx + 1] ret = v.owner.inputs[0].owner.inputs[idx + 1]
ret = get_scalar_constant_value(ret) ret = get_scalar_constant_value(ret)
# join can cast implicitly its input in some case. # join can cast implicitly its input in some case.
...@@ -658,7 +666,8 @@ def get_scalar_constant_value(orig_v, elemwise=True): ...@@ -658,7 +666,8 @@ def get_scalar_constant_value(orig_v, elemwise=True):
for joined in v.owner.inputs[0].owner.inputs[1:]: for joined in v.owner.inputs[0].owner.inputs[1:]:
ll = get_vector_length(joined) ll = get_vector_length(joined)
if idx < length + ll: if idx < length + ll:
return get_scalar_constant_value(joined[idx-length]) return get_scalar_constant_value(
joined[idx - length])
length += ll length += ll
except TypeError: except TypeError:
pass pass
...@@ -666,13 +675,14 @@ def get_scalar_constant_value(orig_v, elemwise=True): ...@@ -666,13 +675,14 @@ def get_scalar_constant_value(orig_v, elemwise=True):
pass pass
elif (v.owner.inputs[0].owner and elif (v.owner.inputs[0].owner and
isinstance(v.owner.inputs[0].owner.op, isinstance(v.owner.inputs[0].owner.op,
theano.tensor.opt.MakeVector) and theano.tensor.opt.MakeVector) and
# MakeVector normally accept only scalar as input. # MakeVector normally accept only scalar as input.
# We put this check in case there is change in the future # We put this check in case there is change in the future
python_all(var.ndim == 0 for var in python_all(var.ndim == 0 for var in
v.owner.inputs[0].owner.inputs) and v.owner.inputs[0].owner.inputs) and
len(v.owner.op.idx_list) == 1): len(v.owner.op.idx_list) == 1):
idx = v.owner.op.idx_list[0] idx = v.owner.op.idx_list[0]
if isinstance(idx, gof.Type): if isinstance(idx, gof.Type):
idx = get_scalar_constant_value(v.owner.inputs[1]) idx = get_scalar_constant_value(v.owner.inputs[1])
...@@ -773,7 +783,7 @@ def scalar(name=None, dtype=None): ...@@ -773,7 +783,7 @@ def scalar(name=None, dtype=None):
return type(name) return type(name)
scalars, fscalars, dscalars, iscalars, lscalars = _multi( scalars, fscalars, dscalars, iscalars, lscalars = _multi(
scalar, fscalar, dscalar, iscalar, lscalar) scalar, fscalar, dscalar, iscalar, lscalar)
int_types = bscalar, wscalar, iscalar, lscalar int_types = bscalar, wscalar, iscalar, lscalar
float_types = fscalar, dscalar float_types = fscalar, dscalar
...@@ -803,7 +813,7 @@ def vector(name=None, dtype=None): ...@@ -803,7 +813,7 @@ def vector(name=None, dtype=None):
return type(name) return type(name)
vectors, fvectors, dvectors, ivectors, lvectors = _multi( vectors, fvectors, dvectors, ivectors, lvectors = _multi(
vector, fvector, dvector, ivector, lvector) vector, fvector, dvector, ivector, lvector)
int_vector_types = bvector, wvector, ivector, lvector int_vector_types = bvector, wvector, ivector, lvector
float_vector_types = fvector, dvector float_vector_types = fvector, dvector
...@@ -830,7 +840,7 @@ def matrix(name=None, dtype=None): ...@@ -830,7 +840,7 @@ def matrix(name=None, dtype=None):
return type(name) return type(name)
matrices, fmatrices, dmatrices, imatrices, lmatrices = _multi( matrices, fmatrices, dmatrices, imatrices, lmatrices = _multi(
matrix, fmatrix, dmatrix, imatrix, lmatrix) matrix, fmatrix, dmatrix, imatrix, lmatrix)
int_matrix_types = bmatrix, wmatrix, imatrix, lmatrix int_matrix_types = bmatrix, wmatrix, imatrix, lmatrix
float_matrix_types = fmatrix, dmatrix float_matrix_types = fmatrix, dmatrix
...@@ -899,7 +909,7 @@ def tensor3(name=None, dtype=None): ...@@ -899,7 +909,7 @@ def tensor3(name=None, dtype=None):
return type(name) return type(name)
tensor3s, ftensor3s, dtensor3s, itensor3s, ltensor3s = _multi( tensor3s, ftensor3s, dtensor3s, itensor3s, ltensor3s = _multi(
tensor3, ftensor3, dtensor3, itensor3, ltensor3) tensor3, ftensor3, dtensor3, itensor3, ltensor3)
ctensor4 = TensorType('complex64', ((False,) * 4)) ctensor4 = TensorType('complex64', ((False,) * 4))
ztensor4 = TensorType('complex128', ((False,) * 4)) ztensor4 = TensorType('complex128', ((False,) * 4))
...@@ -921,7 +931,7 @@ def tensor4(name=None, dtype=None): ...@@ -921,7 +931,7 @@ def tensor4(name=None, dtype=None):
type = TensorType(dtype, (False, False, False, False)) type = TensorType(dtype, (False, False, False, False))
return type(name) return type(name)
tensor4s, ftensor4s, dtensor4s, itensor4s, ltensor4s = _multi( tensor4s, ftensor4s, dtensor4s, itensor4s, ltensor4s = _multi(
tensor4, ftensor4, dtensor4, itensor4, ltensor4) tensor4, ftensor4, dtensor4, itensor4, ltensor4)
Tensor = TensorType Tensor = TensorType
...@@ -1039,7 +1049,8 @@ class ScalarFromTensor(Op): ...@@ -1039,7 +1049,8 @@ class ScalarFromTensor(Op):
assert t.type.broadcastable == () assert t.type.broadcastable == ()
return Apply(self, return Apply(self,
[t], [t],
[scal.get_scalar_type(dtype=t.type.dtype).make_variable()]) [scal.get_scalar_type(dtype=t.type.dtype).make_variable()]
)
def perform(self, node, inp, out_): def perform(self, node, inp, out_):
s, = inp s, = inp
...@@ -1091,66 +1102,66 @@ def _conversion(real_value, name): ...@@ -1091,66 +1102,66 @@ def _conversion(real_value, name):
# `cast()` function below. # `cast()` function below.
_convert_to_int8 = _conversion( _convert_to_int8 = _conversion(
elemwise.Elemwise(scal.convert_to_int8), 'int8') elemwise.Elemwise(scal.convert_to_int8), 'int8')
"""Cast to 8-bit integer""" """Cast to 8-bit integer"""
_convert_to_int16 = _conversion( _convert_to_int16 = _conversion(
elemwise.Elemwise(scal.convert_to_int16), 'int16') elemwise.Elemwise(scal.convert_to_int16), 'int16')
"""Cast to 16-bit integer""" """Cast to 16-bit integer"""
_convert_to_int32 = _conversion( _convert_to_int32 = _conversion(
elemwise.Elemwise(scal.convert_to_int32), 'int32') elemwise.Elemwise(scal.convert_to_int32), 'int32')
"""Cast to 32-bit integer""" """Cast to 32-bit integer"""
_convert_to_int64 = _conversion( _convert_to_int64 = _conversion(
elemwise.Elemwise(scal.convert_to_int64), 'int64') elemwise.Elemwise(scal.convert_to_int64), 'int64')
"""Cast to 64-bit integer""" """Cast to 64-bit integer"""
_convert_to_uint8 = _conversion( _convert_to_uint8 = _conversion(
elemwise.Elemwise(scal.convert_to_uint8), 'uint8') elemwise.Elemwise(scal.convert_to_uint8), 'uint8')
"""Cast to unsigned 8-bit integer""" """Cast to unsigned 8-bit integer"""
_convert_to_uint16 = _conversion( _convert_to_uint16 = _conversion(
elemwise.Elemwise(scal.convert_to_uint16), 'uint16') elemwise.Elemwise(scal.convert_to_uint16), 'uint16')
"""Cast to unsigned 16-bit integer""" """Cast to unsigned 16-bit integer"""
_convert_to_uint32 = _conversion( _convert_to_uint32 = _conversion(
elemwise.Elemwise(scal.convert_to_uint32), 'uint32') elemwise.Elemwise(scal.convert_to_uint32), 'uint32')
"""Cast to unsigned 32-bit integer""" """Cast to unsigned 32-bit integer"""
_convert_to_uint64 = _conversion( _convert_to_uint64 = _conversion(
elemwise.Elemwise(scal.convert_to_uint64), 'uint64') elemwise.Elemwise(scal.convert_to_uint64), 'uint64')
"""Cast to unsigned 64-bit integer""" """Cast to unsigned 64-bit integer"""
_convert_to_float32 = _conversion( _convert_to_float32 = _conversion(
elemwise.Elemwise(scal.convert_to_float32), 'float32') elemwise.Elemwise(scal.convert_to_float32), 'float32')
"""Cast to single-precision floating point""" """Cast to single-precision floating point"""
_convert_to_float64 = _conversion( _convert_to_float64 = _conversion(
elemwise.Elemwise(scal.convert_to_float64), 'float64') elemwise.Elemwise(scal.convert_to_float64), 'float64')
"""Cast to double-precision floating point""" """Cast to double-precision floating point"""
_convert_to_complex64 = _conversion( _convert_to_complex64 = _conversion(
elemwise.Elemwise(scal.convert_to_complex64), 'complex64') elemwise.Elemwise(scal.convert_to_complex64), 'complex64')
"""Cast to single-precision complex""" """Cast to single-precision complex"""
_convert_to_complex128 = _conversion( _convert_to_complex128 = _conversion(
elemwise.Elemwise(scal.convert_to_complex128), 'complex128') elemwise.Elemwise(scal.convert_to_complex128), 'complex128')
"""Cast to double-precision complex""" """Cast to double-precision complex"""
_cast_mapping = { _cast_mapping = {
'int8': _convert_to_int8, 'int8': _convert_to_int8,
'int16': _convert_to_int16, 'int16': _convert_to_int16,
'int32': _convert_to_int32, 'int32': _convert_to_int32,
'int64': _convert_to_int64, 'int64': _convert_to_int64,
'uint8': _convert_to_uint8, 'uint8': _convert_to_uint8,
'uint16': _convert_to_uint16, 'uint16': _convert_to_uint16,
'uint32': _convert_to_uint32, 'uint32': _convert_to_uint32,
'uint64': _convert_to_uint64, 'uint64': _convert_to_uint64,
'float32': _convert_to_float32, 'float32': _convert_to_float32,
'float64': _convert_to_float64, 'float64': _convert_to_float64,
'complex64': _convert_to_complex64, 'complex64': _convert_to_complex64,
'complex128': _convert_to_complex128} 'complex128': _convert_to_complex128}
@constructor @constructor
...@@ -1235,7 +1246,8 @@ class MaxAndArgmax(Op): ...@@ -1235,7 +1246,8 @@ class MaxAndArgmax(Op):
elif isinstance(axis, Variable): elif isinstance(axis, Variable):
if not isinstance(axis, TensorConstant): if not isinstance(axis, TensorConstant):
raise TypeError("MaxAndArgmax needs a constant axis") raise TypeError("MaxAndArgmax needs a constant axis")
assert axis.dtype.startswith("int") or axis.dtype.startswith("uint") assert (axis.dtype.startswith("int")
or axis.dtype.startswith("uint"))
axis = int(axis.data) axis = int(axis.data)
# we make the axis all positive to make the infer_shape work # we make the axis all positive to make the infer_shape work
# with negative axis # with negative axis
...@@ -1249,8 +1261,8 @@ class MaxAndArgmax(Op): ...@@ -1249,8 +1261,8 @@ class MaxAndArgmax(Op):
if axis is not None: if axis is not None:
if axis < 0 or axis >= x.type.ndim: if axis < 0 or axis >= x.type.ndim:
raise ValueError( raise ValueError(
'Invalid axis: %s (the number of dimensions of the ' 'Invalid axis: %s (the number of dimensions of the '
'input is: %s)' % (axis, x.type.ndim)) 'input is: %s)' % (axis, x.type.ndim))
all_axes.add(axis) all_axes.add(axis)
else: else:
all_axes = range(x.ndim) all_axes = range(x.ndim)
...@@ -1342,7 +1354,7 @@ class MaxAndArgmax(Op): ...@@ -1342,7 +1354,7 @@ class MaxAndArgmax(Op):
if node.inputs[1].data is None: if node.inputs[1].data is None:
return [(), ()] return [(), ()]
rval = tuple([ishape[i] for (i, b) in enumerate( rval = tuple([ishape[i] for (i, b) in enumerate(
node.inputs[0].type.broadcastable) if i != axis.data]) node.inputs[0].type.broadcastable) if i != axis.data])
return [rval, rval] return [rval, rval]
def R_op(self, inputs, eval_points): def R_op(self, inputs, eval_points):
...@@ -1389,9 +1401,10 @@ class MaxAndArgmax(Op): ...@@ -1389,9 +1401,10 @@ class MaxAndArgmax(Op):
if g_max_disconnected and g_max_idx_disconnected: if g_max_disconnected and g_max_idx_disconnected:
return [DisconnectedType()(), DisconnectedType()()] return [DisconnectedType()(), DisconnectedType()()]
axis_grad = grad_undefined(self, 1, axis, axis_grad = grad_undefined(
"argmax is not defined for non-integer axes so" self, 1, axis,
" argmax(x, axis+eps) is undefined") "argmax is not defined for non-integer axes so"
" argmax(x, axis+eps) is undefined")
# if the max is disconnected but the argmax is not, # if the max is disconnected but the argmax is not,
# the gradient on its inputs is zero # the gradient on its inputs is zero
...@@ -1449,7 +1462,8 @@ def makeKeepDims(x, y, axis): ...@@ -1449,7 +1462,8 @@ def makeKeepDims(x, y, axis):
newaxis = [] newaxis = []
for a in axis: for a in axis:
if not isinstance(a, int): if not isinstance(a, int):
raise ValueError("keepdims option can be used only with constant axis") raise ValueError(
"keepdims option can be used only with constant axis")
if a < 0: if a < 0:
a += x.type.ndim a += x.type.ndim
newaxis.append(a) newaxis.append(a)
...@@ -1931,6 +1945,7 @@ def gammaln(a): ...@@ -1931,6 +1945,7 @@ def gammaln(a):
def psi(a): def psi(a):
"""derivative of log gamma function""" """derivative of log gamma function"""
@_scal_elemwise @_scal_elemwise
def chi2sf(x, k): def chi2sf(x, k):
"""chi squared survival function""" """chi squared survival function"""
...@@ -2001,26 +2016,26 @@ def zeros_like(model, dtype=None): ...@@ -2001,26 +2016,26 @@ def zeros_like(model, dtype=None):
return fill(model, constant(0.0, dtype=dtype)) return fill(model, constant(0.0, dtype=dtype))
def zeros(shape, dtype=None): def zeros(shp, dtype=None):
""" """
Create a Tensor filled with zeros, closer to Numpy's syntax than ``alloc``. Create a Tensor filled with zeros, closer to Numpy's syntax than ``alloc``.
""" """
if not isinstance(shape, (list, tuple, TensorVariable)): if not isinstance(shp, (list, tuple, TensorVariable)):
shape = [shape] shp = [shp]
if dtype is None: if dtype is None:
dtype = config.floatX dtype = config.floatX
return alloc(numpy.array(0, dtype=dtype), *shape) return alloc(numpy.array(0, dtype=dtype), *shp)
def ones(shape, dtype=None): def ones(shp, dtype=None):
""" """
Create a Tensor filled with ones, closer to Numpy's syntax than ``alloc``. Create a Tensor filled with ones, closer to Numpy's syntax than ``alloc``.
""" """
if not isinstance(shape, (list, tuple, TensorVariable)): if not isinstance(shp, (list, tuple, TensorVariable)):
shape = [shape] shp = [shp]
if dtype is None: if dtype is None:
dtype = config.floatX dtype = config.floatX
return alloc(numpy.array(1, dtype=dtype), *shape) return alloc(numpy.array(1, dtype=dtype), *shp)
class Nonzero(gof.Op): class Nonzero(gof.Op):
...@@ -2198,8 +2213,10 @@ class Tri(gof.Op): ...@@ -2198,8 +2213,10 @@ class Tri(gof.Op):
N = as_tensor_variable(N) N = as_tensor_variable(N)
M = as_tensor_variable(M) M = as_tensor_variable(M)
k = as_tensor_variable(k) k = as_tensor_variable(k)
return gof.Apply(self, [N, M, k], return gof.Apply(
[TensorType(dtype=self.dtype, broadcastable=(False, False))()]) self,
[N, M, k],
[TensorType(dtype=self.dtype, broadcastable=(False, False))()])
def perform(self, node, inp, out_): def perform(self, node, inp, out_):
N, M, k = inp N, M, k = inp
...@@ -2307,8 +2324,10 @@ class Eye(gof.Op): ...@@ -2307,8 +2324,10 @@ class Eye(gof.Op):
assert n.ndim == 0 assert n.ndim == 0
assert m.ndim == 0 assert m.ndim == 0
assert k.ndim == 0 assert k.ndim == 0
return gof.Apply(self, [n, m, k], return gof.Apply(
[TensorType(dtype=self.dtype, broadcastable=(False, False))()]) self,
[n, m, k],
[TensorType(dtype=self.dtype, broadcastable=(False, False))()])
def perform(self, node, inp, out_): def perform(self, node, inp, out_):
n, m, k = inp n, m, k = inp
...@@ -2370,8 +2389,9 @@ class Alloc(gof.Op): ...@@ -2370,8 +2389,9 @@ class Alloc(gof.Op):
Returns an N-dimensional tensor initialized by `value` using something Returns an N-dimensional tensor initialized by `value` using something
equivalent to equivalent to
>>> z = numpy.zeros(shape, value.dtype)
>>> z += value z = numpy.zeros(shape, value.dtype)
z += value
The result has N dimensions, has the dtype of `value` and is obtained by The result has N dimensions, has the dtype of `value` and is obtained by
broadcasting value over the output ndarray. broadcasting value over the output ndarray.
...@@ -2555,25 +2575,25 @@ class Alloc(gof.Op): ...@@ -2555,25 +2575,25 @@ class Alloc(gof.Op):
# If the output is a constant, it will have to be deepcopied # If the output is a constant, it will have to be deepcopied
# each time the function is called. So we do not fold. # each time the function is called. So we do not fold.
return False return False
elif (#The following ops work inplace of their input id 0. elif (
client[1] == 0 and # The following ops work inplace of their input id 0.
isinstance(client[0].op, ( client[1] == 0 and
#Ops that will work inplace on the Alloc. So if they isinstance(client[0].op, (
#get constant_folded, they would copy the # Ops that will work inplace on the Alloc. So if they
#constant and this is less efficients. # get constant_folded, they would copy the
# constant and this is less efficients.
#Not doing the constant folding could also lower
#the peak memory usage, as we the "constant" won't # Not doing the constant folding could also lower
#always exists. # the peak memory usage, as we the "constant" won't
theano.tensor.subtensor.IncSubtensor, # always exists.
theano.tensor.subtensor.AdvancedIncSubtensor1, theano.tensor.subtensor.IncSubtensor,
theano.tensor.subtensor.AdvancedIncSubtensor, theano.tensor.subtensor.AdvancedIncSubtensor1,
theano.tensor.blas.Gemv, theano.tensor.subtensor.AdvancedIncSubtensor,
theano.tensor.blas_c.CGemv, theano.tensor.blas.Gemv,
theano.tensor.blas.Ger, theano.tensor.blas_c.CGemv,
theano.tensor.blas_c.CGer, theano.tensor.blas.Ger,
theano.tensor.blas_scipy.ScipyGer theano.tensor.blas_c.CGer,
))): theano.tensor.blas_scipy.ScipyGer))):
return False return False
#If the clients is a transfer to the GPU, we don't want to #If the clients is a transfer to the GPU, we don't want to
#fold. We let the Alloc being moved to the GPU, then we #fold. We let the Alloc being moved to the GPU, then we
...@@ -2687,7 +2707,7 @@ class Mean(elemwise.CAReduce): ...@@ -2687,7 +2707,7 @@ class Mean(elemwise.CAReduce):
@constructor @constructor
def mean(input, axis=None, dtype=None, op=False, keepdims=False, def mean(input, axis=None, dtype=None, op=False, keepdims=False,
acc_dtype=None): acc_dtype=None):
""" """
Computes the mean value along the given axis(es) of a tensor `input` Computes the mean value along the given axis(es) of a tensor `input`
...@@ -2719,16 +2739,16 @@ def mean(input, axis=None, dtype=None, op=False, keepdims=False, ...@@ -2719,16 +2739,16 @@ def mean(input, axis=None, dtype=None, op=False, keepdims=False,
if op: if op:
if dtype not in (None, 'float64'): if dtype not in (None, 'float64'):
raise NotImplementedError( raise NotImplementedError(
'The Mean op does not support the dtype argument, ' 'The Mean op does not support the dtype argument, '
'and will always use float64. If you want to specify ' 'and will always use float64. If you want to specify '
'the dtype, call tensor.mean(..., op=False).', 'the dtype, call tensor.mean(..., op=False).',
dtype) dtype)
if acc_dtype not in (None, 'float64'): if acc_dtype not in (None, 'float64'):
raise NotImplementedError( raise NotImplementedError(
'The Mean op does not support the acc_dtype argument, ' 'The Mean op does not support the acc_dtype argument, '
'and will always use float64. If you want to specify ' 'and will always use float64. If you want to specify '
'acc_dtype, call tensor.mean(..., op=False).', 'acc_dtype, call tensor.mean(..., op=False).',
dtype) dtype)
out = Mean(axis)(input) out = Mean(axis)(input)
if keepdims: if keepdims:
out = makeKeepDims(input, out, axis) out = makeKeepDims(input, out, axis)
...@@ -2848,9 +2868,9 @@ class Default(gof.Op): ...@@ -2848,9 +2868,9 @@ class Default(gof.Op):
def make_node(self, x, default): def make_node(self, x, default):
x, default = as_tensor_variable(x), as_tensor_variable(default) x, default = as_tensor_variable(x), as_tensor_variable(default)
if x.type != default.type: if x.type != default.type:
raise TypeError('Both default() arguments must have same type', raise TypeError('Both default() arguments must have same type',
x, default) x, default)
return gof.Apply(self, [x, default], [default.type()]) return gof.Apply(self, [x, default], [default.type()])
def perform(self, node, inp, out_): def perform(self, node, inp, out_):
...@@ -2951,8 +2971,8 @@ def ceil_intdiv(a, b): ...@@ -2951,8 +2971,8 @@ def ceil_intdiv(a, b):
def mod_check(x, y): def mod_check(x, y):
"""Make sure we do not try to use complex numbers.""" """Make sure we do not try to use complex numbers."""
if (as_tensor_variable(x).dtype in complex_dtypes or if ((as_tensor_variable(x).dtype in complex_dtypes or
as_tensor_variable(y).dtype in complex_dtypes): as_tensor_variable(y).dtype in complex_dtypes)):
# Currently forbidden. # Currently forbidden.
raise scal.Mod.complex_error raise scal.Mod.complex_error
else: else:
...@@ -2982,7 +3002,7 @@ def clip(x, min, max): ...@@ -2982,7 +3002,7 @@ def clip(x, min, max):
pprint.assign(add, printing.OperatorPrinter('+', -2, 'either')) pprint.assign(add, printing.OperatorPrinter('+', -2, 'either'))
pprint.assign(mul, printing.OperatorPrinter('*', -1, 'either')) pprint.assign(mul, printing.OperatorPrinter('*', -1, 'either'))
pprint.assign(sub, printing.OperatorPrinter('-', -2, 'left')) pprint.assign(sub, printing.OperatorPrinter('-', -2, 'left'))
pprint.assign(neg, printing.OperatorPrinter('-', 0, 'either')) pprint.assign(neg, printing.OperatorPrinter('-', 0, 'either'))
pprint.assign(true_div, printing.OperatorPrinter('/', -1, 'left')) pprint.assign(true_div, printing.OperatorPrinter('/', -1, 'left'))
pprint.assign(int_div, printing.OperatorPrinter('//', -1, 'left')) pprint.assign(int_div, printing.OperatorPrinter('//', -1, 'left'))
pprint.assign(pow, printing.OperatorPrinter('**', 1, 'right')) pprint.assign(pow, printing.OperatorPrinter('**', 1, 'right'))
...@@ -3006,8 +3026,8 @@ def extract_constant(x, elemwise=True): ...@@ -3006,8 +3026,8 @@ def extract_constant(x, elemwise=True):
x = get_scalar_constant_value(x, elemwise=elemwise) x = get_scalar_constant_value(x, elemwise=elemwise)
except NotScalarConstantError: except NotScalarConstantError:
pass pass
if (isinstance(x, scal.ScalarVariable) or if ((isinstance(x, scal.ScalarVariable) or
isinstance(x, scal.sharedvar.ScalarSharedVariable)): isinstance(x, scal.sharedvar.ScalarSharedVariable))):
if x.owner and isinstance(x.owner.op, ScalarFromTensor): if x.owner and isinstance(x.owner.op, ScalarFromTensor):
x = x.owner.inputs[0] x = x.owner.inputs[0]
else: else:
...@@ -3046,11 +3066,12 @@ def batched_dot(x, y): ...@@ -3046,11 +3066,12 @@ def batched_dot(x, y):
But numpy einsum is slower than dot or tensordot: But numpy einsum is slower than dot or tensordot:
http://mail.scipy.org/pipermail/numpy-discussion/2012-October/064259.html http://mail.scipy.org/pipermail/numpy-discussion/2012-October/064259.html
""" """
result, updates = theano.scan(fn=lambda x_mat, y_mat: result, updates = theano.scan(
theano.tensor.dot(x_mat, y_mat), fn=lambda x_mat, y_mat:
outputs_info=None, theano.tensor.dot(x_mat, y_mat),
sequences=[x, y], outputs_info=None,
non_sequences=None) sequences=[x, y],
non_sequences=None)
return result return result
...@@ -3059,42 +3080,45 @@ def batched_tensordot(x, y, axes=2): ...@@ -3059,42 +3080,45 @@ def batched_tensordot(x, y, axes=2):
:param x: A Tensor with sizes e.g.: for 3D (dim1, dim3, dim2) :param x: A Tensor with sizes e.g.: for 3D (dim1, dim3, dim2)
:param y: A Tensor with sizes e.g.: for 3D (dim1, dim2, dim4) :param y: A Tensor with sizes e.g.: for 3D (dim1, dim2, dim4)
:param axes: an integer or array. If an integer, the number of axes :param axes: an integer or array. If an integer, the number of axes
to sum over. If an array, it must have two array to sum over. If an array, it must have two array
elements containing the axes to sum over in each tensor. elements containing the axes to sum over in each tensor.
If an integer i, it is converted to an array containing If an integer i, it is converted to an array containing
the last i dimensions of the first tensor and the first the last i dimensions of the first tensor and the first
i dimensions of the second tensor (excluding the first i dimensions of the second tensor (excluding the first
(batch) dimension): (batch) dimension):
axes = [range(a.ndim - i, b.ndim), range(1,i+1)] axes = [range(a.ndim - i, b.ndim), range(1,i+1)]
If an array, its two elements must contain compatible axes If an array, its two elements must contain compatible axes
of the two tensors. For example, [[1, 2], [2, 4]] means sum of the two tensors. For example, [[1, 2], [2, 4]] means sum
over the 2nd and 3rd axes of a and the 3rd and 5th axes of b. over the 2nd and 3rd axes of a and the 3rd and 5th axes of b.
(Remember axes are zero-indexed!) The 2nd axis of a and the (Remember axes are zero-indexed!) The 2nd axis of a and the
3rd axis of b must have the same shape; the same is true for 3rd axis of b must have the same shape; the same is true for
the 3rd axis of a and the 5th axis of b. the 3rd axis of a and the 5th axis of b.
:type axes: int or array-like of length 2 :type axes: int or array-like of length 2
A hybrid of batch_dot and tensordot, this function computes the A hybrid of batch_dot and tensordot, this function computes the
tensordot product between the two tensors, by iterating over the tensordot product between the two tensors, by iterating over the
first dimension using scan to perform a sequence of tensordots. first dimension using scan to perform a sequence of tensordots.
""" """
if isinstance(axes, (list, numpy.ndarray)): if isinstance(axes, (list, numpy.ndarray)):
if isinstance(axes, list): if isinstance(axes, list):
axes = numpy.asarray(axes) axes = numpy.asarray(axes)
else: else:
axes = axes.copy() axes = axes.copy()
assert numpy.greater(axes,0).all(), "All axes should be greater than one, as the first axis is iterated over (batch-wise scan)" assert numpy.greater(axes, 0).all(), (
"All axes should be greater than one, as the "
"first axis is iterated over (batch-wise scan)")
axes -= 1 axes -= 1
result, updates = theano.scan(fn=lambda x_mat, y_mat: result, updates = theano.scan(
theano.tensor.tensordot(x_mat, y_mat, axes), fn=lambda x_mat, y_mat:
outputs_info=None, theano.tensor.tensordot(x_mat, y_mat, axes),
sequences=[x, y], outputs_info=None,
non_sequences=None) sequences=[x, y],
non_sequences=None)
return result return result
def split(x, splits_size, n_splits, axis=0): def split(x, splits_size, n_splits, axis=0):
the_split = Split(n_splits) the_split = Split(n_splits)
...@@ -3174,10 +3198,10 @@ class Split(Op): ...@@ -3174,10 +3198,10 @@ class Split(Op):
except: except:
raise ValueError('Split.perform() with axis=(%s) is invalid' raise ValueError('Split.perform() with axis=(%s) is invalid'
' for x.shape==(%s)' ' for x.shape==(%s)'
% (axis, x.shape)) % (axis, x.shape))
if len(splits) != self.len_splits: if len(splits) != self.len_splits:
raise ValueError('In Split.perform(), len(splits) != len_splits.', raise ValueError('In Split.perform(), len(splits) != len_splits.',
(len(splits), self.len_splits)) (len(splits), self.len_splits))
if numpy.sum(splits) != len_along_axis: if numpy.sum(splits) != len_along_axis:
raise ValueError('The splits sum to %s, expected %s' % raise ValueError('The splits sum to %s, expected %s' %
...@@ -3233,7 +3257,7 @@ class Split(Op): ...@@ -3233,7 +3257,7 @@ class Split(Op):
grad_undefined(self, 2, n)] grad_undefined(self, 2, n)]
def R_op(self, inputs, eval_points): def R_op(self, inputs, eval_points):
if eval_points[0] is None: if eval_points[0] is None:
return [None for i in self.len_splits] return [None for i in self.len_splits]
return self.make_node(eval_points[0], *inputs[1:]).outputs return self.make_node(eval_points[0], *inputs[1:]).outputs
...@@ -3327,11 +3351,11 @@ class Join(Op): ...@@ -3327,11 +3351,11 @@ class Join(Op):
output_maker = lambda bcastable: tensor(dtype=out_dtype, output_maker = lambda bcastable: tensor(dtype=out_dtype,
broadcastable=bcastable) broadcastable=bcastable)
return self._make_node_internal(axis, tensors, return self._make_node_internal(
as_tensor_variable_args, output_maker) axis, tensors, as_tensor_variable_args, output_maker)
def _make_node_internal(self, axis, tensors, def _make_node_internal(self, axis, tensors,
as_tensor_variable_args, output_maker): as_tensor_variable_args, output_maker):
if not python_all(targs.type.ndim for targs if not python_all(targs.type.ndim for targs
in as_tensor_variable_args): in as_tensor_variable_args):
raise TypeError('Join cannot handle arguments of dimension 0.' raise TypeError('Join cannot handle arguments of dimension 0.'
...@@ -3387,7 +3411,8 @@ class Join(Op): ...@@ -3387,7 +3411,8 @@ class Join(Op):
if not python_all([x.ndim == len(bcastable) if not python_all([x.ndim == len(bcastable)
for x in as_tensor_variable_args[1:]]): for x in as_tensor_variable_args[1:]]):
raise TypeError("Join() can only join tensor with the same number of dimensions.") raise TypeError("Join() can only join tensors with the same "
"number of dimensions.")
inputs = [as_tensor_variable(axis)] + list(as_tensor_variable_args) inputs = [as_tensor_variable(axis)] + list(as_tensor_variable_args)
if inputs[0].type not in int_types: if inputs[0].type not in int_types:
...@@ -3403,7 +3428,7 @@ class Join(Op): ...@@ -3403,7 +3428,7 @@ class Join(Op):
out, = out_ out, = out_
axis, tensors = axis_and_tensors[0], axis_and_tensors[1:] axis, tensors = axis_and_tensors[0], axis_and_tensors[1:]
out[0] = theano._asarray(numpy.concatenate(tensors, axis=axis), out[0] = theano._asarray(numpy.concatenate(tensors, axis=axis),
dtype=node.outputs[0].type.dtype) dtype=node.outputs[0].type.dtype)
def c_code_cache_version(self): def c_code_cache_version(self):
return (2,) return (2,)
...@@ -3617,14 +3642,14 @@ def stack(*tensors): ...@@ -3617,14 +3642,14 @@ def stack(*tensors):
# And DebugMode can't detect error in this code as it is not in an # And DebugMode can't detect error in this code as it is not in an
# optimization. # optimization.
# See ticket #660 # See ticket #660
if numpy.all([ if numpy.all(
# in case there is direct int in tensors. [ # in case there is direct int in tensors.
isinstance(t, (numpy.number, float, int, python_complex, isinstance(t, (numpy.number, float, int, python_complex,
long)) or long)) or
(isinstance(t, Variable) and (isinstance(t, Variable) and
isinstance(t.type, TensorType) and isinstance(t.type, TensorType) and
t.ndim == 0) t.ndim == 0)
for t in tensors]): for t in tensors]):
# in case there is direct int # in case there is direct int
tensors = map(as_tensor_variable, tensors) tensors = map(as_tensor_variable, tensors)
dtype = scal.upcast(*[i.dtype for i in tensors]) dtype = scal.upcast(*[i.dtype for i in tensors])
...@@ -3649,9 +3674,10 @@ def concatenate(tensor_list, axis=0): ...@@ -3649,9 +3674,10 @@ def concatenate(tensor_list, axis=0):
# instead of # instead of
# c = concatenate((x, y)) # c = concatenate((x, y))
if not isinstance(tensor_list, (tuple, list)): if not isinstance(tensor_list, (tuple, list)):
raise TypeError("The 'tensors' argument must be either a tuple " raise TypeError(
"or a list, make sure you did not forget () or [] around " "The 'tensors' argument must be either a tuple "
"arguments of concatenate.", tensor_list) "or a list, make sure you did not forget () or [] around "
"arguments of concatenate.", tensor_list)
return join(axis, *tensor_list) return join(axis, *tensor_list)
...@@ -3682,10 +3708,10 @@ def get_vector_length(v): ...@@ -3682,10 +3708,10 @@ def get_vector_length(v):
if v.owner and isinstance(v.owner.op, Shape): if v.owner and isinstance(v.owner.op, Shape):
return v.owner.inputs[0].type.ndim return v.owner.inputs[0].type.ndim
# If we take this slice: var[:0], we know it will have 0 elements. # If we take this slice: var[:0], we know it will have 0 elements.
if (v.owner and if ((v.owner and
isinstance(v.owner.op, theano.tensor.subtensor.Subtensor) and isinstance(v.owner.op, theano.tensor.subtensor.Subtensor) and
isinstance(v.owner.op.idx_list[0], slice) and isinstance(v.owner.op.idx_list[0], slice) and
v.owner.op.idx_list[0].start in [None, 0]): v.owner.op.idx_list[0].start in [None, 0])):
stop = theano.tensor.subtensor.get_idx_list( stop = theano.tensor.subtensor.get_idx_list(
v.owner.inputs, v.owner.op.idx_list)[0].stop v.owner.inputs, v.owner.op.idx_list)[0].stop
if extract_constant(stop) == 0: if extract_constant(stop) == 0:
...@@ -3767,8 +3793,8 @@ class Reshape(Op): ...@@ -3767,8 +3793,8 @@ class Reshape(Op):
# If so, that dimension should be broadcastable. # If so, that dimension should be broadcastable.
try: try:
bcasts[index] = ( bcasts[index] = (
hasattr(y, 'get_scalar_constant_value') and hasattr(y, 'get_scalar_constant_value') and
y.get_scalar_constant_value() == 1) y.get_scalar_constant_value() == 1)
except NotScalarConstantError: except NotScalarConstantError:
pass pass
return gof.Apply(self, [x, shp], [tensor(x.type.dtype, bcasts)]) return gof.Apply(self, [x, shp], [tensor(x.type.dtype, bcasts)])
...@@ -3843,7 +3869,7 @@ class Reshape(Op): ...@@ -3843,7 +3869,7 @@ class Reshape(Op):
requ = [mul(*ishapes[0])] requ = [mul(*ishapes[0])]
elif crit > 1: elif crit > 1:
raise ValueError('shape argument to Reshape.perform' raise ValueError('shape argument to Reshape.perform'
' must have at most one entry equal to -1') ' must have at most one entry equal to -1')
return [requ] return [requ]
else: else:
oshape = [] oshape = []
...@@ -3911,11 +3937,12 @@ def reshape(x, newshape, ndim=None, name=None): ...@@ -3911,11 +3937,12 @@ def reshape(x, newshape, ndim=None, name=None):
try: try:
ndim = get_vector_length(newshape) ndim = get_vector_length(newshape)
except ValueError: except ValueError:
raise ValueError("The length of the provided shape (%s) cannot " raise ValueError(
"be automatically determined, so Theano is not able " "The length of the provided shape (%s) cannot "
"to know what the number of dimensions of the reshaped " "be automatically determined, so Theano is not able "
"variable will be. You can provide the 'ndim' keyword " "to know what the number of dimensions of the reshaped "
"argument to 'reshape' to avoid this problem." % newshape) "variable will be. You can provide the 'ndim' keyword "
"argument to 'reshape' to avoid this problem." % newshape)
op = Reshape(ndim, name) op = Reshape(ndim, name)
rval = op(x, newshape) rval = op(x, newshape)
return rval return rval
...@@ -4223,15 +4250,16 @@ def arange(start, stop=None, step=1, dtype=None): ...@@ -4223,15 +4250,16 @@ def arange(start, stop=None, step=1, dtype=None):
# `numpy.arange` returns an int64 array (on 64-bit platforms), # `numpy.arange` returns an int64 array (on 64-bit platforms),
# while the upcast above returns int32. # while the upcast above returns int32.
numpy_dtype = numpy.arange( numpy_dtype = numpy.arange(
start=numpy.array(0, dtype=start.dtype), start=numpy.array(0, dtype=start.dtype),
stop=numpy.array(1, dtype=stop.dtype), stop=numpy.array(1, dtype=stop.dtype),
step=numpy.array(1, dtype=step.dtype)).dtype step=numpy.array(1, dtype=step.dtype)).dtype
if numpy_dtype != dtype: if numpy_dtype != dtype:
if (config.cast_policy == 'numpy+floatX' and if (config.cast_policy == 'numpy+floatX' and
config.floatX == 'float32' and config.floatX == 'float32' and
numpy_dtype == 'float64' and numpy_dtype == 'float64' and
# No explicit float64 in the three arguments? # No explicit float64 in the three arguments?
python_all(dt != 'float64' python_all(
dt != 'float64'
for dt in [s.dtype for s in (start, stop, step)])): for dt in [s.dtype for s in (start, stop, step)])):
# We use float32 instead. # We use float32 instead.
assert dtype != 'float64' assert dtype != 'float64'
...@@ -4283,9 +4311,9 @@ class PermuteRowElements(Op): ...@@ -4283,9 +4311,9 @@ class PermuteRowElements(Op):
assert (y.type.dtype.startswith('int') or assert (y.type.dtype.startswith('int') or
y.type.dtype.startswith('uint')) y.type.dtype.startswith('uint'))
# Inverse should be an integer scalar # Inverse should be an integer scalar
assert inverse.type.ndim == 0 and\ assert (inverse.type.ndim == 0 and
(inverse.type.dtype.startswith('int') or\ (inverse.type.dtype.startswith('int') or
inverse.type.dtype.startswith('uint')) inverse.type.dtype.startswith('uint')))
# Match shapes of x and y # Match shapes of x and y
x_dim = x.type.ndim x_dim = x.type.ndim
...@@ -4331,11 +4359,11 @@ class PermuteRowElements(Op): ...@@ -4331,11 +4359,11 @@ class PermuteRowElements(Op):
if (numpy.__version__ <= '1.6.1' and if (numpy.__version__ <= '1.6.1' and
out.size != numpy.uint32(out.size)): out.size != numpy.uint32(out.size)):
warnings.warn( warnings.warn(
'Numpy versions 1.6.1 and below have a bug preventing ' 'Numpy versions 1.6.1 and below have a bug preventing '
'advanced indexing from correctly filling arrays that ' 'advanced indexing from correctly filling arrays that '
'are too big (>= 2^32 elements). It is possible that ' 'are too big (>= 2^32 elements). It is possible that '
'out (%s), with shape %s, is not correctly filled.' 'out (%s), with shape %s, is not correctly filled.'
% (out, out.shape)) % (out, out.shape))
else: else:
xs0 = x.shape[0] xs0 = x.shape[0]
ys0 = y.shape[0] ys0 = y.shape[0]
...@@ -4447,9 +4475,9 @@ def inverse_permutation(perm): ...@@ -4447,9 +4475,9 @@ def inverse_permutation(perm):
Each row of input should contain a permutation of the first integers. Each row of input should contain a permutation of the first integers.
""" """
return permute_row_elements( return permute_row_elements(
arange(perm.shape[-1], dtype=perm.dtype), arange(perm.shape[-1], dtype=perm.dtype),
perm, perm,
inverse=True) inverse=True)
######################### #########################
...@@ -4571,7 +4599,6 @@ class Dot(Op): ...@@ -4571,7 +4599,6 @@ class Dot(Op):
# R_op for a \dot b evaluted at c for a and d for b is # R_op for a \dot b evaluted at c for a and d for b is
# simply c \dot b + a \dot d # simply c \dot b + a \dot d
assert len(inputs) == 2 assert len(inputs) == 2
assert len(eval_points) == 2 assert len(eval_points) == 2
if eval_points[0] is None and eval_points[1] is None: if eval_points[0] is None and eval_points[1] is None:
...@@ -4599,14 +4626,16 @@ class Dot(Op): ...@@ -4599,14 +4626,16 @@ class Dot(Op):
ev0 = gof.op.get_test_value(eval_points[0]) ev0 = gof.op.get_test_value(eval_points[0])
except AttributeError: except AttributeError:
gof.op.missing_test_message( gof.op.missing_test_message(
'first eval point passed to Dot.R_op has no test value') 'first eval point passed to Dot.R_op '
'has no test value')
debugger_available = False debugger_available = False
if eval_points[1]: if eval_points[1]:
try: try:
ev1 = gof.op.get_test_value(eval_points[1]) ev1 = gof.op.get_test_value(eval_points[1])
except AttributeError: except AttributeError:
gof.op.missing_test_message( gof.op.missing_test_message(
'second eval point passed to Dot.R_op has no test value') 'second eval point passed to Dot.R_op '
'has no test value')
debugger_available = False debugger_available = False
if debugger_available: if debugger_available:
...@@ -4616,11 +4645,10 @@ class Dot(Op): ...@@ -4616,11 +4645,10 @@ class Dot(Op):
for i in xrange(2): for i in xrange(2):
if eval_point_values[i] is not None and \ if eval_point_values[i] is not None and \
input_values[i].shape != eval_point_values[i].shape: input_values[i].shape != eval_point_values[i].shape:
raise ValueError('input ' + str(i) + ' and eval_point ' + raise ValueError(
str(i) + ' to Dot.R_op ' 'input ' + str(i) + ' and eval_point ' + str(i)
'should have the ' + ' to Dot.R_op should have the same shape, but '
'same shape, but their shapes are' 'their shapes are %s and %s, respectively' % (
' %s and %s, respectively' % (
str(input_values[i].shape), str(input_values[i].shape),
str(eval_point_values[i].shape))) str(eval_point_values[i].shape)))
if eval_points[0]: if eval_points[0]:
...@@ -4658,7 +4686,7 @@ class Dot(Op): ...@@ -4658,7 +4686,7 @@ class Dot(Op):
_dot = Dot() _dot = Dot()
pprint.assign(_dot, printing.OperatorPrinter(printing.special['middle_dot'], pprint.assign(_dot, printing.OperatorPrinter(printing.special['middle_dot'],
-1, 'left')) -1, 'left'))
def dot(a, b): def dot(a, b):
...@@ -4814,7 +4842,7 @@ def tensordot(a, b, axes=2): ...@@ -4814,7 +4842,7 @@ def tensordot(a, b, axes=2):
# check if axes is valid given the dimension of a and b # check if axes is valid given the dimension of a and b
if axes > a.ndim: if axes > a.ndim:
raise ValueError('axes can not be larger than the dimension of ' raise ValueError('axes can not be larger than the dimension of '
'a (a.ndim=%i, axes=%i)' % (a.ndim, axes)) 'a (a.ndim=%i, axes=%i)' % (a.ndim, axes))
if axes > b.ndim: if axes > b.ndim:
raise ValueError('axes can not be larger than than the dimension ' raise ValueError('axes can not be larger than than the dimension '
'of b (b.ndim=%i, axes=%i)' % (b.ndim, axes)) 'of b (b.ndim=%i, axes=%i)' % (b.ndim, axes))
...@@ -4887,9 +4915,10 @@ def tensordot(a, b, axes=2): ...@@ -4887,9 +4915,10 @@ def tensordot(a, b, axes=2):
(b.ndim, numpy.max(numpy.array(b_axes)))) (b.ndim, numpy.max(numpy.array(b_axes))))
a_order = (tuple(x for x in tuple(xrange(a.ndim)) if x not in a_axes) a_order = (tuple(x for x in tuple(xrange(a.ndim)) if x not in a_axes)
+ a_axes) + a_axes)
b_order = (b_axes b_order = (b_axes + tuple(x
+ tuple(x for x in tuple(xrange(b.ndim)) if x not in b_axes)) for x in tuple(xrange(b.ndim))
if x not in b_axes))
a_shuffled = a.dimshuffle(a_order) a_shuffled = a.dimshuffle(a_order)
b_shuffled = b.dimshuffle(b_order) b_shuffled = b.dimshuffle(b_order)
...@@ -4908,8 +4937,8 @@ def outer(x, y): ...@@ -4908,8 +4937,8 @@ def outer(x, y):
if y.ndim != 1: if y.ndim != 1:
y = y.flatten() y = y.flatten()
return dot( return dot(
x.dimshuffle(0, 'x'), x.dimshuffle(0, 'x'),
y.dimshuffle('x', 0)) y.dimshuffle('x', 0))
def any(x, axis=None, keepdims=False): def any(x, axis=None, keepdims=False):
...@@ -5082,7 +5111,7 @@ def ptp(a, axis=None): ...@@ -5082,7 +5111,7 @@ def ptp(a, axis=None):
def power(x, y): def power(x, y):
return x**y return x ** y
def swapaxes(y, axis1, axis2): def swapaxes(y, axis1, axis2):
...@@ -5098,25 +5127,48 @@ def choose(a, choices, out=None, mode='raise'): ...@@ -5098,25 +5127,48 @@ def choose(a, choices, out=None, mode='raise'):
""" """
Construct an array from an index array and a set of arrays to choose from. Construct an array from an index array and a set of arrays to choose from.
First of all, if confused or uncertain, definitely look at the Examples - in its full generality, this function is less simple than it might seem from the following code description (below ndi = numpy.lib.index_tricks): First of all, if confused or uncertain, definitely look at the Examples -
in its full generality, this function is less simple than it might seem
from the following code description (below ndi = numpy.lib.index_tricks):
np.choose(a,c) == np.array([c[a[I]][I] for I in ndi.ndindex(a.shape)]). np.choose(a,c) == np.array([c[a[I]][I] for I in ndi.ndindex(a.shape)]).
But this omits some subtleties. Here is a fully general summary: But this omits some subtleties. Here is a fully general summary:
Given an ``index`` array (a) of integers and a sequence of n arrays (choices), a and each choice array are first broadcast, as necessary, to arrays of a common shape; calling these Ba and Bchoices[i], i = 0,...,n-1 we have that, necessarily, Ba.shape == Bchoices[i].shape for each i. Then, a new array with shape Ba.shape is created as follows: Given an ``index`` array (a) of integers and a sequence of n arrays
(choices), a and each choice array are first broadcast, as necessary,
if mode=raise (the default), then, first of all, each element of a (and thus Ba) must be in the range [0, n-1]; now, suppose that i (in that range) is the value at the (j0, j1, ..., jm) position in Ba - then the value at the same position in the new array is the value in Bchoices[i] at that same position; to arrays of a common shape; calling these Ba and
if mode=wrap, values in a (and thus Ba) may be any (signed) integer; modular arithmetic is used to map integers outside the range [0, n-1] back into that range; and then the new array is constructed as above; Bchoices[i], i = 0,...,n-1 we have that, necessarily,
if mode=clip, values in a (and thus Ba) may be any (signed) integer; negative integers are mapped to 0; values greater than n-1 are mapped to n-1; and then the new array is constructed as above. Ba.shape == Bchoices[i].shape for each i.
Then, a new array with shape Ba.shape is created as follows:
:Parameters: *a* - int array
This array must contain integers in [0, n-1], where n is the number of choices, unless mode=wrap or mode=clip, in which cases any integers are permissible. - if mode=raise (the default), then, first of all, each element of a
:Parameters: *choices* - sequence of arrays (and thus Ba) must be in the range [0, n-1]; now, suppose that
Choice arrays. a and all of the choices must be broadcastable to the same shape. If choices is itself an array (not recommended), then its outermost dimension (i.e., the one corresponding to choices.shape[0]) is taken as defining the ``sequence``. i (in that range) is the value at the (j0, j1, ..., jm) position in Ba -
:Parameters: *out* - array, optional then the value at the same position in the new array is the value in
If provided, the result will be inserted into this array. It should be of the appropriate shape and dtype. Bchoices[i] at that same position;
:Parameters: *mode* - {``raise`` (default), ``wrap``, ``clip``}, optional
- if mode=wrap, values in a (and thus Ba) may be any (signed) integer;
modular arithmetic is used to map integers outside the range [0, n-1]
back into that range; and then the new array is constructed as above;
- if mode=clip, values in a (and thus Ba) may be any (signed) integer;
negative integers are mapped to 0; values greater than n-1 are mapped
to n-1; and then the new array is constructed as above.
:Parameter: *a* - int array
This array must contain integers in [0, n-1], where n is the number of
choices, unless mode=wrap or mode=clip, in which cases any integers
are permissible.
:Parameter: *choices* - sequence of arrays
Choice arrays. a and all of the choices must be broadcastable to
the same shape. If choices is itself an array (not recommended),
then its outermost dimension (i.e., the one corresponding to
choices.shape[0]) is taken as defining the ``sequence``.
:Parameter: *out* - array, optional
If provided, the result will be inserted into this array.
It should be of the appropriate shape and dtype.
:Parameter: *mode* - {``raise`` (default), ``wrap``, ``clip``}, optional
Specifies how indices outside [0, n-1] will be treated: Specifies how indices outside [0, n-1] will be treated:
``raise`` : an exception is raised ``raise`` : an exception is raised
``wrap`` : value becomes value mod n ``wrap`` : value becomes value mod n
...@@ -5145,17 +5197,18 @@ class Choose(Op): ...@@ -5145,17 +5197,18 @@ class Choose(Op):
return[(shapes[0])] return[(shapes[0])]
else: else:
import theano.typed_list import theano.typed_list
assert isinstance(node.inputs[1], theano.typed_list.TypedListVariable) assert isinstance(node.inputs[1],
theano.typed_list.TypedListVariable)
raise ShapeError("Case not implemented") raise ShapeError("Case not implemented")
shape = shapes[0] shape = shapes[0]
for i in range(len(shapes[0])-1): for i in range(len(shapes[0]) - 1):
shape[i] = shapes[1][i] shape[i] = shapes[1][i]
return [(shape)] return [(shape)]
def make_node(self, a, choices): def make_node(self, a, choices):
# Import here as it isn't imported by default and we can't # Import here as it isn't imported by default and we can't
# import at the top as it would cause circular import. # import at the top as it would cause circular import.
from theano import typed_list import theano.typed_list
a = as_tensor_variable(a) a = as_tensor_variable(a)
if isinstance(choices, (tuple, list)): if isinstance(choices, (tuple, list)):
choice = theano.typed_list.make_list(choices) choice = theano.typed_list.make_list(choices)
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论