提交 ff2a27d5 authored 作者: Ian Goodfellow's avatar Ian Goodfellow

merged

...@@ -25,7 +25,8 @@ Edit ``setup.py`` to contain the newest version number :: ...@@ -25,7 +25,8 @@ Edit ``setup.py`` to contain the newest version number ::
* Change the ``version`` and ``release`` variables to new version number. * Change the ``version`` and ``release`` variables to new version number.
* Change the upper copyright year to the current year if necessary. * Change the upper copyright year to the current year if necessary.
* Update the year in the Theano/LICENSE.txt file.
Update the year in the ``Theano/LICENSE.txt`` file too, if necessary.
``NEWS.txt`` usually contains the name and date of the release, change them ``NEWS.txt`` usually contains the name and date of the release, change them
too. too.
......
...@@ -296,8 +296,79 @@ import theano and print the config variable, as in: ...@@ -296,8 +296,79 @@ import theano and print the config variable, as in:
Default: False Default: False
Remove compiled file when not needed anymore. If False, source code files are removed when they are not needed anymore.
This mean it remove file that he tried to compile but failed. This means files whose compilation failed are deleted.
Set to True to keep the source file that failed to compile to Set to True to keep those files in order to debug compilation errors.
debug them.
.. attribute:: config.DebugMode
This section contains various attributes configuring the behaviour
of mode :class:`~debugmode.DebugMode`.
.. attribute:: config.numpy.seterr_all
String Value: ``'ignore'``, ``'warn'``, ``'raise'``, ``'call'``,
``'print'``, ``'log'``, ``'None'``
Default: ``'ignore'``
Set the default behaviour described by `numpy.seterr
<http://docs.scipy.org/doc/numpy/reference/generated/numpy.seterr.html>`__.
``'None'`` means that numpy's default behaviour will not be changed (unless
one of the other `config.numpy.seterr_*` overrides it), but this behaviour
can change between numpy releases.
This flag sets the default behaviour for all kinds of floating-pont
errors, and it can be overriden for specific errors by setting one
(or more) of the flags below.
This flag's value cannot be modified during the program execution.
.. attribute:: config.numpy.seterr_divide
String Value: ``'None'``, ``'ignore'``, ``'warn'``, ``'raise'``,
``'call'``, ``'print'``, ``'log'``
Default: ``'None'``
Sets numpy's behavior for division by zero. ``'None'`` means using the
default, defined by config.numpy.seterr_all.
This flag's value cannot be modified during the program execution.
.. attribute:: config.numpy.seterr_over
String Value: ``'None'``, ``'ignore'``, ``'warn'``, ``'raise'``,
``'call'``, ``'print'``, ``'log'``
Default: ``'None'``
Sets numpy's behavior for floating-point overflow. ``'None'`` means
using the default, defined by config.numpy.seterr_all.
This flag's value cannot be modified during the program execution.
.. attribute:: config.numpy.seterr_under
String Value: ``'None'``, ``'ignore'``, ``'warn'``, ``'raise'``,
``'call'``, ``'print'``, ``'log'``
Default: ``'None'``
Sets numpy's behavior for floating-point underflow. ``'None'`` means
using the default, defined by config.numpy.seterr_all.
This flag's value cannot be modified during the program execution.
.. attribute:: numpy.seterr_invalid
String Value: ``'None'``, ``'ignore'``, ``'warn'``, ``'raise'``,
``'call'``, ``'print'``, ``'log'``
Default: ``'None'``
Sets numpy's behavior for invalid floating-point operation. ``'None'``
means using the default, defined by :attr:`config.numpy.seterr_all`.
This flag's value cannot be modified during the program execution.
...@@ -38,11 +38,11 @@ output. ...@@ -38,11 +38,11 @@ output.
Shape inference problem Shape inference problem
======================= =======================
Theano do shape information propagation in the graph. Sometimes this Theano propagates shape information in the graph. Sometimes this
can had error. Example: can lead to errors. For example:
.. code-block:: python .. code-block:: python
import numpy import numpy
import theano import theano
x = theano.tensor.matrix('x') x = theano.tensor.matrix('x')
...@@ -71,10 +71,10 @@ can had error. Example: ...@@ -71,10 +71,10 @@ can had error. Example:
# |Shape_i{1} [@55959184] '' 0 # |Shape_i{1} [@55959184] '' 0
# | |<TensorType(float64, matrix)> [@55583888] # | |<TensorType(float64, matrix)> [@55583888]
print f(xv,yv)# DONT RAISE AN ERROR AS SHOULD BE. print f(xv,yv)# DOES NOT RAISE AN ERROR AS SHOULD BE.
#[8,4] #[8,4]
f = theano.function([x,y], z)# Don't take the shape. f = theano.function([x,y], z)# Do not take the shape.
theano.printing.debugprint(f) theano.printing.debugprint(f)
#Join [@44540496] '' 0 #Join [@44540496] '' 0
# |0 [@44540432] # |0 [@44540432]
...@@ -84,22 +84,25 @@ can had error. Example: ...@@ -84,22 +84,25 @@ can had error. Example:
f(xv,yv) f(xv,yv)
# Raise a dimensions mismatch error. # Raise a dimensions mismatch error.
As you see, when you ask for the shape of some computation(join in the As you see, when you ask for the shape of some computation (join in the
example), we sometimes compute the shape without executing the example), we sometimes compute an inferred shape directly, without executing
computation(there is no join in the first output or debugprint). the computation itself (there is no join in the first output or debugprint).
This make the computation of the shape faster, but can hide error. In This makes the computation of the shape faster, but it can hide errors. In
the example, the computation of the shape of join is done on the first the example, the computation of the shape of join is done on the first
theano variable in the join, not on the other. theano variable in the join, not on the other.
This can probably happen with many other op as elemwise, dot, ... This can probably happen with many other op as elemwise, dot, ...
Indeed, to make some optimizations (for speed or stability, for instance),
Theano can assume that the computation is correct and consistent
in the first place, this is the case here.
You can detect those problem by running the code without this You can detect those problem by running the code without this
optimization with the theano flag optimization, with the Theano flag
`optimizer_excluding=local_shape_to_shape_i`. You can also have the `optimizer_excluding=local_shape_to_shape_i`. You can also have the
same effect by running in the mode FAST_COMPILE(won't apply this same effect by running in the mode FAST_COMPILE (it will not apply this
optimization and most other optimization too) or DEBUG_MODE(will test optimization, nor most other optimizations) or DEBUG_MODE (it will test
before and after all optimizations(much slower)). before and after all optimizations (much slower)).
Specifing exact shape Specifing exact shape
......
...@@ -81,6 +81,36 @@ import gof ...@@ -81,6 +81,36 @@ import gof
if config.device.startswith('gpu') or config.init_gpu_device.startswith('gpu'): if config.device.startswith('gpu') or config.init_gpu_device.startswith('gpu'):
import theano.sandbox.cuda import theano.sandbox.cuda
# Use config.numpy to call numpy.seterr
import numpy
if config.numpy.seterr_all == 'None':
_all = None
else:
_all = config.numpy.seterr_all
if config.numpy.seterr_divide == 'None':
_divide = None
else:
_divide = config.numpy.seterr_divide
if config.numpy.seterr_over == 'None':
_over = None
else:
_over = config.numpy.seterr_over
if config.numpy.seterr_under == 'None':
_under = None
else:
_under = config.numpy.seterr_under
if config.numpy.seterr_invalid == 'None':
_invalid = None
else:
_invalid = config.numpy.seterr_invalid
numpy.seterr(
all=_all,
divide=_divide,
over=_over,
under=_under,
invalid=_invalid)
del _all, _divide, _over, _under, _invalid
## import scalar_opt ## import scalar_opt
### This is defined here because it is designed to work across symbolic datatypes ### This is defined here because it is designed to work across symbolic datatypes
......
...@@ -535,7 +535,13 @@ def _check_inputs(node, storage_map, r_vals, dr_vals, active_nodes, clobber_dr_v ...@@ -535,7 +535,13 @@ def _check_inputs(node, storage_map, r_vals, dr_vals, active_nodes, clobber_dr_v
if warn_input_not_reused and destroyed_res_list: if warn_input_not_reused and destroyed_res_list:
dmap=getattr(node.op,'destroy_map',{}) dmap=getattr(node.op,'destroy_map',{})
for oo,ii in dmap.iteritems(): for oo,ii in dmap.iteritems():
if storage_map[node.outputs[oo]][0] is not storage_map[node.inputs[ii[0]]][0]: out_var = storage_map[node.outputs[oo]][0]
in_var = storage_map[node.inputs[ii[0]]][0]
if isinstance (node.op, theano.compile.mode.OutputGuard):
# The point of OutputGuard is to be declared as destructive
# while not destroying anything
continue
if out_var is not in_var:
opt_warning("input idx %d marked as destroyed was not changed for node '%s'"%(ii[0],str(node))) opt_warning("input idx %d marked as destroyed was not changed for node '%s'"%(ii[0],str(node)))
if warn_input_not_reused: if warn_input_not_reused:
......
...@@ -190,7 +190,36 @@ class DeepCopyOp(theano.gof.Op): ...@@ -190,7 +190,36 @@ class DeepCopyOp(theano.gof.Op):
else: else:
super(DeepCopyOp, self).c_code(node, name, inames, onames, sub) super(DeepCopyOp, self).c_code(node, name, inames, onames, sub)
class ViewOp(theano.gof.Op):
def __init__(self):
self.view_map={0:[0]}
def __str__(self):
return self.__class__.__name__
def __hash__(self):
return hash(type(self))
def __eq__(self, other):
return type(self) == type(other)
def make_node(self, x):
return theano.gof.Apply(self, [x], [x.type()])
def perform( self, node, args, outs):
outs[0][0] = args[0]
def infer_shape(self, node, input_shapes):
return input_shapes
def grad(self, args, g_outs):
return g_outs
deep_copy_op = DeepCopyOp() deep_copy_op = DeepCopyOp()
view_op = ViewOp()
DUPLICATE = ['DUPLICATE'] # unique id object used as a placeholder for duplicate entries DUPLICATE = ['DUPLICATE'] # unique id object used as a placeholder for duplicate entries
class Function(object): class Function(object):
...@@ -771,7 +800,10 @@ def insert_deepcopy(env, wrapped_inputs, wrapped_outputs): ...@@ -771,7 +800,10 @@ def insert_deepcopy(env, wrapped_inputs, wrapped_outputs):
# We could don't put deep copy if both outputs have borrow==True # We could don't put deep copy if both outputs have borrow==True
# and not(wrapped_outputs[i].borrow and wrapped_outputs[j].borrow): # and not(wrapped_outputs[i].borrow and wrapped_outputs[j].borrow):
if env.outputs[j] in views_of_output_i: if env.outputs[j] in views_of_output_i:
env.change_input('output', i, deep_copy_op(env.outputs[i])) if wrapped_outputs[i].borrow and wrapped_outputs[j].borrow:
env.change_input('output',i, view_op(env.outputs[i]))
else:
env.change_input('output', i, deep_copy_op(env.outputs[i]))
copied = True copied = True
break break
...@@ -784,10 +816,22 @@ def insert_deepcopy(env, wrapped_inputs, wrapped_outputs): ...@@ -784,10 +816,22 @@ def insert_deepcopy(env, wrapped_inputs, wrapped_outputs):
continue continue
if input_j in updated_env_inputs: if input_j in updated_env_inputs:
continue continue
# We could don't put deep_copy_op if the input and the output have borrow==True
if input_j in views_of_output_i: if input_j in views_of_output_i:
env.change_input('output', i, deep_copy_op(env.outputs[i])) # We don't put deep_copy_op if the input and the output have borrow==True
break if input_j in env.inputs:
j = env.inputs.index(input_j)
if wrapped_outputs[i].borrow and wrapped_inputs[j].borrow:
env.change_input('output',i, view_op(env.outputs[i]))
break
else:
env.change_input('output', i, deep_copy_op(env.outputs[i]))
break
elif wrapped_outputs[i].borrow:
env.change_input('output',i, view_op(env.outputs[i]))
break
else:
env.change_input('output', i, deep_copy_op(env.outputs[i]))
break
NODEFAULT = ['NODEFAULT'] NODEFAULT = ['NODEFAULT']
class FunctionMaker(object): class FunctionMaker(object):
......
...@@ -164,6 +164,11 @@ class In(SymbolicInput): ...@@ -164,6 +164,11 @@ class In(SymbolicInput):
True: permit the compiled function to modify the python object being passed as the input True: permit the compiled function to modify the python object being passed as the input
False: do not permit the compiled function to modify the python object being passed as the input. False: do not permit the compiled function to modify the python object being passed as the input.
borrow: Bool (default: False if update is None, True if update is not None)
True: permit the output of the compiled function to be aliased to the input
False: do not permit any output to be aliased to the input
strict: Bool (default: False) strict: Bool (default: False)
True: means that the value you pass for this input must have exactly the right type True: means that the value you pass for this input must have exactly the right type
False: the value you pass for this input may be cast automatically to the proper type False: the value you pass for this input may be cast automatically to the proper type
...@@ -194,7 +199,7 @@ class In(SymbolicInput): ...@@ -194,7 +199,7 @@ class In(SymbolicInput):
def __init__(self, variable, name=None, value=None, update=None, def __init__(self, variable, name=None, value=None, update=None,
mutable=None, strict=False, allow_downcast=None, autoname=True, mutable=None, strict=False, allow_downcast=None, autoname=True,
implicit=None, borrow=None): implicit=None, borrow=None):
# mutable implies the output can be both aliased to the input and that the input can be # mutable implies the output can be both aliased to the input and that the input can be
# destroyed. borrow simply implies the output can be aliased to the input. Thus # destroyed. borrow simply implies the output can be aliased to the input. Thus
# mutable=True should require borrow=True. Raise warning when borrow is explicitely set # mutable=True should require borrow=True. Raise warning when borrow is explicitely set
...@@ -210,7 +215,7 @@ class In(SymbolicInput): ...@@ -210,7 +215,7 @@ class In(SymbolicInput):
# borrow=None basically means False. We can't set default value to False because of the # borrow=None basically means False. We can't set default value to False because of the
# above business with mutable. # above business with mutable.
if borrow is None: if borrow is None:
borrow = False borrow = False
if implicit is None: if implicit is None:
...@@ -226,6 +231,7 @@ class In(SymbolicInput): ...@@ -226,6 +231,7 @@ class In(SymbolicInput):
autoname=autoname, autoname=autoname,
implicit=implicit) implicit=implicit)
self.value = value self.value = value
self.borrow = borrow
if self.implicit and value is None: if self.implicit and value is None:
raise TypeError('An implicit input must be given a default value') raise TypeError('An implicit input must be given a default value')
......
...@@ -244,7 +244,7 @@ def rebuild_collect_shared( outputs ...@@ -244,7 +244,7 @@ def rebuild_collect_shared( outputs
class Param(object): class Param(object):
def __init__(self, variable, default=None, name=None, mutable=False, def __init__(self, variable, default=None, name=None, mutable=False,
strict=False, allow_downcast=None, implicit=None): strict=False, allow_downcast=None, implicit=None, borrow = False):
""" """
:param variable: A variable in an expression graph to use as a compiled-function parameter :param variable: A variable in an expression graph to use as a compiled-function parameter
...@@ -255,6 +255,11 @@ class Param(object): ...@@ -255,6 +255,11 @@ class Param(object):
:param mutable: True -> function is allowed to modify this argument. :param mutable: True -> function is allowed to modify this argument.
:param borrow: True -> function is allowed to alias some output to
this input
False: do not permit any output to be aliased to the input
:param strict: False -> function arguments may be copied or casted to match the :param strict: False -> function arguments may be copied or casted to match the
type required by the parameter `variable`. True -> function arguments must exactly match the type type required by the parameter `variable`. True -> function arguments must exactly match the type
required by `variable`. required by `variable`.
...@@ -271,9 +276,15 @@ class Param(object): ...@@ -271,9 +276,15 @@ class Param(object):
self.default = default self.default = default
self.name = name self.name = name
self.mutable = mutable self.mutable = mutable
# Mutable implies borrow. You can get borrow = False because of the
# default and it is a bit annoying to require the user to set both
# borrow and mutable to True
if mutable:
borrow = True
self.strict = strict self.strict = strict
self.allow_downcast = allow_downcast self.allow_downcast = allow_downcast
self.implicit = implicit self.implicit = implicit
self.borrow = borrow
def pfunc(params, outputs=None, mode=None, updates=[], givens=[], def pfunc(params, outputs=None, mode=None, updates=[], givens=[],
no_default_updates=False, accept_inplace=False, name=None, no_default_updates=False, accept_inplace=False, name=None,
...@@ -396,6 +407,7 @@ def _pfunc_param_to_in(param, strict=False, allow_downcast=None): ...@@ -396,6 +407,7 @@ def _pfunc_param_to_in(param, strict=False, allow_downcast=None):
value=param.default, value=param.default,
mutable=param.mutable, mutable=param.mutable,
strict=param.strict, strict=param.strict,
borrow = param.borrow,
allow_downcast=param.allow_downcast, allow_downcast=param.allow_downcast,
implicit = param.implicit) implicit = param.implicit)
raise TypeError('Unknown parameter type: %s' % type(param)) raise TypeError('Unknown parameter type: %s' % type(param))
......
...@@ -304,11 +304,8 @@ class T_function(unittest.TestCase): ...@@ -304,11 +304,8 @@ class T_function(unittest.TestCase):
assert (out==4).all() assert (out==4).all()
out[0]=3 out[0]=3
out2 = f() out2 = f()
# Currently we don't do this optimization! assert out2 is out
# As this is a corner case that is not usefull for use assert (out2==3).all()
# We probably won't optimize it.
assert out2 is not out
assert (out2==4).all()
def test_borrow_input(self): def test_borrow_input(self):
......
...@@ -71,7 +71,7 @@ AddConfigVar('home', ...@@ -71,7 +71,7 @@ AddConfigVar('home',
#This expanduser works on windows (see discussion on theano-users, July 13 2010) #This expanduser works on windows (see discussion on theano-users, July 13 2010)
AddConfigVar('nocleanup', AddConfigVar('nocleanup',
"suppress the deletion of code files that did not compile cleanly", "Suppress the deletion of code files that did not compile cleanly",
BoolParam(False)) BoolParam(False))
AddConfigVar('tensor.cmp_sloppy', AddConfigVar('tensor.cmp_sloppy',
...@@ -115,6 +115,44 @@ AddConfigVar('experimental.mrg', ...@@ -115,6 +115,44 @@ AddConfigVar('experimental.mrg',
"Another random number generator that work on the gpu", "Another random number generator that work on the gpu",
BoolParam(False)) BoolParam(False))
AddConfigVar('numpy.seterr_all',
("Sets numpy's behaviour for floating-point errors, ",
"see numpy.seterr. "
"'None' means not to change numpy's default, which can be "
"different for different numpy releases. "
"This flag sets the default behaviour for all kinds of floating-"
"point errors, its effect can be overriden for specific errors "
"by the following flags: seterr_divide, seterr_over, "
"seterr_under and seterr_invalid."),
EnumStr('ignore', 'warn', 'raise', 'call', 'print', 'log', 'None',
allow_override=False))
AddConfigVar('numpy.seterr_divide',
("Sets numpy's behavior for division by zero, see numpy.seterr. "
"'None' means using the default, defined by numpy.seterr_all."),
EnumStr('None', 'ignore', 'warn', 'raise', 'call', 'print', 'log',
allow_override=False))
AddConfigVar('numpy.seterr_over',
("Sets numpy's behavior for floating-point overflow, "
"see numpy.seterr. "
"'None' means using the default, defined by numpy.seterr_all."),
EnumStr('None', 'ignore', 'warn', 'raise', 'call', 'print', 'log',
allow_override=False))
AddConfigVar('numpy.seterr_under',
("Sets numpy's behavior for floating-point underflow, "
"see numpy.seterr. "
"'None' means using the default, defined by numpy.seterr_all."),
EnumStr('None', 'ignore', 'warn', 'raise', 'call', 'print', 'log',
allow_override=False))
AddConfigVar('numpy.seterr_invalid',
("Sets numpy's behavior for invalid floating-point operation, "
"see numpy.seterr. "
"'None' means using the default, defined by numpy.seterr_all."),
EnumStr('None', 'ignore', 'warn', 'raise', 'call', 'print', 'log',
allow_override=False))
### ###
### To disable some warning about old bug that are fixed now. ### To disable some warning about old bug that are fixed now.
......
...@@ -5,10 +5,10 @@ WARNING ...@@ -5,10 +5,10 @@ WARNING
This directory is for the internal of Theano. This directory is for the internal of Theano.
You are strongly adviced to don't use it except if you know You are strongly advised not to use it, except if you know
what you do! what you are doing!
If you want to use scalar variable in a Theano graph, If you want to use a scalar variable in a Theano graph,
you probably want to use theano.tensor.[c,z,f,d,b,w,i,l,]scalar! you probably want to use theano.tensor.[c,z,f,d,b,w,i,l,]scalar!
""" """
...@@ -113,7 +113,7 @@ class Scalar(Type): ...@@ -113,7 +113,7 @@ class Scalar(Type):
raise TypeError("Could not convert %s (value=%s) to %s" % (type(data), data, self.dtype), e) raise TypeError("Could not convert %s (value=%s) to %s" % (type(data), data, self.dtype), e)
def values_eq_approx(self, a, b, tolerance = 1e-4): def values_eq_approx(self, a, b, tolerance = 1e-4):
return abs(a - b) / (a+b) < tolerance return abs(a - b) <= ((abs(a)+abs(b)) * tolerance)
def c_headers(self): def c_headers(self):
l=['<math.h>'] l=['<math.h>']
......
...@@ -846,15 +846,7 @@ def scan( fn ...@@ -846,15 +846,7 @@ def scan( fn
info['inplace'] = False info['inplace'] = False
info['gpu'] = False info['gpu'] = False
revised_outs = [] local_op = scan_op.Scan( inner_inputs, new_outs, info )
for o in new_outs:
if (o in inner_inputs or
isinstance(o, tensor.Constant)):
revised_outs.append( scan_utils.cloneOp(o))
else:
revised_outs.append(o)
local_op = scan_op.Scan( inner_inputs, revised_outs, info )
## ##
### Step 8. Compute the outputs using the scan op ### Step 8. Compute the outputs using the scan op
......
...@@ -2007,7 +2007,34 @@ class T_Scan(unittest.TestCase): ...@@ -2007,7 +2007,34 @@ class T_Scan(unittest.TestCase):
assert scan1.owner.op == scan2.owner.op assert scan1.owner.op == scan2.owner.op
assert hash(scan1.owner.op) == hash(scan2.owner.op) assert hash(scan1.owner.op) == hash(scan2.owner.op)
def test_same(self):
# This test is checking a bug discovered by Arnaud and it is based
# on his code
x = theano.tensor.fmatrix('x')
mem_val = numpy.zeros((2,), dtype='float32')
memory = theano.shared(mem_val.copy())
W = theano.shared(numpy.random.random((5, 2)).astype('float32'))
def f(inp, mem):
i = theano.tensor.join(0, inp, mem)
d = theano.tensor.dot(i, W)
return d, d
outs, updts = theano.scan(f, sequences=[x],
non_sequences=[],
outputs_info=[None, memory])
f = theano.function([x], outs[0])
f2 = theano.function([x], outs[1])
x_val = numpy.random.random((4, 3)).astype('float32')
f_vals = f(x_val)
memory.set_value(mem_val.copy())
f2_vals = f2(x_val)
assert numpy.allclose(f_vals, f2_vals)
if __name__ == '__main__': if __name__ == '__main__':
#''' #'''
......
...@@ -190,6 +190,63 @@ class T_AddMul(unittest.TestCase): ...@@ -190,6 +190,63 @@ class T_AddMul(unittest.TestCase):
self.assertTrue(numpy.all(val.todense() == numpy.array([[1, 0], self.assertTrue(numpy.all(val.todense() == numpy.array([[1, 0],
[9, 0], [0, 36]]))) [9, 0], [0, 36]])))
def test_upcast(self):
array1 = numpy.array([[1, 0], [3, 0], [0, 6]], dtype='float32')
array2 = numpy.array([[1, 0], [3, 0], [0, 6]], dtype='int32')
array3 = numpy.array([[1, 0], [3, 0], [0, 6]], dtype='int8')
# AddSS and MulSS
for mtype in _mtypes:
a = mtype(array1)
aR = as_sparse_variable(a)
b = mtype(array2)
bR = as_sparse_variable(b)
c = mtype(array3)
cR = as_sparse_variable(c)
# Ops that do not upcast
self.assertRaises(NotImplementedError, add, aR, bR)
self.assertRaises(NotImplementedError, add, bR, aR)
self.assertRaises(NotImplementedError, add, bR, cR)
self.assertRaises(NotImplementedError, add, cR, bR)
self.assertRaises(NotImplementedError, add, aR, cR)
self.assertRaises(NotImplementedError, add, cR, aR)
self.assertRaises(NotImplementedError, mul, aR, bR)
self.assertRaises(NotImplementedError, mul, bR, aR)
self.assertRaises(NotImplementedError, mul, bR, cR)
self.assertRaises(NotImplementedError, mul, cR, bR)
self.assertRaises(NotImplementedError, mul, aR, cR)
self.assertRaises(NotImplementedError, mul, cR, aR)
# AddSD and MulSD
for mtype in _mtypes:
a = mtype(array1)
a_sv = as_sparse_variable(a)
a_dv = tensor.as_tensor_variable(array1)
b = mtype(array2)
b_sv = as_sparse_variable(b)
b_dv = tensor.as_tensor_variable(array2)
c = mtype(array3)
c_sv = as_sparse_variable(c)
c_dv = tensor.as_tensor_variable(array3)
# add does not upcast
self.assertRaises(NotImplementedError, add, a_sv, b_dv)
self.assertRaises(NotImplementedError, add, b_sv, a_dv)
self.assertRaises(NotImplementedError, add, b_sv, c_dv)
self.assertRaises(NotImplementedError, add, c_sv, b_dv)
self.assertRaises(NotImplementedError, add, a_sv, c_dv)
self.assertRaises(NotImplementedError, add, c_sv, a_dv)
# mul upcasts the dense input if needed
self.assertRaises(NotImplementedError, mul, a_sv, b_dv)
self.assertRaises(NotImplementedError, mul, b_sv, a_dv)
assert mul(b_sv, c_dv).dtype == 'int32'
self.assertRaises(NotImplementedError, mul, c_sv, b_dv)
assert mul(a_sv, c_dv).dtype == 'float32'
self.assertRaises(NotImplementedError, mul, c_sv, a_dv)
class T_conversion(unittest.TestCase): class T_conversion(unittest.TestCase):
def setUp(self): def setUp(self):
......
...@@ -3753,7 +3753,7 @@ class Reshape(Op): ...@@ -3753,7 +3753,7 @@ class Reshape(Op):
shp_orig = shp shp_orig = shp
shp = as_tensor_variable(shp, ndim=1) shp = as_tensor_variable(shp, ndim=1)
if not shp.dtype.startswith('int'): if not shp.dtype.startswith('int'):
raise TypeError("Shape must be integers") raise TypeError("Shape must be integers", shp, shp.dtype)
assert shp.ndim == 1 assert shp.ndim == 1
if isinstance(shp, TensorConstant): if isinstance(shp, TensorConstant):
bcast = [s==1 for s in shp.data] bcast = [s==1 for s in shp.data]
...@@ -3788,9 +3788,18 @@ class Reshape(Op): ...@@ -3788,9 +3788,18 @@ class Reshape(Op):
g_out, = grads g_out, = grads
return [reshape(g_out, shape(x), ndim=x.ndim), None] return [reshape(g_out, shape(x), ndim=x.ndim), None]
def infer_shape(self, node, ishapes): def infer_shape(self, node, ishapes):
#we can't just put node.inputs[1] as not all op support interation # inputs[1] can contain at most one value of '-1', meaning the actual
#and this is needed in the ShapeOptimizer # shape of the output will be automatically computed by reshape, so
return [tuple([node.inputs[1][i] for i in range(self.ndim)])] # that the total number of elements stays the same.
# TODO: Maybe put that formula here?
# It's not trivial, because we would have to check if the product of
# all the non-minus-one shapes is a divisor of the product of the
# original shapes.
return [tuple([switch(eq(node.inputs[1][i], -1),
theano.tensor.opt.Shape_i(i)(node.outputs[0]),
node.inputs[1][i])
for i in range(self.ndim)]
)]
def reshape(x, newshape, ndim=None, name=None): def reshape(x, newshape, ndim=None, name=None):
if ndim is None: if ndim is None:
...@@ -4739,10 +4748,10 @@ def grad(cost, wrt, g_cost=None, consider_constant=[], warn_type=False, ...@@ -4739,10 +4748,10 @@ def grad(cost, wrt, g_cost=None, consider_constant=[], warn_type=False,
ret = [] ret = []
for p in wrt: for p in wrt:
if p not in gmap and not assume_continuously_differentiable: if p not in gmap and not assume_continuously_differentiable:
raise ValueError(("grad method was asked to compute the graident " raise ValueError(("grad method was asked to compute the gradient "
"with respect to a variable that is not part of " "with respect to a variable that is not part of "
"the computational graph of the cost or is used " "the computational graph of the cost, or is used "
"by a non-differentiable operator "),p) "by a non-differentiable operator"), p)
else: else:
ret.append(gmap.get(p, zeros_like(p))) ret.append(gmap.get(p, zeros_like(p)))
......
...@@ -3290,79 +3290,102 @@ class T_op_cache(unittest.TestCase): ...@@ -3290,79 +3290,102 @@ class T_op_cache(unittest.TestCase):
a = numpy.random.rand(5,2).astype(config.floatX) a = numpy.random.rand(5,2).astype(config.floatX)
self.assertTrue(numpy.all(fn_py(a) == fn_c_or_py(a))) self.assertTrue(numpy.all(fn_py(a) == fn_c_or_py(a)))
class T_reshape(unittest.TestCase):
def setUp(self):
utt.seed_rng()
def test_reshape(): def test_reshape(self):
a = dvector()
a = dvector() b = dmatrix()
b = dmatrix() d = dmatrix()
d = dmatrix()
#basic to 1 dim(without list)
#basic to 1 dim(without list) c = reshape(b, as_tensor_variable(6), ndim=1)
c = reshape(b, as_tensor_variable(6), ndim=1) f = inplace_func([b], c)
f = inplace_func([b], c) assert numpy.all(f(numpy.asarray([[0,1,2],[3,4,5]])) == numpy.asarray([0,1,2,3,4,5]))
assert numpy.all(f(numpy.asarray([[0,1,2],[3,4,5]])) == numpy.asarray([0,1,2,3,4,5])) #print f.maker.env.toposort()
#print f.maker.env.toposort() #check that we remove the useless reshape
#check that we remove the useless reshape
#basic to 1 dim(with list)
#basic to 1 dim(with list) c = reshape(b, (as_tensor_variable(6),), ndim=1)
c = reshape(b, (as_tensor_variable(6),), ndim=1) f = inplace_func([b], c)
f = inplace_func([b], c) assert numpy.all(f(numpy.asarray([[0,1,2],[3,4,5]])) == numpy.asarray([0,1,2,3,4,5]))
assert numpy.all(f(numpy.asarray([[0,1,2],[3,4,5]])) == numpy.asarray([0,1,2,3,4,5])) #print f.maker.env.toposort()
#print f.maker.env.toposort() #check that we remove the useless reshape
#check that we remove the useless reshape
#basic to shape object of same ndim
#basic to shape object of same ndim c = reshape(b,d.shape)
c = reshape(b,d.shape) f = inplace_func([b,d], c)
f = inplace_func([b,d], c) assert numpy.all(f(numpy.asarray([[0,1,2],[3,4,5]]),[[0,1],[2,3],[4,5]]) == numpy.asarray([[0,1],[2,3],[4,5]]))
assert numpy.all(f(numpy.asarray([[0,1,2],[3,4,5]]),[[0,1],[2,3],[4,5]]) == numpy.asarray([[0,1],[2,3],[4,5]]))
#basic to 2 dims
#basic to 2 dims c = reshape(a, [2,3])
c = reshape(a, [2,3]) f = inplace_func([a], c)
f = inplace_func([a], c) assert numpy.all(f(numpy.asarray([0,1,2,3,4,5])) == numpy.asarray([[0,1,2], [3,4,5]]))
assert numpy.all(f(numpy.asarray([0,1,2,3,4,5])) == numpy.asarray([[0,1,2], [3,4,5]]))
#test that it works without inplace operations
#test that it works without inplace operations a_val = numpy.asarray([0,1,2,3,4,5])
a_val = numpy.asarray([0,1,2,3,4,5]) a_val_copy = numpy.asarray([0,1,2,3,4,5])
a_val_copy = numpy.asarray([0,1,2,3,4,5]) b_val = numpy.asarray([[0,1,2],[3,4,5]])
b_val = numpy.asarray([[0,1,2],[3,4,5]])
f_sub = inplace_func([a,b], c-b)
assert numpy.all(f_sub(a_val, b_val) == 0.0)
assert numpy.all(a_val == a_val_copy)
#test that it works with inplace operations
a_val = theano._asarray([0,1,2,3,4,5], dtype='float64')
a_val_copy = theano._asarray([0,1,2,3,4,5], dtype='float64')
b_val = theano._asarray([[0,1,2],[3,4,5]], dtype='float64')
f_sub = inplace_func([a,b], c-b)
assert numpy.all(f_sub(a_val, b_val) == 0.0)
assert numpy.all(a_val == a_val_copy)
# verify gradient
def just_vals(v):
return Reshape(2)(v, theano._asarray([2,3], dtype='int32'))
utt.verify_grad(just_vals, [a_val])
#test infer_shape
f_sub = function([a,b], (c-b).shape)
if config.mode=="FAST_COMPILE":
assert len(f_sub.maker.env.toposort())==3
else:
topo = f_sub.maker.env.toposort()
assert len(topo)==1
topo[0].op == theano.compile.function_module.deep_copy_op
#assert numpy.all(f_sub(a_val,numpy.asarray([[0,1],[2,3],[4,5]]))==[2,3])#work in FAST_RUN, but fail on other!
#assert numpy.all(f_sub(a_val,numpy.asarray([[0,1],[2,3],[4,5],[6,7]]))==[2,3])#work in FAST_RUN, but fail on other!
f_sub = inplace_func([a,b], c-b) # test broadcast flag for constant value of 1
assert numpy.all(f_sub(a_val, b_val) == 0.0) c = reshape(b, (b.shape[0],b.shape[1],1))
assert numpy.all(a_val == a_val_copy) f = inplace_func([b], c)
assert numpy.all(f(numpy.asarray([[0,1,2],[3,4,5]])) == numpy.asarray([[[0],[1],[2]],[[3],[4],[5]]]))
assert f.maker.env.toposort()[-2].outputs[0].type.broadcastable==(False, False, True)
#test that it works with inplace operations assert numpy.all(f_sub(a_val,b_val)==[2,3])
a_val = theano._asarray([0,1,2,3,4,5], dtype='float64')
a_val_copy = theano._asarray([0,1,2,3,4,5], dtype='float64')
b_val = theano._asarray([[0,1,2],[3,4,5]], dtype='float64')
f_sub = inplace_func([a,b], c-b) def test_infer_shape(self):
assert numpy.all(f_sub(a_val, b_val) == 0.0) a = matrix('a')
assert numpy.all(a_val == a_val_copy) shapes = ivector('shapes')
ndim = 2
# verify gradient r = a.reshape(shapes, ndim=2)
def just_vals(v): z = zeros_like(r)
return Reshape(2)(v, theano._asarray([2,3], dtype='int32'))
utt.verify_grad(just_vals, [a_val])
#test infer_shape f = function([a, shapes], z.shape)
f_sub = function([a,b], (c-b).shape)
if config.mode=="FAST_COMPILE":
assert len(f_sub.maker.env.toposort())==3
else:
topo = f_sub.maker.env.toposort()
assert len(topo)==1
topo[0].op == theano.compile.function_module.deep_copy_op
#assert numpy.all(f_sub(a_val,numpy.asarray([[0,1],[2,3],[4,5]]))==[2,3])#work in FAST_RUN, but fail on other!
#assert numpy.all(f_sub(a_val,numpy.asarray([[0,1],[2,3],[4,5],[6,7]]))==[2,3])#work in FAST_RUN, but fail on other!
# test broadcast flag for constant value of 1 rng = numpy.random.RandomState(seed=utt.fetch_seed())
c = reshape(b, (b.shape[0],b.shape[1],1)) a_val = rng.uniform(size=(3,4)).astype(config.floatX)
f = inplace_func([b], c)
assert numpy.all(f(numpy.asarray([[0,1,2],[3,4,5]])) == numpy.asarray([[[0],[1],[2]],[[3],[4],[5]]]))
assert f.maker.env.toposort()[-2].outputs[0].type.broadcastable==(False, False, True)
self.assertTrue((f(a_val, [4, 3]) == [4, 3]).all())
self.assertTrue((f(a_val, [-1, 3]) == [4, 3]).all())
self.assertTrue((f(a_val, [4, -1]) == [4, 3]).all())
self.assertRaises(ValueError, f, a_val, [-1, 5])
self.assertRaises(ValueError, f, a_val, [7, -1])
self.assertRaises(ValueError, f, a_val, [7, 5])
self.assertRaises(ValueError, f, a_val, [-1, -1])
assert numpy.all(f_sub(a_val,b_val)==[2,3])
def test_make_column_matrix_broadcastable(): def test_make_column_matrix_broadcastable():
# The goal of the operation made by `b` is to ensure the second dimension # The goal of the operation made by `b` is to ensure the second dimension
......
...@@ -2787,11 +2787,6 @@ def test_local_tensor_scalar_tensor(): ...@@ -2787,11 +2787,6 @@ def test_local_tensor_scalar_tensor():
assert len(cast_nodes) == 0 assert len(cast_nodes) == 0
f(0) f(0)
@dec.knownfailureif(
isinstance(theano.compile.mode.get_default_mode(),
theano.compile.debugmode.DebugMode),
("This test fails in DEBUG_MODE, but the generated code is OK. "
"It is actually a problem of DEBUG_MODE, see #624."))
def test_local_scalar_tensor_scalar(): def test_local_scalar_tensor_scalar():
dtypes = ['int8', 'int16', 'int32', 'int64', dtypes = ['int8', 'int16', 'int32', 'int64',
'uint8', 'uint16', 'uint32', 'uint64', 'uint8', 'uint16', 'uint32', 'uint64',
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论