Non-uniform Tensors¶

Colab   •   🌐 ΦML   •   📖 Documentation   •   🔗 API   •   ▶ Videos   •   Examples

In [1]:
%%capture
!pip install phiml

from phiml import math
from phiml.math import stack, zeros, ones, spatial, channel

ΦML allows tensors of varying sizes to be stacked into a single non-uniform tensor. Unlike with many other libraries, the result shape is still well-defined.

Uniformity is not to be confused with homogeneity, which refers to data types.

Let's look at an example.

In [2]:
a = zeros(spatial(x=4, y=2))
b = ones(spatial(y=2, x=5))
stacked = stack([a, b], channel('s'))
stacked
Out[2]:
(sᶜ=2, xˢ=(4, 5) along sᶜ int64, yˢ=2) 0.556 ± 0.497 (0e+00...1e+00)

Here, we have stacked two tensors with varying sizes along x. Looking at the result shape, we see that the sizes of y and the stack dimension s are simple integers, but the size of x has become a Tensor itself.

In [3]:
stacked.shape['y'].size
Out[3]:
2
In [4]:
stacked.shape['x'].size
Out[4]:
(4, 5) along sᶜ int64

We can easily verify that the shape is non-uniform.

In [5]:
stacked.shape.is_uniform
Out[5]:
False

For uniform tensors, you can think of their shape as a list of sizes, i.e. a 1D tensor. For non-uniform tensors, this is not the case, as can be seen with the second-order shape.

In [6]:
stacked.shape.shape
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[6], line 1
----> 1 stacked.shape.shape

AttributeError: 'MixedShape' object has no attribute 'shape'

This is the shape of the tensor shape. While it may be confusing to think about at first, the second-order shape now contains all non-uniform dimensions in addition to the standard dims dimension.

For tensors with a single non-uniform dimension, slicing along that dimension yields uniform tensors.

In [7]:
stacked.s[0]
Out[7]:
(xˢ=4, yˢ=2) const 0.0

You can also get a slice of the shape without actually slicing the tensor.

In [8]:
stacked.shape.after_gather({'s': 0})
Out[8]:
(xˢ=4, yˢ=2)

Tensors with multiple non-uniform dimensions are also supported.

In [9]:
stacked2 = stack([stacked, stacked.y[:1]], channel('s2'))
stacked2
Out[9]:
(s2ᶜ=2, sᶜ=2, xˢ=(4, 5) along sᶜ int64, yˢ=(2, 1) along s2ᶜ int64) 0.556 ± 0.497 (0e+00...1e+00)

While most math functions can handle non-uniform tensors correctly, we recommend using this feature cautiously.

In [10]:
math.mean(stacked2)
Out[10]:
float64 0.5555555555555556
In [11]:
try:
    math.std(stacked2, 's2')
except Exception as exc:
    print(exc)
Cannot merge shapes ((xˢ=4, yˢ=2), (xˢ=4, yˢ=1)) because dimension 'y' exists with different sizes.

Further Reading¶

🌐 ΦML   •   📖 Documentation   •   🔗 API   •   ▶ Videos   •   Examples