Cartesian Product in Tensorflow - python

Is there any easy way to do cartesian product in Tensorflow like itertools.product? I want to get combination of elements of two tensors (a and b), in Python it is possible via itertools as list(product(a, b)). I am looking for an alternative in Tensorflow.

I'm going to assume here that both a and b are 1-D tensors.
To get the cartesian product of the two, I would use a combination of tf.expand_dims and tf.tile:
a = tf.constant([1,2,3])
b = tf.constant([4,5,6,7])
tile_a = tf.tile(tf.expand_dims(a, 1), [1, tf.shape(b)[0]])
tile_a = tf.expand_dims(tile_a, 2)
tile_b = tf.tile(tf.expand_dims(b, 0), [tf.shape(a)[0], 1])
tile_b = tf.expand_dims(tile_b, 2)
cartesian_product = tf.concat([tile_a, tile_b], axis=2)
cart = tf.Session().run(cartesian_product)
print(cart.shape)
print(cart)
You end up with a len(a) * len(b) * 2 tensor where each combination of the elements of a and b is represented in the last dimension.

A shorter solution to the same, using tf.add() for broadcasting (tested):
import tensorflow as tf
a = tf.constant([1,2,3])
b = tf.constant([4,5,6,7])
a, b = a[ None, :, None ], b[ :, None, None ]
cartesian_product = tf.concat( [ a + tf.zeros_like( b ),
tf.zeros_like( a ) + b ], axis = 2 )
with tf.Session() as sess:
print( sess.run( cartesian_product ) )
will output:
[[[1 4]
[2 4]
[3 4]]
[[1 5]
[2 5]
[3 5]]
[[1 6]
[2 6]
[3 6]]
[[1 7]
[2 7]
[3 7]]]

import tensorflow as tf
a = tf.constant([0, 1, 2])
b = tf.constant([2, 3])
c = tf.stack(tf.meshgrid(a, b, indexing='ij'), axis=-1)
c = tf.reshape(c, (-1, 2))
with tf.Session() as sess:
print(sess.run(c))
Output:
[[0 2]
[0 3]
[1 2]
[1 3]
[2 2]
[2 3]]
credit to jdehesa: link

A more succinct version of Sunreef's answer uses tf.stack instead of tf.concat
a = tf.constant([1,2,3])
b = tf.constant([4,5,6,7])
tile_a = tf.tile(tf.expand_dims(a, 1), [1, tf.shape(b)[0]])
tile_b = tf.tile(tf.expand_dims(b, 0), [tf.shape(a)[0], 1])
ans = tf.stack([tile_a, tile_b], -1)

I'm inspired by Jaba's answer. If you want to get the cartesian_product of two 2-D tensors, you can do it as following:
input a:[N,L] and b:[M,L], get a [N*M,L] concat tensor
tile_a = tf.tile(tf.expand_dims(a, 1), [1, M, 1])
tile_b = tf.tile(tf.expand_dims(b, 0), [N, 1, 1])
cartesian_product = tf.concat([tile_a, tile_b], axis=2)
cartesian = tf.reshape(cartesian_product, [N*M, -1])
cart = tf.Session().run(cartesian)
print(cart.shape)
print(cart)

Related

How to produce all the combinations of the elements of two or more tensors?

