Skip to content
This repository was archived by the owner on Nov 17, 2025. It is now read-only.

Commit 9f56e41

Browse files
Remove redundant AdvancedBoolean*Subtensor classes
This change also fixes the `set_subtensor` boolean gradient bug in #105.
1 parent 9733a2a commit 9f56e41

8 files changed

Lines changed: 84 additions & 326 deletions

File tree

tests/gpuarray/test_subtensor.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
GpuSubtensor,
1212
GpuAdvancedSubtensor1,
1313
GpuAdvancedSubtensor,
14-
GpuAdvancedBooleanSubtensor,
1514
GpuAdvancedIncSubtensor,
1615
GpuAdvancedIncSubtensor1,
1716
GpuAdvancedIncSubtensor1_dev20,
@@ -37,7 +36,6 @@ def shared(x, **kwargs):
3736
self.adv_sub1 = GpuAdvancedSubtensor1
3837
self.adv_incsub1 = GpuAdvancedIncSubtensor1
3938
self.adv_sub = GpuAdvancedSubtensor
40-
self.adv_bool_sub = GpuAdvancedBooleanSubtensor
4139
self.dimshuffle = GpuDimShuffle
4240
self.mode = mode_with_gpu
4341
# avoid errors with limited devices
@@ -60,7 +58,6 @@ def shared(x, **kwargs):
6058
self.adv_sub1 = GpuAdvancedSubtensor1
6159
self.adv_incsub1 = GpuAdvancedIncSubtensor1
6260
self.adv_sub = GpuAdvancedSubtensor
63-
self.adv_bool_sub = GpuAdvancedBooleanSubtensor
6461
self.dimshuffle = GpuDimShuffle
6562
self.mode = mode_with_gpu
6663
# avoid errors with limited devices

tests/tensor/test_subtensor.py

Lines changed: 40 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@
4646
AdvancedIncSubtensor1,
4747
AdvancedSubtensor,
4848
AdvancedSubtensor1,
49-
AdvancedBooleanSubtensor,
50-
AdvancedBooleanIncSubtensor,
5149
advanced_inc_subtensor,
5250
advanced_inc_subtensor1,
5351
advanced_set_subtensor,
@@ -67,8 +65,6 @@
6765
IncSubtensor,
6866
AdvancedSubtensor1,
6967
AdvancedIncSubtensor1,
70-
AdvancedBooleanSubtensor,
71-
AdvancedBooleanIncSubtensor,
7268
)
7369

7470

@@ -368,12 +364,10 @@ def numpy_inc_subtensor(x, idx, a):
368364

