I'm trying to contract a network with multiple tensors and using singular value decomposition during contraction to simplify the contraction process. Whilst this works perfectly when I'm not taking any gradient, it fails once gradient tape starts to watch the tensors (I'm not sure why this is related). Below I wrote my simple contraction function and the function that I'm taking svd:
import tensorflow as tf
@tf.function
def contraction_step(network, max_singular_values: int):
bottom = network[-1]
uppper = network[-2]
def contract_up_down(up,dn):
shu = tf.shape(up)
shd = tf.shape(dn)
c = tf.einsum("ijkxlm,nkpyqr->injpxylqmr", up, dn)
return tf.reshape(c, (
shu[0]*shd[0], shu[1], shd[2], shu[-3], shd[-3], shu[-2]*shd[-2], shu[-1]*shd[-1]
))
new = []
multiplier = tf.eye(tf.shape(bottom[-1])[-1]*tf.shape(uppper[-1])[-1], dtype=uppper[-1].dtype)
for ix in reversed(range(len(bottom))):
tensor = contract_up_down(uppper[ix], bottom[ix])
t = tf.einsum("ludpxor,ij->ludpxoj",tensor, multiplier)
u, s, vh = svd(t, 1, max_singular_values = max_singular_values)
multiplier = tf.tensordot(u, s/tf.norm(s), axes=(-1,0))
new.insert(0, vh)
new[-1] = tf.tensordot(new[-1], multiplier, axes=(-1,0))
return network[:-2] + [new]
def svd(tensor,pivot,max_singular_values = None,cutoff = 0.0):
left_dims = tf.shape(tensor)[:pivot]
right_dims = tf.shape(tensor)[pivot:]
tensor = tf.reshape(tensor, (tf.reduce_prod(left_dims), tf.reduce_prod(right_dims)))
s, u, v = tf.linalg.svd(tensor)
s_shape = tf.math.count_nonzero(
tf.cast(s >= cutoff, dtype = tf.int32), dtype = tf.int32
)
if max_singular_values is None:
max_singular_values = s_shape
else:
max_singular_values = tf.cast(tf.constant(max_singular_values), dtype = tf.int32)
num_sing_vals_keep = tf.maximum(
tf.minimum(max_singular_values, s_shape), tf.constant(1, dtype = tf.int32)
)
s = tf.slice(s, [0], [num_sing_vals_keep])
u = tf.slice(u, [0, 0], [tf.shape(u)[0], num_sing_vals_keep])
v = tf.slice(v, [0, 0], [tf.shape(v)[0], num_sing_vals_keep])
vh = tf.linalg.adjoint(v)
dim_s = tf.shape(s)[0] # must use tf.shape (not s.shape) to compile
u = tf.reshape(u, tf.concat([left_dims, [dim_s]], axis = -1))
vh = tf.reshape(vh, tf.concat([[dim_s], right_dims], axis = -1))
return u, tf.linalg.diag(s), vh
These functions work perfectly while using standalone:
upper = [tf.random.uniform((5,3,3,2,1,5), dtype=tf.float64) for _ in range(5)]
lower = [tf.random.uniform((5,3,3,2,1,5), dtype=tf.float64) for _ in range(5)]
contracted = contraction_step([upper, lower], 2)[0]
print(f"shapes: {', '.join([str(x.shape) for x in contracted])}")
# shapes: (2, 3, 3, 2, 2, 1, 2), (2, 3, 3, 2, 2, 1, 2), (2, 3, 3, 2, 2, 1, 2), (2, 3, 3, 2, 2, 1, 2), (2, 3, 3, 2, 2, 1, 2)
However, with the gradient, I get the following error:
with tf.GradientTape() as tape:
tape.watch(upper + lower)
contracted = contraction_step([upper, lower], 2)[0]
NotImplementedError: SVD gradient has not been implemented for input with unknown inner matrix shape.
It seems like for some reason during gradient mode TensorFlow loses the shape information of the tensors. Note that I get the same error when I set the tensors as tf.Variable instead of watching them manually. Any help would be highly appreciated!
Thanks
System information
- OS Platform and Distribution: macOS v12.0.1
- TensorFlow version: v2.6.0-rc2-32-g919f693420e 2.6.0
- Python version: 3.8.9