提交 ce1eeab9 authored 作者: abergeron's avatar abergeron

Merge pull request #1797 from nouiz/fast_opt

Make the slow scan test fast!
......@@ -1077,6 +1077,7 @@ class FunctionMaker(object):
self.mode = mode
self.accept_inplace = accept_inplace
self.function_builder = function_builder
self.on_unused_input = on_unused_input # Used only for the pickling
self.required = [(i.value is None) for i in self.inputs]
self.refeed = [
......@@ -1215,6 +1216,7 @@ def _pickle_FunctionMaker(self):
accept_inplace=self.accept_inplace,
function_builder=self.function_builder,
profile=self.profile,
on_unused_input=self.on_unused_input,
)
return (_constructor_FunctionMaker, (kwargs,))
......
......@@ -508,13 +508,31 @@ class EmptyConstantError(NotScalarConstantError):
"""
def numpy_scalar(data):
""" Return a scalar stored in a numpy ndarray, or raise
NotScalarConstantError if the numpy ndarray is not a scalar
"""
# handle case where data is numpy.array([])
if data.ndim > 0 and (len(data.shape) == 0 or
__builtins__['max'](data.shape) == 0):
assert numpy.all(numpy.array([]) == data)
raise EmptyConstantError()
try:
numpy.complex(data) # works for all numeric scalars
return data
except Exception:
raise NotScalarConstantError(
'v.data is non-numeric, non-scalar, or has more than one'
' unique value', data)
get_scalar_constant_value_elemwises = (
scal.Cast, scal.Switch,
scal.NEQ, scal.EQ,
scal.LT, scal.GT, scal.LE, scal.GE,
scal.Sub, scal.Add, scal.Mod, scal.Mul,
scal.IntDiv, scal.TrueDiv)
def get_scalar_constant_value(v):
def get_scalar_constant_value(orig_v, elemwise=True):
"""return the constant scalar(0-D) value underlying variable `v`
If v is the output of dimshuffles, fills, allocs, rebroadcasts, cast
......@@ -523,10 +541,14 @@ def get_scalar_constant_value(v):
If `v` is not some view of constant scalar data, then raise a
NotScalarConstantError.
:param elemwise: If False, we won't try to go into elemwise.
So this call is faster.
:note: There may be another function similar to this one in the
code, but I'm not sure where it is.
"""
v = orig_v
while True:
if v is None:
# None is not a scalar (and many uses of this function seem to depend
# on passing it None)
......@@ -535,24 +557,6 @@ def get_scalar_constant_value(v):
if isinstance(v, (numpy.integer, int, float)):
return numpy.asarray(v)
def numpy_scalar(data):
""" Return a scalar stored in a numpy ndarray, or raise
NotScalarConstantError if the numpy ndarray is not a scalar
"""
# handle case where data is numpy.array([])
if data.ndim > 0 and (len(data.shape) == 0 or
__builtins__['max'](data.shape) == 0):
assert numpy.all(numpy.array([]) == data)
raise EmptyConstantError()
try:
numpy.complex(data) # works for all numeric scalars
return data
except Exception:
raise NotScalarConstantError(
'v.data is non-numeric, non-scalar, or has more than one'
' unique value', data)
if isinstance(v, numpy.ndarray):
return numpy_scalar(v)
......@@ -567,9 +571,10 @@ def get_scalar_constant_value(v):
if isinstance(v.owner.op, (Alloc, DimShuffle, Rebroadcast,
compile.ops.OutputGuard,
compile.DeepCopyOp)):
return get_scalar_constant_value(v.owner.inputs[0])
elif (isinstance(v.owner.op, theano.compile.ops.Shape_i) and
isinstance(v.owner.inputs[0], Constant)):
v = v.owner.inputs[0]
continue
elif isinstance(v.owner.op, theano.compile.ops.Shape_i):
if isinstance(v.owner.inputs[0], Constant):
return v.owner.inputs[0].data.shape[v.owner.op.i]
# Don't act as the constant_folding optimization here as this
# fct is used too early in the optimization phase. This would
......@@ -580,18 +585,20 @@ def get_scalar_constant_value(v):
if isinstance(v.owner.op, scal.Second):
# We don't need both input to be constant for second
shape, val = v.owner.inputs
return get_scalar_constant_value(val)
v = val
continue
if isinstance(v.owner.op, get_scalar_constant_value_elemwises):
const = [get_scalar_constant_value(i)
for i in v.owner.inputs]
ret = [[None]]
v.owner.op.perform(v.owner, const, ret)
return ret[0][0]
elif isinstance(v.owner.op, Elemwise):
elif elemwise and isinstance(v.owner.op, Elemwise):
if isinstance(v.owner.op.scalar_op, scal.Second):
# We don't need both input to be constant for second
shape, val = v.owner.inputs
return get_scalar_constant_value(val)
v = val
continue
elif isinstance(v.owner.op.scalar_op,
get_scalar_constant_value_elemwises):
const = [get_scalar_constant_value(i) for i in v.owner.inputs]
......@@ -3075,7 +3082,7 @@ pprint.assign(pow, printing.OperatorPrinter('**', 1, 'right'))
##########################
def extract_constant(x):
def extract_constant(x, elemwise=True):
'''
This function is basically a call to tensor.get_scalar_constant_value. The
main difference is the behaviour in case of failure. While
......@@ -3085,7 +3092,7 @@ def extract_constant(x):
ScalarVariable, we convert it to a tensor with tensor_from_scalar.
'''
try:
x = get_scalar_constant_value(x)
x = get_scalar_constant_value(x, elemwise=elemwise)
except NotScalarConstantError:
pass
if (isinstance(x, scal.ScalarVariable) or
......
......@@ -1581,7 +1581,7 @@ def local_upcast_elemwise_constant_inputs(node):
else:
try:
# works only for scalars
cval_i = get_scalar_constant_value(i)
cval_i = get_scalar_constant_value(i, elemwise=False)
if all(i.broadcastable):
new_inputs.append(T.shape_padleft(
T.cast(cval_i, output_dtype),
......@@ -2327,7 +2327,7 @@ def local_remove_switch_const_cond(node):
"""
if (isinstance(node.op, T.Elemwise) and
isinstance(node.op.scalar_op, scalar.basic.Switch)):
cond = T.extract_constant(node.inputs[0])
cond = T.extract_constant(node.inputs[0], elemwise=False)
if type(cond) is numpy.ndarray and cond.ndim == 0:
if cond == 0:
out = node.inputs[2]
......@@ -2377,7 +2377,8 @@ def local_mul_switch_sink(node):
if i.owner and i.owner.op == T.switch:
switch = i.owner
try:
if get_scalar_constant_value(switch.inputs[1]) == 0.:
if (isinstance(switch.inputs[0], Constant) and
get_scalar_constant_value(switch.inputs[1]) == 0.):
listmul = node.inputs[:idx] + node.inputs[idx + 1:]
fct = [T.switch(switch.inputs[0], 0,
T.mul(*(listmul + [switch.inputs[2]])))]
......@@ -2387,7 +2388,8 @@ def local_mul_switch_sink(node):
except NotScalarConstantError:
pass
try:
if get_scalar_constant_value(switch.inputs[2]) == 0.:
if (isinstance(switch.inputs[2], Constant) and
get_scalar_constant_value(switch.inputs[2]) == 0.):
listmul = node.inputs[:idx] + node.inputs[idx + 1:]
fct = [T.switch(switch.inputs[0],
T.mul(*(listmul + [switch.inputs[1]])), 0)]
......@@ -3784,7 +3786,7 @@ def local_abs_merge(node):
for i in node.inputs:
if i.owner and i.owner.op == T.abs_:
inputs.append(i.owner.inputs[0])
else:
elif isinstance(i, Constant):
try:
const = get_scalar_constant_value(i)
except NotScalarConstantError:
......@@ -3792,6 +3794,8 @@ def local_abs_merge(node):
if not (const >= 0).all():
return False
inputs.append(i)
else:
return False
return [T.abs_(T.mul(*inputs))]
if node.op == T.true_div and sum([i.owner.op == T.abs_ for i in
node.inputs if i.owner]) == 2:
......
......@@ -1495,11 +1495,11 @@ def test_log1p():
f = function([x], T.log(1 + (x)), mode=m)
assert [node.op for node in f.maker.fgraph.toposort()] == [T.log1p]
f = function([x], T.log(1 + (-x)), mode=m)
assert [node.op for node in f.maker.fgraph.toposort()] == [T.neg,
inplace.log1p_inplace]
assert [node.op for node in f.maker.fgraph.toposort()] == [
T.neg, inplace.log1p_inplace]
f = function([x], -T.log(1 + (-x)), mode=m)
assert [node.op for node in f.maker.fgraph.toposort()] == [T.neg,
inplace.log1p_inplace, inplace.neg_inplace]
assert [node.op for node in f.maker.fgraph.toposort()] == [
T.neg, inplace.log1p_inplace, inplace.neg_inplace]
# check trickier cases (and use different dtype)
y = fmatrix()
......@@ -1507,12 +1507,12 @@ def test_log1p():
print f.maker.fgraph.toposort()
# the first three ops are Shape_i, Shape_i, and Dimshuffle
theano.printing.debugprint(f)
assert [node.op for node in f.maker.fgraph.toposort()][3:] \
== [T.log1p, tensor.alloc]
assert [node.op for node in f.maker.fgraph.toposort()][3:] == [
T.log1p, tensor.alloc]
f = function([x, y], T.log(0 + (x) + tensor.fill(y, 1.0)), mode=m)
theano.printing.debugprint(f)
assert [node.op for node in f.maker.fgraph.toposort()][3:] \
== [T.log1p, tensor.alloc]
assert [node.op for node in f.maker.fgraph.toposort()][3:] == [
T.log1p, tensor.alloc]
f = function([x, y], T.log(2 + (x) - tensor.fill(y, 1.0)), mode=m)
theano.printing.debugprint(f)
assert [node.op for node in f.maker.fgraph.toposort()][3:] \
......@@ -2333,7 +2333,8 @@ class Test_alloc_zero(unittest.TestCase):
def setUp(self):
mode = theano.compile.mode.get_default_mode()
self.mode = mode.including("local_incsubtensor_of_allocs",
"local_setsubtensor_of_allocs", "local_0_dot_x")
"local_setsubtensor_of_allocs",
"local_0_dot_x")
def test_setsubtensor_allocs0(self):
x = tensor.matrix()
......@@ -2427,7 +2428,7 @@ class Test_alloc_zero(unittest.TestCase):
f(_e1[1], _e2[1])
f(_e1[2], _e2[2])
assert numpy.all([not isinstance(x.op, tensor.Dot) for x in
f.maker.fgraph.toposort() ])
f.maker.fgraph.toposort()])
#test that we don't remove shape errors
self.assertRaises((ValueError, AssertionError), f,
......@@ -2809,8 +2810,8 @@ class test_assert(utt.InferShapeTester):
x = T.scalar()
y = T.scalar()
f = theano.function([x, y], theano.tensor.opt.assert_(x, y,
1), mode=mode)
f = theano.function([x, y], theano.tensor.opt.assert_(x, y, 1),
mode=mode)
assert f(1, 1) == 1
assert f(5, 1) == 5
topo = f.maker.fgraph.toposort()
......@@ -2827,8 +2828,8 @@ class test_assert(utt.InferShapeTester):
x = T.scalar()
y = T.scalar()
f = theano.function([x, y], theano.tensor.opt.assert_(x, y,
0), mode=mode)
f = theano.function([x, y], theano.tensor.opt.assert_(x, y, 0),
mode=mode)
self.assertRaises(AssertionError, f, 1, 0)
topo = f.maker.fgraph.toposort()
assert len(topo) == 2
......@@ -3177,8 +3178,9 @@ def test_constant_get_stabilized():
class T_local_switch_sink(unittest.TestCase):
def setUp(self):
# condition values
self.condm = numpy.asarray([[0.1, 0, 1, -1], [0., 0., 0.,
0.], [1, 1, 1, 1]])
self.condm = numpy.asarray([[0.1, 0, 1, -1],
[0., 0., 0., 0.],
[1, 1, 1, 1]])
self.condv = numpy.asarray([0.1, 0, 1, -1])
self.conds = [0.1, 0, 1, -1]
......@@ -3256,14 +3258,14 @@ class T_local_erf(unittest.TestCase):
f = theano.function([x], 1 + T.erf(x), mode=self.mode)
print f.maker.fgraph.toposort()
assert [n.op for n in f.maker.fgraph.toposort()] == [T.mul, T.
erfc], f.maker.fgraph.toposort()
assert [n.op for n in f.maker.fgraph.toposort()] == [
T.mul, T.erfc], f.maker.fgraph.toposort()
f(val)
f = theano.function([x], T.erf(x) + 1, mode=self.mode)
print f.maker.fgraph.toposort()
assert [n.op for n in f.maker.fgraph.toposort()] == [T.mul, T.
erfc], f.maker.fgraph.toposort()
assert [n.op for n in f.maker.fgraph.toposort()] == [
T.mul, T.erfc], f.maker.fgraph.toposort()
f(val)
f = theano.function([x], T.erf(x) + 2, mode=self.mode)
......@@ -3305,7 +3307,7 @@ class T_local_erf(unittest.TestCase):
assert topo[0].op == T.erf, f.maker.fgraph.toposort()
assert isinstance(topo[1].op, T.Elemwise), f.maker.fgraph.toposort()
assert isinstance(topo[1].op.scalar_op, scal.Add)\
or isinstance(topo[1].op.scalar_op,scal.Sub), f.maker.fgraph.toposort()
or isinstance(topo[1].op.scalar_op, scal.Sub), f.maker.fgraph.toposort()
print f(val)
def test_local_erf_minus_one(self):
......@@ -3345,7 +3347,8 @@ class T_local_erfc(unittest.TestCase):
'canonicalize').including('fast_run').excluding('gpu')
self.mode = self.mode_fusion.excluding('fusion')
self.mode._optimizer.position_cutoff = 1.50001
if theano.config.cxx == '' and not theano.scalar.basic_scipy.imported_scipy_special:
if (theano.config.cxx == '' and
not theano.scalar.basic_scipy.imported_scipy_special):
raise SkipTest("erfc need a c++ compiler or scipy")
def test_local_one_minus_erfc(self):
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论