I would like to compute all the combinations of two or more tensors. For example, for two tensors containing resp. the values [1, 2] and [3, 4, 5], I would like to get the 6x2 tensor
[[1, 3],
[1, 4],
[1, 5],
[2, 3],
[2, 4],
[2, 5]]
To do this, I came up with the following hack
import tensorflow as tf
def combine(x, y):
x, y = x[:, None], y[:, None]
x1 = tf.concat([x, tf.ones_like(x)], axis=-1)
y1 = tf.concat([tf.ones_like(y), y], axis=-1)
return tf.reshape(x1[:, None] * y1[None], (-1, 2))
x = tf.constant([1, 2])
y = tf.constant([3, 4, 5])
print(combine(x, y))
# tf.Tensor(
# [[1 3]
# [1 4]
# [1 5]
# [2 3]
# [2 4]
# [2 5]], shape=(6, 2), dtype=int32)
However I am not satisfied with this solution:
It uses multiplication to combine the elements, which is clearly overkilled
It does not easily generalize to an arbitrary combination of n tensors
Is there a more efficient and/or general way of doing this?
You can do that easily with tf.meshgrid:
import tensorflow as tf
def combine(x, y):
xx, yy = tf.meshgrid(x, y, indexing='ij')
return tf.stack([tf.reshape(xx, [-1]), tf.reshape(yy, [-1])], axis=1)
x = tf.constant([1, 2])
y = tf.constant([3, 4, 5])
print(combine(x, y).numpy())
# [[1 3]
# [1 4]
# [1 5]
# [2 3]
# [2 4]
# [2 5]]

Extract max-sum submatrices

I've a 2D NxN matrix that has elements from a set of real numbers. I need to identify top n DxD sub-matrices from it such that their sum is maximum and return top left index of the sub-matrices. I need to do it in Tensorflow.
For example I have following 4x4 matrix:
[1 1 4 4]
[1 1 4 4]
[3 3 2 2]
[3 3 2 2]
I need to identify 2 sub-matrices that have the largest sum and return their top left index. In above case, 2 sub-matrices that have the largest and second largest sum are:
[[4 4] [[3 3]
[4 4]] & [3 3]]
I need to return [[0,2],[2,0]], the top left indices to both the matrices. Thanks.
You can get that with the following snippet. The idea is to build a tensor holding the row and column indices of each element of each submatrix, then sum the submatrices and find the largest sums.
import tensorflow as tf
# Input data
input = tf.placeholder(tf.int32, [None, None])
# Submatrix dimension
dims = tf.placeholder(tf.int32, [2])
# Number of top submatrices to find
k = tf.placeholder(tf.int32, [])
# Sizes
input_shape = tf.shape(input)
rows, cols = input_shape[0], input_shape[1]
d_rows, d_cols = dims[0], dims[1]
subm_rows, subm_cols = rows - d_rows + 1, cols - d_cols + 1
# Index grids
ii, jj = tf.meshgrid(tf.range(subm_rows), tf.range(subm_cols), indexing='ij')
d_ii, d_jj = tf.meshgrid(tf.range(d_rows), tf.range(d_cols), indexing='ij')
# Add indices
subm_ii = ii[:, :, tf.newaxis, tf.newaxis] + d_ii
subm_jj = jj[:, :, tf.newaxis, tf.newaxis] + d_jj
# Make submatrices tensor
subm = tf.gather_nd(input, tf.stack([subm_ii, subm_jj], axis=-1))
# Add submatrices
subm_sum = tf.reduce_sum(subm, axis=(2, 3))
# Use TopK to find top submatrices
_, top_idx = tf.nn.top_k(tf.reshape(subm_sum, [-1]), tf.minimum(k, tf.size(subm_sum)))
# Get row and column
top_row = top_idx // subm_cols
top_col = top_idx % subm_cols
result = tf.stack([top_row, top_col], axis=-1)
# Test
with tf.Session() as sess:
mat = [
[1, 1, 4, 4],
[1, 1, 4, 4],
[3, 3, 2, 2],
[3, 3, 2, 2],
]
print(sess.run(result, feed_dict={input: mat, dims: [2, 2], k: 2}))
Output:
[[0 2]
[1 2]]
Note that the output in this case is [0, 2] and [1, 2], but not [2, 0]. That's because the submatrix starting at [1, 2] sums the same amount as the one at [2, 0], and it is before in the matrix, if you iterate it by rows. If you pass k: 3 in the test you would get [2, 0] too in the result.

How to efficiently extract all slices of given length using tensorflow