369365
# indexing with a mask for some dimensions
370366
mask = np.array([True, False])
371-
val = self.eval_output_and_check(
372-
test_array[mask], op_type=AdvancedBooleanSubtensor
373-
)
367+
val = self.eval_output_and_check(test_array[mask], op_type=AdvancedSubtensor)
374368
assert_array_equal(test_array_np[mask], val)
375369
val = self.eval_output_and_check(
376-
inc_subtensor(test_array[mask], 1), op_type=AdvancedBooleanIncSubtensor
370+
inc_subtensor(test_array[mask], 1), op_type=AdvancedIncSubtensor
377371
)
378372
assert_array_equal(numpy_inc_subtensor(test_array_np, mask, 1), val)
379373
assert_array_equal(
@@ -580,8 +574,8 @@ def test_grad_1d(self):
580574
topo_ = [node for node in topo if not isinstance(node.op, DeepCopyOp)]
581575
if not self.fast_compile:
582576
assert len(topo_) == 6
583-
assert np.sum([isinstance(node.op, IncSubtensor) for node in topo_]) == 1
584-
assert np.sum([isinstance(node.op, Subtensor) for node in topo_]) == 1
577+
assert any(isinstance(node.op, IncSubtensor) for node in topo_)
578+
assert any(isinstance(node.op, Subtensor) for node in topo_)
585579
gval = f()
586580

587581
good = np.zeros_like(data)
@@ -1161,47 +1155,6 @@ def test_shape_list(self):
11611155
val = f()
11621156
assert np.allclose(val, data[idx].shape)
11631157

1164-
def test_grad_advanced_inc_subtensor(self):
1165-
def inc_slice(*s):
1166-
def just_numeric_args(a, b):
1167-
cost = (a[s] + b).sum()
1168-
cost_wrt_a = tensor.grad(cost, a)
1169-
cost_wrt_b = tensor.grad(cost, b)
1170-
grads = cost_wrt_a.sum() + cost_wrt_b.sum()
1171-
return grads
1172-
1173-
return just_numeric_args
1174-
1175-
# vector
1176-
utt.verify_grad(
1177-
inc_slice(slice(2, 4, None)),
1178-
(
1179-
np.asarray([0, 1, 2, 3, 4, 5.0]),
1180-
np.asarray([9, 9.0]),
1181-
),
1182-
mode=self.mode,
1183-
)
1184-
1185-
# matrix
1186-
utt.verify_grad(
1187-
inc_slice(slice(1, 2, None), slice(None, None, None)),
1188-
(
1189-
np.asarray([[0, 1], [2, 3], [4, 5.0]]),
1190-
np.asarray([[9, 9.0]]),
1191-
),
1192-
mode=self.mode,
1193-
)
1194-
1195-
# single element
1196-
utt.verify_grad(
1197-
inc_slice(2, 1),
1198-
(
1199-
np.asarray([[0, 1], [2, 3], [4, 5.0]]),
1200-
np.asarray(9.0),
1201-
),
1202-
mode=self.mode,
1203-
)
1204-
12051158
def test_inc_and_set_subtensor(self):
12061159
# Test increment and set with broadcast
12071160

@@ -1323,21 +1276,6 @@ def test_advanced1_inc_and_set(self):
13231276
all_params.append(
13241277
(set_instead_of_inc, inplace, data_shape, inc_shape)
13251278
)
1326-
if False: # Enable for debugging purpose.
1327-
f = self.function(
1328-
[data_var, idx_var, inc_var],
1329-
output,
1330-
accept_inplace=inplace,
1331-
op=AdvancedIncSubtensor1,
1332-
)
1333-
if inplace:
1334-
# Ensure calling `f` will not alter `data_num`.
1335-
data_num = data_num.copy()
1336-
f_out = f(data_num.copy(), idx_num, inc_num)
1337-
assert np.allclose(f_out, data_copy)
1338-
if not inplace:
1339-
# Sanity check: `data_num` should be intact.
1340-
assert (data_num == data_num_init).all()
13411279

13421280
# Actual test (we compile a single Theano function to make it faster).
13431281
orig_warn = theano.config.warn.gpu_set_subtensor1
@@ -1647,18 +1585,18 @@ def check(idx, y_val, x_val, true):
16471585
rep[idx] += y_val
16481586
check(idx, y_val, x_val, rep)
16491587

1650-
def eval_output_and_check(self, t):
1588+
def eval_output_and_check(self, t, op):
16511589
f = inplace_func([], t, mode=self.mode)
16521590
topo = f.maker.fgraph.toposort()
16531591
topo_ = [node for node in topo if not isinstance(node.op, DeepCopyOp)]
16541592
assert len(topo_) == 1
1655-
assert isinstance(topo_[0].op, AdvancedSubtensor)
1593+
assert isinstance(topo_[0].op, op)
16561594
tval = f()
16571595
return tval
16581596

16591597
def test_cant_adv_idx_into_scalar(self):
16601598
with pytest.raises(IndexError):
1661-
(lambda: self.s[self.ix1])()
1599+
self.s[self.ix1]
16621600

16631601
def test_index_into_vec_w_vec(self):
16641602
a = self.v[self.ix1]
@@ -1698,7 +1636,7 @@ def test_index_w_int_and_vec(self):
16981636

16991637
assert isinstance(t.owner.op, AdvancedSubtensor)
17001638

1701-
val = self.eval_output_and_check(t)
1639+
val = self.eval_output_and_check(t, AdvancedSubtensor)
17021640
if isinstance(idx, list):
17031641
good = data[0, idx]
17041642
else:
@@ -1942,6 +1880,35 @@ def fun(x, y):
19421880
mode=self.mode,
19431881
)
19441882

1883+
# Test boolean gradients
1884+
def fun(x, y):
1885+
return advanced_inc_subtensor(
1886+
x, y, tensor.as_tensor(np.array([[True, False], [False, True]]))
1887+
)
1888+
1889+
utt.verify_grad(
1890+
fun,
1891+
[
1892+
np.random.rand(2, 2).astype(self.dtype),
1893+
np.random.rand(2).astype(self.dtype),
1894+
],
1895+
mode=self.mode,
1896+
)
1897+
1898+
def fun(x, y):
1899+
return advanced_set_subtensor(
1900+
x, y, tensor.as_tensor(np.array([[True, False], [False, True]]))
1901+
)
1902+
1903+
utt.verify_grad(
1904+
fun,
1905+
[
1906+
np.random.rand(2, 2).astype(self.dtype),
1907+
np.random.rand(2).astype(self.dtype),
1908+
],
1909+
mode=self.mode,
1910+
)
1911+
19451912

