提交 c3d3ea87 authored 作者: Frederic's avatar Frederic

Move SpecifyShape

上级 e37988e5
...@@ -3,7 +3,8 @@ from theano.compile.ops import ( ...@@ -3,7 +3,8 @@ from theano.compile.ops import (
Shape, shape, register_shape_c_code, Shape, shape, register_shape_c_code,
Shape_i, register_shape_i_c_code, Shape_i, register_shape_i_c_code,
ViewOp, view_op, register_view_op_c_code, FromFunctionOp, ViewOp, view_op, register_view_op_c_code, FromFunctionOp,
as_op, Rebroadcast, register_rebroadcast_c_code) as_op, Rebroadcast, register_rebroadcast_c_code,
SpecifyShape, specify_shape)
from theano.compile.function_module import * from theano.compile.function_module import *
......
...@@ -250,7 +250,7 @@ class Shape(gof.Op): ...@@ -250,7 +250,7 @@ class Shape(gof.Op):
# the elements of the tensor variable do not participate # the elements of the tensor variable do not participate
# in the computation of the shape, so they are not really # in the computation of the shape, so they are not really
# part of the graph # part of the graph
return [DisconnectedType()()] return [theano.gradient.DisconnectedType()()]
def R_op(self, inputs, eval_points): def R_op(self, inputs, eval_points):
return [None] return [None]
...@@ -611,3 +611,126 @@ class Rebroadcast(gof.Op): ...@@ -611,3 +611,126 @@ class Rebroadcast(gof.Op):
version.append((str(t), v)) version.append((str(t), v))
return tuple(version) return tuple(version)
class SpecifyShape(gof.Op):
"""
L{Op} that puts into the graph the user-provided shape.
In the case where this op stays in the final graph, we assert the shape.
For this the output of this op must be used in the graph. This is not
the case most of the time if we only take the shape of the output.
Maybe there are other optimizations that will mess with this.
@note: Maybe in the future we will never do the assert!
@note: We currently don't support specifying partial shape information.
@todo: test this op with sparse and cuda ndarray.
Do C code for them too.
"""
view_map = {0: [0]}
def __hash__(self):
return hash(type(self))
def __eq__(self, other):
return type(self) == type(other)
def __str__(self):
return self.__class__.__name__
def make_node(self, x, shape):
if not isinstance(x, gof.Variable):
x = theano.tensor.as_tensor_variable(x)
shape = theano.tensor.as_tensor_variable(shape)
assert shape.ndim == 1
assert "int" in shape.dtype
if isinstance(shape, theano.tensor.TensorConstant):
assert shape.data.size == x.ndim
return gof.Apply(self, [x, shape], [x.type()])
def perform(self, node, inp, out_):
x, shape = inp
out, = out_
assert x.ndim == shape.size
assert numpy.all(x.shape == shape), ("got shape", x.shape,
"expected", shape)
out[0] = x
def infer_shape(self, node, shapes):
xshape, sshape = shapes
new_shape = []
for dim in xrange(node.inputs[0].ndim):
try:
s = theano.tensor.get_scalar_constant_value(node.inputs[1][dim])
s = theano.tensor.as_tensor_variable(s)
new_shape.append(s)
except theano.tensor.NotScalarConstantError:
new_shape.append(node.inputs[1][dim])
assert len(new_shape) == len(xshape)
return [new_shape]
def connection_pattern(self, node):
return [[True], [False]]
def grad(self, inp, grads):
x, s = inp
gz, = grads
# Should I set an SpecifyShape on gz? I think so
# But I don't do it now as we need to make an optimization
# to remove that op from the graph to don't block other optimization
# Should I do an optimizer that will remove the SpecifyShape?
# I think Yes
return [gz, theano.gradient.DisconnectedType()()]
return [specify_shape(gz, s), theano.gradient.DisconnectedType()()]
def R_op(self, inputs, eval_points):
if eval_points[0] is None:
# It means that the this op sits on top of a non-differentiable
# path
return [None]
return self.make_node(eval_points[0], *inputs[1:]).outputs
def c_code(self, node, nodename, inp, out, sub):
if not isinstance(node.inputs[0], theano.tensor.TensorVariable):
# The C code below supports only Tensor. super.c_code
# will raise an exception to tell that there is no C code
# for the other cases.
return super(SpecifyShape, self).c_code(node, nodename,
inp, out, sub)
iname, shape = inp
oname, = out
fail = sub['fail']
return """
if (PyArray_NDIM(%(iname)s) != PyArray_DIMS(%(shape)s)[0]) {
PyErr_Format(PyExc_AssertionError,
"SpecifyShape: vector of shape has %%d elements,"
" but the input has %%d dimensions.",
PyArray_NDIM(%(iname)s),
PyArray_DIMS(%(shape)s)[0]);
%(fail)s;
}
for(int i = 0; i < PyArray_NDIM(%(iname)s); i++){
dtype_%(shape)s shp = ((dtype_%(shape)s*)PyArray_GETPTR1(%(shape)s,
i))[0];
if (PyArray_DIMS(%(iname)s)[i] != shp) {
PyErr_Format(PyExc_AssertionError,
"SpecifyShape: dim %%d of input has shape %%d,"
" expected %%d.",
i, PyArray_DIMS(%(iname)s)[i],
shp);
%(fail)s;
}
}
Py_XDECREF(%(oname)s);
%(oname)s = %(iname)s;
Py_XINCREF(%(oname)s);
""" % locals()
def c_code_cache_version(self):
return (1,)
specify_shape = SpecifyShape()
...@@ -25,7 +25,8 @@ from theano.gof.python25 import partial, any, all ...@@ -25,7 +25,8 @@ from theano.gof.python25 import partial, any, all
from theano.gof.utils import hashtype 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
from theano.compile import Rebroadcast, Shape, shape #For history #For history
from theano.compile import Rebroadcast, Shape, shape, SpecifyShape, specify_shape
# We use these exceptions as well. # We use these exceptions as well.
...@@ -1164,129 +1165,6 @@ def old_shape(a): ...@@ -1164,129 +1165,6 @@ def old_shape(a):
return va.type.shape return va.type.shape
class SpecifyShape(Op):
"""
L{Op} that puts into the graph the user-provided shape.
In the case where this op stays in the final graph, we assert the shape.
For this the output of this op must be used in the graph. This is not
the case most of the time if we only take the shape of the output.
Maybe there are other optimizations that will mess with this.
@note: Maybe in the future we will never do the assert!
@note: We currently don't support specifying partial shape information.
@todo: test this op with sparse and cuda ndarray.
Do C code for them too.
"""
view_map = {0: [0]}
def __hash__(self):
return hash(type(self))
def __eq__(self, other):
return type(self) == type(other)
def __str__(self):
return self.__class__.__name__
def make_node(self, x, shape):
if not isinstance(x, Variable):
x = as_tensor_variable(x)
shape = as_tensor_variable(shape)
assert shape.ndim == 1
assert "int" in shape.dtype
if isinstance(shape, TensorConstant):
assert shape.data.size == x.ndim
return Apply(self, [x, shape], [x.type()])
def perform(self, node, inp, out_):
x, shape = inp
out, = out_
assert x.ndim == shape.size
assert numpy.all(x.shape == shape), ("got shape", x.shape,
"expected", shape)
out[0] = x
def infer_shape(self, node, shapes):
xshape, sshape = shapes
new_shape = []
for dim in xrange(node.inputs[0].ndim):
try:
s = get_scalar_constant_value(node.inputs[1][dim])
s = as_tensor_variable(s)
new_shape.append(s)
except NotScalarConstantError:
new_shape.append(node.inputs[1][dim])
assert len(new_shape) == len(xshape)
return [new_shape]
def connection_pattern(self, node):
return [[True], [False]]
def grad(self, inp, grads):
x, s = inp
gz, = grads
# Should I set an SpecifyShape on gz? I think so
# But I don't do it now as we need to make an optimization
# to remove that op from the graph to don't block other optimization
# Should I do an optimizer that will remove the SpecifyShape?
# I think Yes
return [gz, DisconnectedType()()]
return [specify_shape(gz, s), DisconnectedType()()]
def R_op(self, inputs, eval_points):
if eval_points[0] is None:
# It means that the this op sits on top of a non-differentiable
# path
return [None]
return self.make_node(eval_points[0], *inputs[1:]).outputs
def c_code(self, node, nodename, inp, out, sub):
if not isinstance(node.inputs[0], TensorVariable):
# The C code below supports only Tensor. super.c_code
# will raise an exception to tell that there is no C code
# for the other cases.
return super(SpecifyShape, self).c_code(node, nodename,
inp, out, sub)
iname, shape = inp
oname, = out
fail = sub['fail']
return """
if (PyArray_NDIM(%(iname)s) != PyArray_DIMS(%(shape)s)[0]) {
PyErr_Format(PyExc_AssertionError,
"SpecifyShape: vector of shape has %%d elements,"
" but the input has %%d dimensions.",
PyArray_NDIM(%(iname)s),
PyArray_DIMS(%(shape)s)[0]);
%(fail)s;
}
for(int i = 0; i < PyArray_NDIM(%(iname)s); i++){
dtype_%(shape)s shp = ((dtype_%(shape)s*)PyArray_GETPTR1(%(shape)s,
i))[0];
if (PyArray_DIMS(%(iname)s)[i] != shp) {
PyErr_Format(PyExc_AssertionError,
"SpecifyShape: dim %%d of input has shape %%d,"
" expected %%d.",
i, PyArray_DIMS(%(iname)s)[i],
shp);
%(fail)s;
}
}
Py_XDECREF(%(oname)s);
%(oname)s = %(iname)s;
Py_XINCREF(%(oname)s);
""" % locals()
def c_code_cache_version(self):
return (1,)
specify_shape = SpecifyShape()
class MaxAndArgmax(Op): class MaxAndArgmax(Op):
"""Calculate the max and argmax over a given axis or over all axes. """Calculate the max and argmax over a given axis or over all axes.
""" """
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论