I am trying to extract all slices of length 4 along 0th axis of a 2-dim tensor. So far I can do it mixing pure Python with tensorflow.
r = test.shape[0] # test should be a tensor
n = 4
a_list = list(range(r))
the_list = np.array([a_list[slice(i, i+n)] for i in range(r - n+1)])
test_stacked = tf.stack(tf.gather(test, the_list))
What would be an efficient way of doing that without using pure Python? Note that the "test" array is actually supposed to be a tensor, thus its shape isn't known before I execute the first part of the graph.
A full vanilla example:
array = np.array([[0, 1],[1, 2],[2, 3],[3, 4],[4, 5],[5, 6]])
array.shape # (6,2)
r = array.shape[0]
n = 4
a_list = list(range(r))
the_list = np.array([a_list[slice(i, i+n)] for i in range(r - n+1)])
result = array[the_list] # all possible slices of length 4 of the array along 0th axis
result.shape # (3, 4, 2)
result:
[[[0 1]
[1 2]
[2 3]
[3 4]]
[[1 2]
[2 3]
[3 4]
[4 5]]
[[2 3]
[3 4]
[4 5]
[5 6]]]
You may want to try the more general tf.extract_image_patches.
import tensorflow as tf
a = tf.constant([[0, 1],[1, 2],[2, 3],[3, 4],[4, 5],[5, 6]])
# tf.extract_image_patches requires a [batch, in_rows, in_cols, depth] tensor
a = a[None, :, :, None]
b = tf.extract_image_patches(a,
ksizes=[1, 4, 2, 1],
strides=[1, 1, 1, 1],
rates=[1, 1, 1, 1],
padding='VALID')
b = tf.reshape(tf.squeeze(b), [-1, 4, 2])
sess = tf.InteractiveSession()
print(b.eval())
I believe gather_nd is what you are looking for.
# a is a tensor of size (6, 2)
def get_indices(l, d):
return [[[j] for j in range(i, i + d)] for i in range(l - d + 1)]
b = tf.gather_nd(a, get_indices(6, 4))
# b is a tensor of shape (3, 4, 2)

Tensorflow: stack all row pairs from a tensor