19461913
class TestInferShape(utt.InferShapeTester):
19471914
def test_IncSubtensor(self):
@@ -2216,7 +2183,7 @@ def test_AdvancedSubtensor(self):
22162183
AdvancedSubtensor,
22172184
)
22182185

2219-
def test_AdvancedBooleanSubtensor(self):
2186+
def test_AdvancedSubtensor_bool(self):
22202187
n = dmatrix()
22212188
n_val = np.arange(6).reshape((2, 3))
22222189

@@ -2225,14 +2192,14 @@ def test_AdvancedBooleanSubtensor(self):
22252192
[n],
22262193
[n[n[:, 0] > 2, n[0, :] > 2]],
22272194
[n_val],
2228-
AdvancedBooleanSubtensor,
2195+
AdvancedSubtensor,
22292196
check_topo=False,
22302197
)
22312198
self._compile_and_check(
22322199
[n],
22332200
[n[n[:, 0] > 2]],
22342201
[n_val],
2235-
AdvancedBooleanSubtensor,
2202+
AdvancedSubtensor,
22362203
check_topo=False,
22372204
)
22382205

tests/tensor/test_var.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
from theano.tensor.subtensor import (
1212
Subtensor,
1313
AdvancedSubtensor,
14-
AdvancedBooleanSubtensor,
1514
AdvancedSubtensor1,
1615
)
1716
from theano.tensor.elemwise import DimShuffle
@@ -124,31 +123,30 @@ def test__getitem__Subtensor():
124123
assert op_types[-1] == Subtensor
125124

126125

127-
def test__getitem__AdvancedBooleanSubtensor():
128-
# Make sure we get `AdvancedBooleanSubtensor`s for basic indexing operations
126+
def test__getitem__AdvancedSubtensor_bool():
129127
x = tt.matrix("x")
130128
i = tt.type.TensorType("bool", (False, False))("i")
131129

132130
z = x[i]
133131
op_types = [type(node.op) for node in theano.gof.graph.io_toposort([x, i], [z])]
134-
assert op_types[-1] == AdvancedBooleanSubtensor
132+
assert op_types[-1] == AdvancedSubtensor
135133

136134
i = tt.type.TensorType("bool", (False,))("i")
137135
z = x[:, i]
138136
op_types = [type(node.op) for node in theano.gof.graph.io_toposort([x, i], [z])]
139-
assert op_types[-1] == AdvancedBooleanSubtensor
137+
assert op_types[-1] == AdvancedSubtensor
140138

141139
i = tt.type.TensorType("bool", (False,))("i")
142140
z = x[..., i]
143141
op_types = [type(node.op) for node in theano.gof.graph.io_toposort([x, i], [z])]
144-
assert op_types[-1] == AdvancedBooleanSubtensor
142+
assert op_types[-1] == AdvancedSubtensor
145143

146144
with pytest.raises(TypeError):
147145
z = x[[True, False], i]
148146

149147
z = x[tt.ivector("b"), i]
150148
op_types = [type(node.op) for node in theano.gof.graph.io_toposort([x, i], [z])]
151-
assert op_types[-1] == AdvancedBooleanSubtensor
149+
assert op_types[-1] == AdvancedSubtensor
152150

153151

154152
def test__getitem__AdvancedSubtensor():

theano/gpuarray/opt.py

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,9 @@
130130
GpuSubtensor,
131131
GpuAdvancedSubtensor,
132132
GpuAdvancedSubtensor1,
133-
GpuAdvancedBooleanSubtensor,
134133
GpuAdvancedIncSubtensor,
135134
GpuAdvancedIncSubtensor1,
136135
GpuAdvancedIncSubtensor1_dev20,
137-
GpuAdvancedBooleanIncSubtensor,
138136
GpuAllocDiag,
139137
GpuExtractDiag,
140138
)
@@ -1291,13 +1289,6 @@ def local_gpua_advanced_subtensor(op, context_name, inputs, outputs):
12911289
return GpuAdvancedSubtensor()
12921290

12931291

