## Understanding static and dynamic shapes
Tensors in TensorFlow have a static shape attribute which is determined during graph construction. The static shape may be underspecified. For example we might define a tensor of shape [None, 128]:
```python
import tensorflow as tf
a = tf.placeholder(tf.float32, [None, 128])
```
This means that the first dimension can be of any size and will be determined dynamically during Session.run(). You can query the static shape of a Tensor as follows:
```python
static_shape = a.shape.as_list() # returns [None, 128]
```
To get the dynamic shape of the tensor you can call tf.shape op, which returns a tensor representing the shape of the given tensor:
```python
dynamic_shape = tf.shape(a)
```
The static shape of a tensor can be set with Tensor.set_shape() method:
```python
a.set_shape([32, 128]) # static shape of a is [32, 128]
a.set_shape([None, 128]) # first dimension of a is determined dynamically
```
You can reshape a given tensor dynamically using tf.reshape function:
```python
a = tf.reshape(a, [32, 128])
```
It can be convenient to have a function that returns the static shape when available and dynamic shape when it's not. The following utility function does just that:
```python
def get_shape(tensor):
static_shape = tensor.shape.as_list()
dynamic_shape = tf.unstack(tf.shape(tensor))
dims = [s[1] if s[0] is None else s[0]
for s in zip(static_shape, dynamic_shape)]
return dims
```
Now imagine we want to convert a Tensor of rank 3 to a tensor of rank 2 by collapsing the second and third dimensions into one. We can use our get_shape() function to do that:
```python
b = tf.placeholder(tf.float32, [None, 10, 32])
shape = get_shape(b)
b = tf.reshape(b, [shape[0], shape[1] * shape[2]])
```
Note that this works whether the shapes are statically specified or not.
In fact we can write a general purpose reshape function to collapse any list of dimensions:
```python
import tensorflow as tf
import numpy as np
def reshape(tensor, dims_list):
shape = get_shape(tensor)
dims_prod = []
for dims in dims_list:
if isinstance(dims, int):
dims_prod.append(shape[dims])
elif all([isinstance(shape[d], int) for d in dims]):
dims_prod.append(np.prod([shape[d] for d in dims]))
else:
dims_prod.append(tf.reduce_prod([shape[d] for d in dims]))
tensor = tf.reshape(tensor, dims_prod)
return tensor
```
Then collapsing the second dimension becomes very easy:
```python
b = tf.placeholder(tf.float32, [None, 10, 32])
b = reshape(b, [0, [1, 2]])
```