Given a tensor t=[[1,2], [3,4]], I need to produce ts=[[1,2,1,2], [1,2,3,4], [3,4,1,2], [3,4,3,4]]. That is, I need to stack together all row pairs.
Important: the tensor has dimension [None, 2], ie. the first dimension is variable.
I have tried:
Using a tf.while_loop to generate a list of indices idx=[[0, 0], [0, 1], [1, 0], [1, 1]], then tf.gather(ts, idx). This works but is messy and I don't know what to do about gradients.
2 for loops iterating over tf.unstack(t), adding stacked rows to a buffer, then tf.stack(buffer). This does not work if the first dimension is variable.
To look for inspiration in broadcasting. For instance, given x=t.expand_dims(t, 0), y=t.expand_dims(t, 1), s=tf.reshape(tf.add(x, y), [-1, 2]) s will be [[2, 4], [4, 6], [4, 6], [6, 8]], ie. the sum of every row combination. But how can I do stacking instead of sum? I've been failing for 2 days :)
Solution with tf.meshgrid() and some reshaping:
import tensorflow as tf
import numpy as np
t = tf.placeholder(tf.int32, [None, 2])
num_rows, size_row = tf.shape(t)[0], tf.shape(t)[1] # actual dynamic dimensions
# Getting pair indices using tf.meshgrid:
idx_range = tf.range(num_rows)
pair_indices = tf.stack(tf.meshgrid(*[idx_range, idx_range]))
pair_indices = tf.transpose(pair_indices, perm=[1, 2, 0])
# Finally gathering the rows accordingly:
res = tf.reshape(tf.gather(t, pair_indices), (-1, size_row * 2))
with tf.Session() as sess:
print(sess.run(res, feed_dict={t: np.array([[1,2], [3,4], [5,6]])}))
# [[1 2 1 2]
# [3 4 1 2]
# [5 6 1 2]
# [1 2 3 4]
# [3 4 3 4]
# [5 6 3 4]
# [1 2 5 6]
# [3 4 5 6]
# [5 6 5 6]]
Solution using cartesian product:
import tensorflow as tf
import numpy as np
t = tf.placeholder(tf.int32, [None, 2])
num_rows, size_row = tf.shape(t)[0], tf.shape(t)[1] # actual dynamic dimensions
# Getting pair indices by computing the indices cartesian product:
row_idx = tf.range(num_rows)
row_idx_a = tf.expand_dims(tf.tile(tf.expand_dims(row_idx, 1), [1, num_rows]), 2)
row_idx_b = tf.expand_dims(tf.tile(tf.expand_dims(row_idx, 0), [num_rows, 1]), 2)
pair_indices = tf.concat([row_idx_a, row_idx_b], axis=2)
# Finally gathering the rows accordingly:
res = tf.reshape(tf.gather(t, pair_indices), (-1, size_row * 2))
with tf.Session() as sess:
print(sess.run(res, feed_dict={t: np.array([[1,2], [3,4], [5,6]])}))
# [[1 2 1 2]
# [1 2 3 4]
# [1 2 5 6]
# [3 4 1 2]
# [3 4 3 4]
# [3 4 5 6]
# [5 6 1 2]
# [5 6 3 4]
# [5 6 5 6]]
Can be achieved by:
tf.concat([tf.tile(tf.expand_dims(t,1), [1, tf.shape(t)[0], 1]), tf.tile(tf.expand_dims(t,0), [tf.shape(t)[0], 1, 1])], axis=2)
Detailed steps:
t = tf.placeholder(tf.int32, shape=[None, 2])
#repeat each row of t
d = tf.tile(tf.expand_dims(t,1), [1, tf.shape(t)[0], 1])
#Output:
#[[[1 2] [1 2]]
# [[3 4] [3 4]]]
#repeat the entire input t
e = tf.tile(tf.expand_dims(t,0), [tf.shape(t)[0], 1, 1])
#Output:
#[[[1 2] [3 4]]
# [[1 2] [3 4]]]
#concat
f = tf.concat([d, e], axis=2)
with tf.Session() as sess:
print(sess.run(f, {t:np.asarray([[1,2],[3,4]])}))
#Output
#[[[1 2 1 2]
#[1 2 3 4]]
#[[3 4 1 2]
#[3 4 3 4]]]

Difference between `tf.reshape(a, [m, n])` and `tf.transpose(tf.reshape(a, [n, m]))`?

Actually, I'm doing the homework "Art Generation with Neural Style Transfer" of deeplearning.ai on coursera. In the function compute_layer_style_cost(a_S, a_G):
a_S = tf.reshape(a_S, [n_H*n_W, n_C])
a_G = tf.reshape(a_G, [n_H*n_W, n_C])
GS = gram_matrix(tf.transpose(a_S))
GG = gram_matrix(tf.transpose(a_G))
Why does this code give the right answer, however, the following doesn't:
a_S = tf.reshape(a_S, [n_C, n_H*n_W])
a_G = tf.reshape(a_G, [n_C, n_H*n_W])
GS = gram_matrix(a_S)
GG = gram_matrix(a_G)
Here's a trivial example that shows the difference between these two expressions:
import tensorflow as tf
tf.InteractiveSession()
x = tf.range(0, 6)
a = tf.reshape(x, [3, 2])
b = tf.transpose(tf.reshape(x, [2, 3]))
print(x.eval())
print(a.eval())
print(b.eval())
The result:
[0 1 2 3 4 5]
[[0 1]
[2 3]
[4 5]]
[[0 3]
[1 4]
[2 5]]
As you can notice, a and b are different, though have the same shape. That's because the first reshaping "splits" x into [0 1], [2 3] and [4 5], while the second reshaping into [0 1 2] and [3 4 5].

Categories