1294-
@register_opt("fast_compile")
1295-
@op_lifter([tensor.AdvancedBooleanSubtensor])
1296-
@register_opt2([tensor.AdvancedBooleanSubtensor], "fast_compile")
1297-
def local_gpua_advanced_boolean_subtensor(op, context_name, inputs, outputs):
1298-
return GpuAdvancedBooleanSubtensor()
1299-
1300-
13011292
@register_opt("fast_compile")
13021293
@op_lifter([tensor.AdvancedIncSubtensor1])
13031294
@register_opt2([tensor.AdvancedIncSubtensor1], "fast_compile")
@@ -1342,20 +1333,6 @@ def local_gpua_advanced_incsubtensor(op, context_name, inputs, outputs):
13421333
return False
13431334

13441335

1345-
# Do not register this optimization for now, as it slows down the
1346-
# execution by a lot in important cases.
1347-
# @register_opt('fast_compile')
1348-
# @op_lifter([tensor.AdvancedBooleanIncSubtensor])
1349-
# @register_opt2([tensor.AdvancedBooleanIncSubtensor], 'fast_compile')
1350-
def local_gpua_advanced_boolean_incsubtensor(op, context_name, inputs, outputs):
1351-
# GpuAdvancedIncSubtensor only works with a single boolean mask,
1352-
# but not with fancy combinations.
1353-
if not op.set_instead_of_inc and len(inputs) == 3:
1354-
return GpuAdvancedBooleanIncSubtensor()
1355-
else:
1356-
return False
1357-
1358-
13591336
@register_inplace()
13601337
@local_optimizer([GpuAdvancedIncSubtensor1, GpuAdvancedIncSubtensor1_dev20])
13611338
def local_advincsub1_gpua_inplace(node):

theano/gpuarray/subtensor.py

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -683,9 +683,6 @@ class GpuAdvancedSubtensor(HideC, BaseGpuAdvancedSubtensor, tensor.AdvancedSubte
683683

684684
def make_node(self, x, *inputs):
685685
ctx_name = infer_context_name(x)
686-
# This method relies on AdvancedSubtensor.make_node to
687-
# call tensor.subtensor.check_and_reject_bool(inputs),
688-
# which raises an IndexError if there are any boolean indices.
689686
rval = tensor.AdvancedSubtensor.make_node(self, x, *inputs)
690687
otype = GpuArrayType(
691688
dtype=rval.outputs[0].type.dtype,
@@ -696,25 +693,6 @@ def make_node(self, x, *inputs):
696693
return gof.Apply(self, [x] + rval.inputs[1:], [otype()])
697694

698695

699-
class GpuAdvancedBooleanSubtensor(
700-
HideC, BaseGpuAdvancedSubtensor, tensor.AdvancedBooleanSubtensor
701-
):
702-
"""
703-
AdvancedBooleanSubtensor on the GPU.
704-
"""
705-
706-
def make_node(self, x, *inputs):
707-
ctx_name = infer_context_name(x)
708-
rval = tensor.AdvancedBooleanSubtensor.make_node(self, x, *inputs)
709-
otype = GpuArrayType(
710-
dtype=rval.outputs[0].type.dtype,
711-
broadcastable=rval.outputs[0].type.broadcastable,
712-
context_name=ctx_name,
713-
)
714-
x = as_gpuarray_variable(x, ctx_name)
715-
return gof.Apply(self, [x] + rval.inputs[1:], [otype()])
716-
717-
718696
class BaseGpuAdvancedIncSubtensor(object):
719697
def perform(self, node, inp, out_):
720698
(out,) = out_
@@ -852,27 +830,6 @@ def make_node(self, x, y, *inputs):
852830
return gof.Apply(self, [x, y] + rval.inputs[2:], [otype()])
853831

854832

855-
class GpuAdvancedBooleanIncSubtensor(
856-
HideC, BaseGpuAdvancedIncSubtensor, tensor.AdvancedBooleanIncSubtensor
857-
):
858-
"""
859-
Implement AdvancedBooleanIncSubtensor on the gpu.
860-
861-
"""
862-
863-
def make_node(self, x, y, *inputs):
864-
ctx_name = infer_context_name(x, y)
865-
rval = tensor.AdvancedBooleanIncSubtensor.make_node(self, x, y, *inputs)
866-
otype = GpuArrayType(
867-
dtype=rval.outputs[0].type.dtype,
868-
broadcastable=rval.outputs[0].type.broadcastable,
869-
context_name=ctx_name,
870-
)
871-
x = as_gpuarray_variable(x, ctx_name)
872-
y = as_gpuarray_variable(y, ctx_name)
873-
return gof.Apply(self, [x, y] + rval.inputs[2:], [otype()])
874-
875-
876833
class GpuAdvancedIncSubtensor1(Op):
877834
"""
878835
Implement AdvancedIncSubtensor1 on the gpu.

0 commit comments

Comments
 (0)