提交 5b5b87b2 authored 作者: Brandon T. Willard's avatar Brandon T. Willard

Use np.shape in Shape/Shape_i Python implementations

This change allows `Shape`/`Shape_i` to handle non-standard `Op` values, like the `None` and `list` values produced by `NoneType` and `MakeList` `Op`s.
上级 5bc0592c
...@@ -5,8 +5,9 @@ import pytest ...@@ -5,8 +5,9 @@ import pytest
import theano import theano
from tests import unittest_tools as utt from tests import unittest_tools as utt
from theano import config, function from theano import change_flags, config, function
from theano.compile.ops import Rebroadcast, SpecifyShape, as_op from theano.compile.ops import Rebroadcast, SpecifyShape, as_op, shape, shape_i
from theano.gof.fg import FunctionGraph
from theano.tensor.basic import ( from theano.tensor.basic import (
TensorType, TensorType,
dmatrix, dmatrix,
...@@ -14,8 +15,13 @@ from theano.tensor.basic import ( ...@@ -14,8 +15,13 @@ from theano.tensor.basic import (
dvector, dvector,
ivector, ivector,
matrix, matrix,
tensor3,
vector, vector,
) )
from theano.tensor.opt import ShapeFeature
from theano.tensor.subtensor import Subtensor
from theano.tensor.type_other import NoneConst
from theano.typed_list import make_list
@as_op([dmatrix, dmatrix], dmatrix) @as_op([dmatrix, dmatrix], dmatrix)
...@@ -202,7 +208,7 @@ class TestRebroadcast(utt.InferShapeTester): ...@@ -202,7 +208,7 @@ class TestRebroadcast(utt.InferShapeTester):
# Rebroadcast # Rebroadcast
adtens4 = dtensor4() adtens4 = dtensor4()
adict = [(0, False), (1, True), (2, False), (3, True)] adict = [(0, False), (1, True), (2, False), (3, True)]
adtens4_val = rng.rand(2, 1, 3, 1) adtens4_val = rng.rand(2, 1, 3, 1).astype(config.floatX)
self._compile_and_check( self._compile_and_check(
[adtens4], [adtens4],
[Rebroadcast(*adict)(adtens4)], [Rebroadcast(*adict)(adtens4)],
...@@ -213,10 +219,35 @@ class TestRebroadcast(utt.InferShapeTester): ...@@ -213,10 +219,35 @@ class TestRebroadcast(utt.InferShapeTester):
adtens4_bro = TensorType("float64", (True, True, True, False))() adtens4_bro = TensorType("float64", (True, True, True, False))()
bdict = [(0, True), (1, False), (2, False), (3, False)] bdict = [(0, True), (1, False), (2, False), (3, False)]
adtens4_bro_val = rng.rand(1, 1, 1, 3) adtens4_bro_val = rng.rand(1, 1, 1, 3).astype(config.floatX)
self._compile_and_check( self._compile_and_check(
[adtens4_bro], [adtens4_bro],
[Rebroadcast(*bdict)(adtens4_bro)], [Rebroadcast(*bdict)(adtens4_bro)],
[adtens4_bro_val], [adtens4_bro_val],
Rebroadcast, Rebroadcast,
) )
@change_flags(compute_test_value="raise")
def test_nonstandard_shapes():
a = tensor3(config.floatX)
a.tag.test_value = np.random.random((2, 3, 4)).astype(config.floatX)
b = tensor3(theano.config.floatX)
b.tag.test_value = np.random.random((2, 3, 4)).astype(config.floatX)
tl = make_list([a, b])
tl_shape = shape(tl)
assert np.array_equal(tl_shape.get_test_value(), (2, 2, 3, 4))
# There's no `FunctionGraph`, so it should return a `Subtensor`
tl_shape_i = shape_i(tl, 0)
assert isinstance(tl_shape_i.owner.op, Subtensor)
assert tl_shape_i.get_test_value() == 2
tl_fg = FunctionGraph([a, b], [tl], features=[ShapeFeature()])
tl_shape_i = shape_i(tl, 0, fgraph=tl_fg)
assert not isinstance(tl_shape_i.owner.op, Subtensor)
assert tl_shape_i.get_test_value() == 2
none_shape = shape(NoneConst)
assert np.array_equal(none_shape.get_test_value(), [])
...@@ -260,7 +260,7 @@ class Shape(Op): ...@@ -260,7 +260,7 @@ class Shape(Op):
def perform(self, node, inp, out_): def perform(self, node, inp, out_):
(x,) = inp (x,) = inp
(out,) = out_ (out,) = out_
out[0] = theano._asarray(x.shape, dtype="int64") out[0] = theano._asarray(np.shape(x), dtype="int64")
def infer_shape(self, node, in_shapes): def infer_shape(self, node, in_shapes):
return [[len(in_shapes[0])]] return [[len(in_shapes[0])]]
...@@ -370,9 +370,6 @@ class Shape_i(Op): ...@@ -370,9 +370,6 @@ class Shape_i(Op):
return "%s{%i}" % (self.__class__.__name__, self.i) return "%s{%i}" % (self.__class__.__name__, self.i)
def make_node(self, x): def make_node(self, x):
# x could be one of a number of types
# the only thing we require is that the variable have a .ndim,
# and that the value have a .shape
if not isinstance(x, theano.Variable): if not isinstance(x, theano.Variable):
raise TypeError("x must be Variable with ndim attribute", x) raise TypeError("x must be Variable with ndim attribute", x)
if x.ndim <= self.i: if x.ndim <= self.i:
...@@ -383,9 +380,9 @@ class Shape_i(Op): ...@@ -383,9 +380,9 @@ class Shape_i(Op):
(x,) = inp (x,) = inp
(out,) = out_ (out,) = out_
if out[0] is None: if out[0] is None:
out[0] = theano._asarray(x.shape[self.i], dtype="int64") out[0] = theano._asarray(np.shape(x)[self.i], dtype="int64")
else: else:
out[0][...] = x.shape[self.i] out[0][...] = np.shape(x)[self.i]
def c_code_cache_version(self): def c_code_cache_version(self):
version = [] version = []
...@@ -489,7 +486,7 @@ def shape_i(var, i, fgraph=None): ...@@ -489,7 +486,7 @@ def shape_i(var, i, fgraph=None):
# If we are not able to use the shape feature, we should not put # If we are not able to use the shape feature, we should not put
# Shape_i in the graph. Otherwise, the shape feature optimization # Shape_i in the graph. Otherwise, the shape feature optimization
# won't get applied. # won't get applied.
return var.shape[i] return shape(var)[i]
def shape_i_op(i): def shape_i_op(i):
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论