Inconsistent behaviour of given kw in theano.function
In the following code, when trying to replace a node by one of its descendants (that is already in the graph), the substitution is not done in a consistent way when using function(..., given={...}).
Using clone(..., replace={...}) is consistent, when there is only one substitution to make.
>>> import theano
>>> from theano import function
>>> import theano.tensor as T
>>>
a = T.iscalar()
>>> b = a + 1
>>> c = b + 2
>>> d = b + 3
>>> e = c + d
>>> f = e + 1Normal run:
>>> ff = function([a], [a, b, c, d, e, f])
>>> ff(1)
[array(1, dtype=int32),
array(2, dtype=int32),
array(4, dtype=int32),
array(5, dtype=int32),
array(9, dtype=int32),
array(10, dtype=int32)]
Replacing b with c does so in the definition of d, but not of c (c uses the old b, d uses the new one):
>>> ff = function([a], [a, b, c, d, e, f], givens={b: c})
>>> ff(1)
[array(1, dtype=int32),
array(4, dtype=int32),
array(4, dtype=int32),
array(7, dtype=int32),
array(11, dtype=int32),
array(12, dtype=int32)]Conversely, replacing b with d does so in the definition of c:
>>> ff = function([a], [a, b, c, d, e, f], givens={b: d})
>>> ff(1)
[array(1, dtype=int32),
array(5, dtype=int32),
array(7, dtype=int32),
array(5, dtype=int32),
array(12, dtype=int32),
array(13, dtype=int32)]Explicitly rebuilding the expression b + 2 works:
>>> ff = function([a], [a, b, c, d, e, f], givens={b: b+2})
>>> ff(1)
[array(1, dtype=int32),
array(4, dtype=int32),
array(6, dtype=int32),
array(7, dtype=int32),
array(13, dtype=int32),
array(14, dtype=int32)]As well as using theano.clone:
>>> ff = function([a], theano.clone([a, b, c, d, e, f], replace={b: b+2}))
>>> ff(1)
[array(1, dtype=int32),
array(4, dtype=int32),
array(6, dtype=int32),
array(7, dtype=int32),
array(13, dtype=int32),
array(14, dtype=int32)]A possible solution would be to detect when the replacement is (or depends on) a child of the replaced node that is already in the original graph. For instance {b: c} or {b: c - 2} would trigger that detection, but {b: b + 2} or b: theano.clone(c) would not.
If such a dependency is detected, I think we should raise an exception. The error message should mention explicit cloning as a solution.
Source: Theano/Theano