提交 ea02793c authored 作者: James Bergstra's avatar James Bergstra

merge

...@@ -620,6 +620,7 @@ class _Linker(gof.link.LocalLinker): ...@@ -620,6 +620,7 @@ class _Linker(gof.link.LocalLinker):
except (NotImplementedError, utils.MethodNotDefined): except (NotImplementedError, utils.MethodNotDefined):
thunks_c.append(None) thunks_c.append(None)
if self.maker.mode.check_py_code:
p = node.op.perform p = node.op.perform
thunk = (lambda p = p, i = node_input_storage, o = node_output_storage, n = thunk = (lambda p = p, i = node_input_storage, o = node_output_storage, n =
node: p(n, [x[0] for x in i], o)) node: p(n, [x[0] for x in i], o))
...@@ -627,6 +628,8 @@ class _Linker(gof.link.LocalLinker): ...@@ -627,6 +628,8 @@ class _Linker(gof.link.LocalLinker):
thunk.outputs = node_output_storage thunk.outputs = node_output_storage
thunk.perform = p thunk.perform = p
thunks_py.append(thunk) thunks_py.append(thunk)
else:
thunks_py.append(None)
if no_recycling is True: if no_recycling is True:
no_recycling = storage_map.values() no_recycling = storage_map.values()
...@@ -675,6 +678,7 @@ class _Linker(gof.link.LocalLinker): ...@@ -675,6 +678,7 @@ class _Linker(gof.link.LocalLinker):
this_node_destroyed_variables = set() this_node_destroyed_variables = set()
# put a copy of each input into the storage_map # put a copy of each input into the storage_map
# also, check that inputs have valid values
for r in node.inputs: for r in node.inputs:
assert isinstance(r, gof.Variable) assert isinstance(r, gof.Variable)
assert r in r_vals assert r in r_vals
...@@ -682,20 +686,22 @@ class _Linker(gof.link.LocalLinker): ...@@ -682,20 +686,22 @@ class _Linker(gof.link.LocalLinker):
if not r.type.is_valid_value(storage_map[r][0]): if not r.type.is_valid_value(storage_map[r][0]):
raise InvalidValueError(r, storage_map[r][0]) raise InvalidValueError(r, storage_map[r][0])
if thunk_py:
thunk_py() thunk_py()
_check_inputs(node, storage_map, r_vals, dr_vals, active_order_set, _check_inputs(node, storage_map, r_vals, dr_vals, active_order_set,
clobber_dr_vals=True) clobber_dr_vals=True)
# check output values for type-correctness
#retrieve each output from the storage_map #retrieve each output from the storage_map
for r in node.outputs: for r in node.outputs:
if not r.type.is_valid_value(storage_map[r][0]): if not r.type.is_valid_value(storage_map[r][0]):
raise InvalidValueError(r, storage_map[r][0]) raise InvalidValueError(r, storage_map[r][0])
if r in r_vals: #if r in r_vals:
print >> sys.stderr, 'OUTPUT', r, 'ALREADY HAS_VALUE!', r_vals[r], 'WHAT ABOUT', storage_map[r][0] #print >> sys.stderr, 'OUTPUT', r, 'ALREADY HAS_VALUE!', r_vals[r], 'WHAT ABOUT', storage_map[r][0]
assert r not in r_vals assert r not in r_vals
r_vals[r] = storage_map[r][0] r_vals[r] = storage_map[r][0]
storage_map[r][0] = None #clear the storage_map for the thunk_c storage_map[r][0] = None #clear the storage_map of outputs for the thunk_c
if thunk_c: if thunk_c:
...@@ -709,12 +715,18 @@ class _Linker(gof.link.LocalLinker): ...@@ -709,12 +715,18 @@ class _Linker(gof.link.LocalLinker):
clobber_dr_vals=False) clobber_dr_vals=False)
for r in node.outputs: for r in node.outputs:
# check output values for type-correctness
if not r.type.is_valid_value(storage_map[r][0]): if not r.type.is_valid_value(storage_map[r][0]):
raise InvalidValueError(r, storage_map[r][0]) raise InvalidValueError(r, storage_map[r][0])
if r in r_vals:
# compares the version from thunk_py (in r_vals) # compares the version from thunk_py (in r_vals)
# to the version produced by thunk_c (in storage_map) # to the version produced by thunk_c (in storage_map)
if not r.type.values_eq_approx(r_vals[r], storage_map[r][0]): if not r.type.values_eq_approx(r_vals[r], storage_map[r][0]):
raise BadClinkerOutput(r, val_py=r_vals[r], val_c=storage_map[r][0]) raise BadClinkerOutput(r, val_py=r_vals[r], val_c=storage_map[r][0])
else:
#retrieve each output from the storage_map
r_vals[r] = storage_map[r][0]
storage_map[r][0] = None #clear the storage_map for the thunk_c storage_map[r][0] = None #clear the storage_map for the thunk_c
# we're done with this thunk # we're done with this thunk
...@@ -1013,14 +1025,18 @@ class DebugMode(Mode): ...@@ -1013,14 +1025,18 @@ class DebugMode(Mode):
optimizer='fast_run', optimizer='fast_run',
stability_patience=10, stability_patience=10,
check_c_code=True, check_c_code=True,
check_py_code=True,
diagnostic=sys.stderr): diagnostic=sys.stderr):
"""Initialize member variables """Initialize member variables
""" """
if not (check_c_code or check_py_code):
raise ValueError('DebugMode has to check at least one of c and py code')
super(DebugMode, self).__init__( super(DebugMode, self).__init__(
optimizer=optimizer, optimizer=optimizer,
linker=_Linker) linker=_Linker)
self.stability_patience = stability_patience self.stability_patience = stability_patience
self.check_c_code = check_c_code self.check_c_code = check_c_code
self.check_py_code = check_py_code
self.diagnostic = diagnostic self.diagnostic = diagnostic
register_mode('DEBUG_MODE',DebugMode(optimizer='fast_run')) register_mode('DEBUG_MODE',DebugMode(optimizer='fast_run'))
...@@ -149,6 +149,95 @@ inconsistent = BROKEN_ON_PURPOSE_StructuredDotCSC(False) ...@@ -149,6 +149,95 @@ inconsistent = BROKEN_ON_PURPOSE_StructuredDotCSC(False)
# off_by_half is a good op, that is different from theano.sparse.sd_csc # off_by_half is a good op, that is different from theano.sparse.sd_csc
off_by_half = BROKEN_ON_PURPOSE_StructuredDotCSC(True) off_by_half = BROKEN_ON_PURPOSE_StructuredDotCSC(True)
class WeirdBrokenOp(gof.Op):
"""
This op can be inplace if behaviour is times1_inplace
This op can be destructive if behaviour is times2_inplace
In both cases, it does not set the destroy_map or view_map correctly so it should raise an
error in DebugMode.
"""
def __init__(self, behaviour):
gof.Op.__init__(self)
self.behaviour = behaviour
def __eq__(self, other):
return type(self) == type(other) and (self.behaviour == other.behaviour)
def __hash__(self):
return hash(type(self)) ^ hash(self.behaviour)
def make_node(self, a):
a_ = theano.tensor.as_tensor_variable(a)
r = gof.Apply(self, [a_], [a_.type()])
return r
def dontuse_perform(self, node, (a,), (out,)):
if self.behaviour == 'times2':
out[0] = a * 2
elif self.behaviour == 'times2_inplace':
out[0] = a
out[0] *= 2
elif self.behaviour == 'times1':
out[0] = a * 1
elif self.behaviour == 'times1_inplace':
out[0] = a
else:
raise ValueError(self.behaviour)
def c_code(self, node, name, (a,), (z,), sub):
if "inplace" in self.behaviour:
z_code = """
if (%(z)s) Py_DECREF(%(z)s);
Py_INCREF(%(a)s);
%(z)s = %(a)s;
"""
else:
z_code = """
if (%(z)s) Py_DECREF(%(z)s);
%(z)s = (PyArrayObject*) PyArray_SimpleNew(1, %(a)s->dimensions, %(a)s->descr->type_num);
"""
prep_vars = """
//the output array has size M x N
npy_intp M = %(a)s->dimensions[0];
npy_intp Sa = %(a)s->strides[0] / %(a)s->descr->elsize;
npy_intp Sz = %(z)s->strides[0] / %(z)s->descr->elsize;
npy_double * Da = (npy_double*)%(a)s->data;
npy_double * Dz = (npy_double*)%(z)s->data;
//clear the output array
for (npy_intp m = 0; m < M; ++m)
{
"""
if self.behaviour == 'times2':
behaviour = " Dz[m * Sz] = 2 * Da[m * Sa]; "
#out[0] = a * 2
elif self.behaviour == 'times2_inplace':
#out[0] = a
#out[0] *= 2
behaviour = " Dz[m * Sz] = 2 * Da[m * Sa]; "
elif self.behaviour == 'times1':
#out[0] = a * 1
behaviour = " Dz[m * Sz] = Da[m * Sa]; "
elif self.behaviour == 'times1_inplace':
#out[0] = a
behaviour = ""
else:
raise ValueError(self.behaviour)
prep_vars2 = """
}
"""
total = (z_code + prep_vars + behaviour + prep_vars2)% dict(locals(), **sub)
return total
wb2i = WeirdBrokenOp('times2_inplace')
wb2 = WeirdBrokenOp('times2')
wb1i = WeirdBrokenOp('times1_inplace')
wb1 = WeirdBrokenOp('times1')
def test_badclinkeroutput(): def test_badclinkeroutput():
...@@ -219,6 +308,10 @@ def test_badoptimization(): ...@@ -219,6 +308,10 @@ def test_badoptimization():
assert False assert False
def test_just_c_code():
x = theano.tensor.dvector()
f = theano.function([x], wb2(x), mode=debugmode.DebugMode(check_py_code=False))
assert numpy.all(f([1,2]) == [2, 4])
def test_baddestroymap(): def test_baddestroymap():
class BadAdd(gof.Op): class BadAdd(gof.Op):
...@@ -239,6 +332,16 @@ def test_baddestroymap(): ...@@ -239,6 +332,16 @@ def test_baddestroymap():
except debugmode.BadDestroyMap: except debugmode.BadDestroyMap:
return return
def test_baddestroymap_c():
x = theano.tensor.dvector()
f = theano.function([x], wb2i(x), mode=debugmode.DebugMode(check_py_code=False))
try:
assert numpy.all(f([1,2]) == [2, 4])
assert False #failed to raise error
except debugmode.BadDestroyMap:
pass
def test_badviewmap(): def test_badviewmap():
class BadAdd(gof.Op): class BadAdd(gof.Op):
def make_node(self, a, b): def make_node(self, a, b):
...@@ -256,3 +359,12 @@ def test_badviewmap(): ...@@ -256,3 +359,12 @@ def test_badviewmap():
assert False #failed to raise error assert False #failed to raise error
except debugmode.BadViewMap: except debugmode.BadViewMap:
return return
def test_badviewmap_c():
x = theano.tensor.dvector()
f = theano.function([x], wb1i(x), mode=debugmode.DebugMode(check_py_code=False))
try:
f([1,2])
assert False #failed to raise error
except debugmode.BadDestroyMap:
pass
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论