10

I was trying to convert the strictly upper triangular part of a matrix into an array in Tensorflow. Here is an example:

Input:

[[1, 2, 3],
 [4, 5, 6],
 [7, 8, 9]]

Output:

[2, 3, 6]

I tried the following code but it did not work (an error was reported):

def upper_triangular_to_array(A):
    mask = tf.matrix_band_part(tf.ones_like(A, dtype=tf.bool), 0, -1)
    return tf.boolean_mask(A, mask)

Thank you!

Alex
  • 317
  • 3
  • 9

3 Answers3

9

The following answer follows closely the answer from @Cech_Cohomology, but it doesn't use Numpy in the process, only TensorFlow.

import tensorflow as tf

# The matrix has size n-by-n
n = 3

# A is the matrix
A = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

ones = tf.ones_like(A)
mask_a = tf.matrix_band_part(ones, 0, -1) # Upper triangular matrix of 0s and 1s
mask_b = tf.matrix_band_part(ones, 0, 0)  # Diagonal matrix of 0s and 1s
mask = tf.cast(mask_a - mask_b, dtype=tf.bool) # Make a bool mask

upper_triangular_flat = tf.boolean_mask(A, mask)

sess = tf.Session()
print(sess.run(upper_triangular_flat))

This outputs:

[2 3 6]

The advantage of this method is that when the graph is run there is no need to give a feed_dict.

Walfits
  • 446
  • 5
  • 9
  • This is a great answer. However, if you wanted to do this for each sample or batch, how could you implement it such that it ignores the first dimension? In other words, gives a shape of [None, N*(N-1)/2], where N is the size of the original matrix (and N*(N-1)/2 is the number of elements in the upper triangle) – chase Aug 20 '20 at 03:57
2

I finally figured out how to do that using Tensorflow.

The idea is to define a placeholder as the boolean mask and then use numpy to pass a boolean matrix to the boolean mask in the runtime. I share my code below:

import tensorflow as tf
import numpy as np

# The matrix has size n-by-n
n = 3
# define a boolean mask as a placeholder
mask = tf.placeholder(tf.bool, shape=(n, n))
# A is the matrix
A = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    npmask = np.triu(np.ones((n, n), dtype=np.bool_), 1)
    A_upper_triangular = tf.boolean_mask(A, mask)
    print(sess.run(A_upper_triangular, feed_dict={mask: npmask}))

My Python version is 3.6 and my Tensorflow version is 0.12.0rc1. The output of the above code is

[2, 3, 6]

This method can be further generalized. We can use numpy to construct any kind of mask and then pass the mask to the Tensorflow to extract the part of the tensor of interest.

Alex
  • 317
  • 3
  • 9
-1

If you are using python 2.7, then for an NxN-element array, you can use a list comprehension with a conditional:

def upper_triangular_to_array(A):
    N = A.shape[0]
    return np.array([p for i, p in enumerate(A.flatten()) if i > (i / N) * (1 + N)])

This function requires that A is a 2-dimensional square numpy array to return the correct result. It also relies on floor division of integers, which you will need to correct for if you use python 3.x

kiliantics
  • 1,148
  • 1
  • 8
  • 11