提交 c4a3bd9f authored 作者: lamblin's avatar lamblin

Merge pull request #1361 from nouiz/get_scalar_constant_value

Get scalar constant value
...@@ -32,7 +32,6 @@ you should check the strides and alignment. ...@@ -32,7 +32,6 @@ you should check the strides and alignment.
.. code-block:: python .. code-block:: python
class Fibby(theano.Op): class Fibby(theano.Op):
""" """
An arbitrarily generalized Fibbonacci sequence An arbitrarily generalized Fibbonacci sequence
""" """
...@@ -45,6 +44,7 @@ you should check the strides and alignment. ...@@ -45,6 +44,7 @@ you should check the strides and alignment.
def make_node(self, x): def make_node(self, x):
x_ = tensor.as_tensor_variable(x) x_ = tensor.as_tensor_variable(x)
assert x_.ndim == 1
return theano.Apply(self, return theano.Apply(self,
inputs=[x_], inputs=[x_],
outputs=[x_.type()]) outputs=[x_.type()])
...@@ -53,7 +53,7 @@ you should check the strides and alignment. ...@@ -53,7 +53,7 @@ you should check the strides and alignment.
def perform(self, node, inputs, output_storage): def perform(self, node, inputs, output_storage):
x, = inputs x, = inputs
y = output_storage[0][0] = x.copy() y = output_storage[0][0] = x.copy()
for i in range(2,len(x)): for i in range(2, len(x)):
y[i] = y[i-1] * y[i-2] + x[i] y[i] = y[i-1] * y[i-2] + x[i]
def c_code(self, node, name, inames, onames, sub): def c_code(self, node, name, inames, onames, sub):
...@@ -64,13 +64,19 @@ you should check the strides and alignment. ...@@ -64,13 +64,19 @@ you should check the strides and alignment.
Py_XDECREF(%(y)s); Py_XDECREF(%(y)s);
%(y)s = (PyArrayObject*)PyArray_FromArray( %(y)s = (PyArrayObject*)PyArray_FromArray(
%(x)s, 0, NPY_ARRAY_ENSURECOPY); %(x)s, 0, NPY_ARRAY_ENSURECOPY);
if (!(%y)s) %(fail)s; if (!%(y)s)
dtype_%(y)s * y = (dtype_%(y)s*)%(y)s->data; %(fail)s;
dtype_%(x)s * x = (dtype_%(x)s*)%(x)s->data; {//New scope needed to make compilation work
for (int i = 2; i < %(x)s->dimensions[0]; ++i) dtype_%(y)s * y = (dtype_%(y)s*)%(y)s->data;
y[i] = y[i-1]*y[i-2] + x[i]; dtype_%(x)s * x = (dtype_%(x)s*)%(x)s->data;
for (int i = 2; i < %(x)s->dimensions[0]; ++i)
y[i] = y[i-1]*y[i-2] + x[i];
}
""" % locals() """ % locals()
def c_code_cache_version(self):
return (1,)
fibby = Fibby() fibby = Fibby()
At a high level, the code fragment declares a class (``Fibby``) and then At a high level, the code fragment declares a class (``Fibby``) and then
...@@ -208,7 +214,7 @@ TODO: talk about OPTIMIZATION STAGES ...@@ -208,7 +214,7 @@ TODO: talk about OPTIMIZATION STAGES
.. code-block:: python .. code-block:: python
from theano.tensor.opt import get_constant_value from theano.tensor.opt import get_scalar_constant_value, NotScalarConstantError
# Remove any fibby(zeros(...)) # Remove any fibby(zeros(...))
@theano.tensor.opt.register_specialize @theano.tensor.opt.register_specialize
...@@ -217,9 +223,9 @@ TODO: talk about OPTIMIZATION STAGES ...@@ -217,9 +223,9 @@ TODO: talk about OPTIMIZATION STAGES
if node.op == fibby: if node.op == fibby:
x = node.inputs[0] x = node.inputs[0]
try: try:
if numpy.all(0 == get_constant_value(x)): if numpy.all(0 == get_scalar_constant_value(x)):
return [x] return [x]
except TypeError: except NotScalarConstantError:
pass pass
The ``register_specialize`` decorator is what activates our optimization, and The ``register_specialize`` decorator is what activates our optimization, and
...@@ -232,3 +238,37 @@ argument for parameter ``node``. It tests using ...@@ -232,3 +238,37 @@ argument for parameter ``node``. It tests using
function ``get_constant_value``, which determines if a function ``get_constant_value``, which determines if a
Variable (``x``) is guaranteed to be a constant, and if so, what constant. Variable (``x``) is guaranteed to be a constant, and if so, what constant.
Test the optimization
=====================
Here is some code that test the optimization is applied only when needed.
.. code-block:: python
# Test it don't apply when not needed
x = T.dvector()
f = function([x], fibby(x))
#theano.printing.debugprint(f)
#We call the function to make sure it run.
#If you run in DebugMode, it will compare the C and Python output
f(numpy.random.rand(5))
topo = f.maker.fgraph.toposort()
assert len(topo) == 1
assert isinstance(topo[0].op, Fibby)
# Test that the optimization get applied
f_zero = function([], fibby(T.zeros([5])))
#theano.printing.debugprint(f_zero)
#If you run in DebugMode, it will compare the output before
# and after the optimization
f_zero()
#Check that the optimization remove the Fibby Op.
#For security, the Theano memory interface make that the output
#of the function is always memory not aliaced to the input.
#That is why there is a DeepCopyOp op.
topo = f_zero.maker.fgraph.toposort()
assert len(topo) == 1
assert isinstance(topo[0].op, theano.compile.ops.DeepCopyOp)
...@@ -899,7 +899,8 @@ class T_fibby(unittest.TestCase): ...@@ -899,7 +899,8 @@ class T_fibby(unittest.TestCase):
return hash(type(self)) return hash(type(self))
def make_node(self, x): def make_node(self, x):
x_ = tensor.as_tensor_variable(x) x_ = theano.tensor.as_tensor_variable(x)
assert x_.ndim == 1
return theano.Apply(self, return theano.Apply(self,
inputs=[x_], inputs=[x_],
outputs=[x_.type()]) outputs=[x_.type()])
...@@ -908,7 +909,7 @@ class T_fibby(unittest.TestCase): ...@@ -908,7 +909,7 @@ class T_fibby(unittest.TestCase):
def perform(self, node, inputs, output_storage): def perform(self, node, inputs, output_storage):
x, = inputs x, = inputs
y = output_storage[0][0] = x.copy() y = output_storage[0][0] = x.copy()
for i in range(2,len(x)): for i in range(2, len(x)):
y[i] = y[i-1] * y[i-2] + x[i] y[i] = y[i-1] * y[i-2] + x[i]
def c_code(self, node, name, inames, onames, sub): def c_code(self, node, name, inames, onames, sub):
...@@ -919,15 +920,23 @@ class T_fibby(unittest.TestCase): ...@@ -919,15 +920,23 @@ class T_fibby(unittest.TestCase):
Py_XDECREF(%(y)s); Py_XDECREF(%(y)s);
%(y)s = (PyArrayObject*)PyArray_FromArray( %(y)s = (PyArrayObject*)PyArray_FromArray(
%(x)s, 0, NPY_ARRAY_ENSURECOPY); %(x)s, 0, NPY_ARRAY_ENSURECOPY);
if (!(%y)s) %(fail)s; if (!%(y)s)
dtype_%(y)s * y = (dtype_%(y)s*)%(y)s->data; %(fail)s;
dtype_%(x)s * x = (dtype_%(x)s*)%(x)s->data; {//New scope needed to make compilation work
for (int i = 2; i < %(x)s->dimensions[0]; ++i) dtype_%(y)s * y = (dtype_%(y)s*)%(y)s->data;
y[i] = y[i-1]*y[i-2] + x[i]; dtype_%(x)s * x = (dtype_%(x)s*)%(x)s->data;
for (int i = 2; i < %(x)s->dimensions[0]; ++i)
y[i] = y[i-1]*y[i-2] + x[i];
}
""" % locals() """ % locals()
def c_code_cache_version(self):
return (1,)
fibby = Fibby() fibby = Fibby()
from theano.tensor.opt import (get_scalar_constant_value,
NotScalarConstantError)
# Remove any fibby(zeros(...)) # Remove any fibby(zeros(...))
@theano.tensor.opt.register_specialize @theano.tensor.opt.register_specialize
...@@ -938,9 +947,36 @@ class T_fibby(unittest.TestCase): ...@@ -938,9 +947,36 @@ class T_fibby(unittest.TestCase):
try: try:
if numpy.all(0 == get_scalar_constant_value(x)): if numpy.all(0 == get_scalar_constant_value(x)):
return [x] return [x]
except TypeError: except NotScalarConstantError:
pass pass
# Test it don't apply when not needed
x = T.dvector()
f = function([x], fibby(x))
#theano.printing.debugprint(f)
#We call the function to make sure it run.
#If you run in DebugMode, it will compare the C and Python output
f(numpy.random.rand(5))
topo = f.maker.fgraph.toposort()
assert len(topo) == 1
assert isinstance(topo[0].op, Fibby)
# Test that the optimization get applied
f_zero = function([], fibby(T.zeros([5])))
#theano.printing.debugprint(f_zero)
#If you run in DebugMode, it will compare the output before
# and after the optimization
f_zero()
#Check that the optimization remove the Fibby Op.
#For security, the Theano memory interface make that the output
#of the function is always memory not aliaced to the input.
#That is why there is a DeepCopyOp op.
topo = f_zero.maker.fgraph.toposort()
assert len(topo) == 1
assert isinstance(topo[0].op, theano.compile.ops.DeepCopyOp)
class T_graphstructures(unittest.TestCase): class T_graphstructures(unittest.TestCase):
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论