TensoSSHT Layers for Keras

This module provides thin wrappers around the harmonic transforms to make them keras layers.

Forward Harmonic Transoform Layers

Forward Single Spin Layer

The forward layer is parameterized according to the image-space sampling, whether the image is real or complex, and on the underlying floating point size. The order of the dimensions can also be given as input.

The layers can be created using the so-called functional style. \(l_\mathrm{max}\) is determined during the functional call from the size of the input images. For instance:

from tensossht import ForwardLayer
from tensossht.sampling import MW

lmax = 10
dtype = tf.float64
sampling = MW(lmax)
inputs = tf.keras.layers.Input(sampling.shape, dtype=dtype)
ssht = ForwardLayer(is_real=True, sampling=sampling, dtype=dtype)(inputs)

The layers also accept the spin and the indices of the \(\theta\), \(\phi\), and coeffficient dimensions as input. The spin is zero by default. Only one spin is computed at a time. We can verify the layer works by creating a model from this single layer and calling it on the spherical harmonics:

from pytest import approx
from tensossht import spherical_harmonics, legendre_labels

model = tf.keras.models.Model(inputs=inputs, outputs=ssht)

labels = legendre_labels(lmax, mmin=0)
sph = tf.math.real(
    spherical_harmonics(sampling.grid[0], sampling.grid[1], labels=labels)
)
y = model(tf.transpose(sph, [2, 0, 1]))
expected = tf.eye(sph.shape[-1], dtype=tf.int32) / tf.where(labels[1] == 0, 1, 2)
assert y.numpy() == approx(expected.numpy(), abs=1e-7)

The model can be saved and reloaded, as illustrated here:

from tempfile import TemporaryDirectory
from pathlib import Path

with TemporaryDirectory() as directory:
    model.save(str(Path(directory) / "model"))
    reloaded = tf.keras.models.load_model(str(Path(directory) / "model"))

We can verify the reloaded model works by applying it to the spherical harmonics:

y = reloaded(tf.transpose(sph, [2, 0, 1]))
assert y.numpy() == approx(expected.numpy(), abs=1e-7)

Forward Multi-Spin Layer

The multi-spin layer expects as input a stack of images. The size of the stack should be odd. It determines the range of spins over which the transform will proceed:

from tensossht import ForwardSpinLayer
from tensossht.sampling import MW

lmax, nspins = 4, 5
dtype = tf.complex128
sampling = MW(lmax)
inputs = tf.keras.layers.Input((nspins, *sampling.shape), dtype=dtype)
ssht = ForwardSpinLayer(sampling=sampling, dtype=dtype)(inputs)

The layer accepts the same parameters as ForwardLayer as well as the index of the spin axis in the images and the index of the spin axis for the harmonic-coefficients. We can verify the layer works by creating a model from this single layer and calling it on the spin spherical harmonics:

from pytest import approx
from tensossht.specialfunctions.naive import spin_spherical_harmonics
from tensossht.sampling import MW

model = tf.keras.models.Model(inputs=inputs, outputs=ssht)

grid = MW(lmax, dtype=tf.math.real(tf.zeros(0, dtype)).dtype).grid
sph = spin_spherical_harmonics(
    grid[0],
    grid[1],
    lmax=lmax,
    mmin=None,
    smin=-(nspins - 1) // 2,
    smax=(nspins - 1) // 2,
    compact_spin=False
)
sph = tf.reshape(sph, (sph.shape[0], sph.shape[1], nspins, -1))
y = model(tf.transpose(sph, [3, 2, 0, 1])).numpy()

Since spins are represented by a separate dimension, some rows in the tensor are empty. They correspond for instance to spin == 1 and l == 0:

for s in tf.range(-(nspins - 1) // 2, (nspins + 1) // 2):
    expected = np.eye(y.shape[-1])
    expected[: s * s, : s * s] = 0
    assert y[:, s + (nspins - 1) // 2] == approx(expected, abs=1e-6, rel=1e-6)

Once again, the model can be saved and reloaded:

from tempfile import TemporaryDirectory
from pathlib import Path

with TemporaryDirectory() as directory:
    model.save(str(Path(directory) / "model"))
    reloaded = tf.keras.models.load_model(str(Path(directory) / "model"))

yreloaded = reloaded(tf.transpose(sph, [3, 2, 0, 1])).numpy()
assert yreloaded == approx(y)

Inverse Harmonic Transform Layers

Inverse Single Spin Layer

The inverser layer works in a similar fashion to the forward single-spin layer. It also takse the same arguments as input:

from pytest import approx
from tensossht import spherical_harmonics, legendre_labels, InverseLayer
from tensossht.sampling import MW, image_sampling_scheme

lmax = 10
rdtype, cdtype = tf.float64, tf.complex128
sampling = MW(lmax)
inputs = tf.keras.layers.Input(lmax * lmax, dtype=cdtype)
ssht = InverseLayer(is_real=False, sampling=sampling, dtype=cdtype)(inputs)
model = tf.keras.models.Model(inputs=inputs, outputs=ssht)

labels = legendre_labels(lmax, mmin=-lmax)
grid = image_sampling_scheme(sampling).value(lmax, dtype=rdtype).grid
sph = tf.transpose(spherical_harmonics(grid[0], grid[1], labels=labels), [2, 0, 1])

coeffs = tf.eye(lmax * lmax, dtype=cdtype)
model = tf.keras.models.Model(inputs=inputs, outputs=ssht)
y = model(coeffs)
assert y.dtype == cdtype
assert y.numpy() == approx(sph.numpy(), abs=1e-6)

Once again, the model can be saved and reloaded:

from tempfile import TemporaryDirectory
from pathlib import Path

with TemporaryDirectory() as directory:
    model.save(str(Path(directory) / "model"))
    reloaded = tf.keras.models.load_model(str(Path(directory) / "model"))

y = reloaded(coeffs)
assert y.dtype == cdtype
assert y.numpy() == approx(sph.numpy(), abs=1e-6)

Inverse Multi-Spin Layer

Setting up a multi-spin layer should offer no surprises:

from pytest import approx
from tensossht import InverseSpinLayer
from tensossht.sampling import MW
from tensossht.specialfunctions.naive import spin_spherical_harmonics

lmax, nspins = 4, 5
dtype = tf.complex128
sampling = MW(lmax)
inputs = tf.keras.layers.Input((nspins, lmax * lmax), dtype=dtype)
ssht = InverseSpinLayer(sampling=sampling, dtype=dtype)(inputs)

model = tf.keras.models.Model(inputs=inputs, outputs=ssht)

grid = MW(lmax, dtype=tf.math.real(tf.zeros(0, dtype)).dtype).grid
coeffs = tf.reshape(
    tf.eye(nspins * lmax * lmax, dtype=dtype),
    (nspins * lmax * lmax, nspins, lmax * lmax),
)

y = model(coeffs)
y = tf.reshape(y, (nspins, lmax * lmax, *y.shape[1:]))

for sin in range(-(nspins // 2), nspins // 2 + 1):
    for sout in range(-(nspins // 2), nspins // 2 + 1):
        element = y[sin + nspins // 2, :, sout + nspins // 2].numpy()
        if sin != sout:
            assert element == approx(0)
        else:
            sph = spin_spherical_harmonics(
                grid[0],
                grid[1],
                lmax=lmax,
                mmin=-lmax,
                smin=sin,
                smax=sin,
                compact_spin=False,
            )
            sph = tf.transpose(sph, [2, 0, 1]).numpy()
            assert element == approx(sph, rel=1e-6, abs=1e-6)

Once again, the model can be saved and reloaded:

from tempfile import TemporaryDirectory
from pathlib import Path

with TemporaryDirectory() as directory:
    model.save(str(Path(directory) / "model"))
    reloaded = tf.keras.models.load_model(str(Path(directory) / "model"))

assert reloaded(coeffs).numpy() == approx(model(coeffs).numpy())

Wigner Transform Layers

The forward and inverse transforms can be instanciated using the factory function wigner_layer() in tensossht.layers. The implementation follows [McEwen2015] and is implemented via Fourier transforms and a spin harmonic transforms.

from tensossht.layers import wigner_layer
from tensossht.sampling import MW

lmax, nspins = 7, 5
sampling = MW(lmax)
inputs = tf.keras.layers.Input((*sampling.shape, nspins), dtype=tf.complex128)
forward = wigner_layer(is_forward=True, sampling="mw", dtype=tf.complex128)(inputs)

We can verify that the transform works correctly by applying it to the Wigner functions themselves. First, we compute all the Wigner functions for the current harmonic-space sampling.

from tensossht.sampling import harmonic_sampling_scheme
from tensossht.specialfunctions import kostelec

hsampling = harmonic_sampling_scheme(
    lmax=lmax, smin=-(nspins // 2), smax=nspins // 2, compact_spin=False
)

wignerd = tf.reshape(
    tf.cast(
        kostelec.wignerd(labels=hsampling.labels, beta=sampling.thetas),
        tf.complex128
    ),
    (-1, 1, 1, hsampling.ncoeffs)
)
gamma = MW(hsampling.smax + 1).phis
gamma_exp = tf.exp(
    tf.complex(
        tf.constant(0, tf.float64),
        -gamma[None, None, :, None] * tf.cast(hsampling.slabels, tf.float64)
    )
)
phi_exp = tf.exp(
    tf.complex(
        tf.constant(0, tf.float64),
        -sampling.phis[None, :, None, None] * tf.cast(hsampling.mlabels, tf.float64)
    )
)

basis = tf.transpose(
    phi_exp * wignerd * gamma_exp
    * tf.cast(
        tf.cast(2 * hsampling.llabels + 1, dtype=tf.float64) / (8 * np.pi * np.pi),
        dtype=tf.complex128
    ),
    perm=[3, 0, 1, 2],
    conjugate=True
)

Then, we create and apply the model:

model = tf.keras.models.Model(inputs=inputs, outputs=forward)
y = model(basis)

We can test that the result is almost identity, expect where the triplet (l, m, s) is not valid:

from pytest import approx

for i, (l, m, s) in enumerate(hsampling.labels.numpy().T):
    if np.abs(m) > l or np.abs(s) > l:
        continue
        assert y[i].numpy() == approx(0)
    else:
        expected = tf.one_hot(i, hsampling.ncoeffs).numpy()
        assert y[i].numpy().flatten() == approx(expected, abs=1e-6)

The inverse layer can be instantiated and a model created as follows:

inv_inputs = tf.keras.layers.Input(y.shape[1:], dtype=tf.complex128)
inverse = wigner_layer(is_forward=False, sampling="mw", dtype=tf.complex128)(
    inv_inputs
)
inv_model = tf.keras.models.Model(inputs=inv_inputs, outputs=inverse)
inv_y = inv_model(y)

assert inv_y.numpy() == approx(basis.numpy(), abs=1e-8, rel=1e-6)

Once again, the models can be saved and reloaded. Lets recreate the forward transform:

from tempfile import TemporaryDirectory
from pathlib import Path

with TemporaryDirectory() as directory:
    model.save(str(Path(directory) / "model"))
    reloaded = tf.keras.models.load_model(str(Path(directory) / "model"))

yreloaded = reloaded(basis)
assert y.numpy() == approx(yreloaded.numpy())

And the inverse transform:

with TemporaryDirectory() as directory:
    inv_model.save(str(Path(directory) / "inv_model"))
    inv_reloaded = tf.keras.models.load_model(str(Path(directory) / "inv_model"))

inv_yreloaded = inv_reloaded(y)
assert inv_y.numpy() == approx(inv_yreloaded.numpy())

Fourier Transform Layer

The Fourier transform performs two operations. It shifts the axis over which to perform the FFT to last, and then it performs the forward or inverse FFT itself. Currently, only complex transforms are implemented.

from tensossht.layers.wigner import FourierLayer

shape = (5, 3)
inputs = tf.keras.layers.Input(shape, dtype=tf.complex128)
fft = FourierLayer(is_forward=True, axis=-2)(inputs)

The axis can be given relative to the last dimension, as a negative index. If given as a positive index, one should not forget the batch dimension. We can verify the transform operates as expected:

from pytest import approx

model = tf.keras.models.Model(inputs=inputs, outputs=fft)
data = tf.cast(tf.random.uniform((2, *shape)), dtype=tf.complex128)
transformed = model(data)

exponent = tf.cast(
    tf.range(shape[0])[:, None] * tf.range(shape[0])[None],
    tf.complex128
) * complex(0, -2 * np.pi / shape[0])
handrolled = tf.reduce_sum(
    tf.transpose(data, perm=[0, 2, 1])[:, :, :, None] * tf.exp(exponent), axis=-2
)

assert transformed.numpy().round(4) == approx(handrolled.numpy().round(4))

The Fourier layer also accepts is_real as an argument, in which case it expects or creates real-valued image-space signals. In that case, the inverse transform cannot guess the size of the image-space signals from the size of the Fourier-space signals. Hence, the layer also accepts a parameter is_odd_spin for signals where the length in real space is odd.

[McEwen2015]

McEwen JD, Büttner M, Leistedt B, Peiris HV, Wiaux Y. A “A novel sampling theorem on the rotation group” IEEE Signal Processing Letters 22.12 (2015): 2425-2429

Packing and Unpacking Layers

By default, the transforms work with coefficients layed out in compressed form along a single dimension, e.g. \((l, m) \rightarrow (0, 0), (1, 0), (1, 1), (2, 0), \dots\). However, some users would rather work with a more convernient two dimension \((l, m)\) matrix, even though it is less memory efficient. tensossht provides a packing layer PackingLayer and an unpacking layer UnpackingLayer to mediate between the two formats. The packing layer should be used before the inverse transform layer. The unpacking layer should be used after the forward transform layer.

To illustrate their use, we will first pack the labels \((l, m)\) themselves. And then unpack them in due course.

Using a fill-value of 100, the unpacked \((l, m)\) labels can be constructed as:

lmax, mmin = 5, 0
coefficients = tf.constant(
    [
        [
            [order if order >= abs(m) else 100 for m in range(mmin, lmax)]
            for order in range(lmax)
        ],
        [
            [m if order >= abs(m) else 100 for m in range(mmin, lmax)]
            for order in range(lmax)
        ],
    ]
)
>>> coefficients
<tf.Tensor: shape=(2, 5, 5), dtype=int32, numpy=
array([[[  0, 100, 100, 100, 100],
        [  1,   1, 100, 100, 100],
        [  2,   2,   2, 100, 100],
        [  3,   3,   3,   3, 100],
        [  4,   4,   4,   4,   4]],

       [[  0, 100, 100, 100, 100],
        [  0,   1, 100, 100, 100],
        [  0,   1,   2, 100, 100],
        [  0,   1,   2,   3, 100],
        [  0,   1,   2,   3,   4]]], dtype=int32)>

The first 2-matrix contains \(l\), and the second contains \(m\).

We now create the layer and attendant model:

from tensossht import PackingLayer

inputs = tf.keras.layers.Input(coefficients.shape[1:], dtype=coefficients.dtype)
packing = PackingLayer(is_real=True, dtype=coefficients.dtype)(inputs)
packing_model = tf.keras.models.Model(inputs=inputs, outputs=packing)

We can verify that applying the layer to the coefficients \((l, m)\) returns the packed labels computed by legendre_labels():

from pytest import approx
from tensossht import legendre_labels

y = packing_model(coefficients)
labels = legendre_labels(lmax=lmax, mmin=mmin)
assert y.shape == (2, ((lmax * (lmax + 1)) // 2))
assert y.numpy() == approx(labels.numpy())

The unpacking layer Unpacking takes the journeyback from the \((l, m)\) matrix to the compressed coefficient form:

from tensossht import UnpackingLayer
inputs = tf.keras.layers.Input(labels.shape[1:], dtype=labels.dtype)
unpacking = UnpackingLayer(
    is_real=True, dtype=labels.dtype, fill_value=100, l_dim=-2, m_dim=-1
)(inputs)
unpacking_model = tf.keras.models.Model(inputs=inputs, outputs=unpacking)

y = unpacking_model(labels)
assert y.shape == (2, lmax, lmax - mmin)
assert y.numpy() == approx(np.array(coefficients))

Both layers can be serialized:

from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as directory:
    packing_model.save(str(Path(directory) / "packing"))
    pckmod = tf.keras.models.load_model(str(Path(directory) / "packing"))

    unpacking_model.save(str(Path(directory) / "unpacking"))
    upckmod = tf.keras.models.load_model(str(Path(directory) / "unpacking"))

assert pckmod(coefficients).numpy() == approx(labels.numpy())
assert upckmod(labels).numpy() == approx(coefficients.numpy())

API

class tensossht.layers.harmonic_transforms.ForwardLayer(*args, **kwargs)[source]
Parameters:
  • is_real (bool) –

  • sampling (Union[str, ImageSamplingSchemes]) –

  • spin (int) –

  • dtype (Union[str, dtype, DType]) –

  • theta_dim (int) –

  • phi_dim (int) –

  • coeff_dim (int) –

build(shape)[source]

Creates the variables of the layer (optional, for subclass implementers).

This is a method that implementers of subclasses of Layer or Model can override if they need a state-creation step in-between layer instantiation and layer call. It is invoked automatically before the first execution of call().

This is typically used to create the weights of Layer subclasses (at the discretion of the subclass implementer).

Parameters:
  • input_shape – Instance of TensorShape, or list of instances of TensorShape if the layer expects a list of inputs (one instance per input).

  • shape (TensorShape) –

Return type:

None

class tensossht.layers.harmonic_transforms.ForwardSpinLayer(*args, **kwargs)[source]
Parameters:
  • sampling (Union[str, ImageSamplingSchemes]) –

  • dtype (Union[str, dtype, DType]) –

  • spin_dim (int) –

  • theta_dim (int) –

  • phi_dim (int) –

  • coeff_dim (int) –

  • out_spin_dim (int) –

  • spin (int) –

build(shape)[source]

Creates the variables of the layer (optional, for subclass implementers).

This is a method that implementers of subclasses of Layer or Model can override if they need a state-creation step in-between layer instantiation and layer call. It is invoked automatically before the first execution of call().

This is typically used to create the weights of Layer subclasses (at the discretion of the subclass implementer).

Parameters:
  • input_shape – Instance of TensorShape, or list of instances of TensorShape if the layer expects a list of inputs (one instance per input).

  • shape (TensorShape) –

Return type:

None

harmonic_sampling(shape)[source]

Figures harmonic sampling from shape of the input tensor.

Parameters:

shape (TensorShape) –

Return type:

HarmonicSampling

class tensossht.layers.harmonic_transforms.InverseLayer(*args, **kwargs)[source]
Parameters:
  • is_real (bool) –

  • sampling (Union[str, ImageSamplingSchemes, ImageSamplingBase]) –

  • spin (int) –

  • dtype (Union[str, dtype, DType]) –

  • theta_dim (int) –

  • phi_dim (int) –

  • coeff_dim (int) –

build(shape)[source]

Creates the variables of the layer (optional, for subclass implementers).

This is a method that implementers of subclasses of Layer or Model can override if they need a state-creation step in-between layer instantiation and layer call. It is invoked automatically before the first execution of call().

This is typically used to create the weights of Layer subclasses (at the discretion of the subclass implementer).

Parameters:
  • input_shape – Instance of TensorShape, or list of instances of TensorShape if the layer expects a list of inputs (one instance per input).

  • shape (TensorShape) –

Return type:

None

class tensossht.layers.harmonic_transforms.InverseSpinLayer(*args, **kwargs)[source]
Parameters:
  • sampling (Union[str, ImageSamplingSchemes]) –

  • dtype (Union[str, dtype, DType]) –

  • spin_dim (int) –

  • coeff_dim (int) –

  • out_spin_dim (int) –

  • theta_dim (int) –

  • phi_dim (int) –

  • spin (int) –

build(shape)[source]

Creates the variables of the layer (optional, for subclass implementers).

This is a method that implementers of subclasses of Layer or Model can override if they need a state-creation step in-between layer instantiation and layer call. It is invoked automatically before the first execution of call().

This is typically used to create the weights of Layer subclasses (at the discretion of the subclass implementer).

Parameters:
  • input_shape – Instance of TensorShape, or list of instances of TensorShape if the layer expects a list of inputs (one instance per input).

  • shape (TensorShape) –

Return type:

None

compute_output_shape(input_shape)[source]

Computes the output shape of the layer.

This method will cause the layer’s state to be built, if that has not happened before. This requires that the layer will later be used with inputs that match the input shape provided here.

Parameters:

input_shape (TensorShape) – Shape tuple (tuple of integers) or tf.TensorShape, or structure of shape tuples / tf.TensorShape instances (one per output tensor of the layer). Shape tuples can include None for free dimensions, instead of an integer.

Returns:

A tf.TensorShape instance or structure of tf.TensorShape instances.

Return type:

TensorShape

get_config()[source]

Returns the config of the layer.

A layer config is a Python dictionary (serializable) containing the configuration of a layer. The same layer can be reinstantiated later (without its trained weights) from this configuration.

The config of a layer does not include connectivity information, nor the layer class name. These are handled by Network (one layer of abstraction above).

Note that get_config() does not guarantee to return a fresh copy of dict every time it is called. The callers should make a copy of the returned dict if they want to modify it.

Returns:

Python dictionary.

harmonic_sampling(shape)[source]

Figures harmonic sampling from shape of the input tensor.

Parameters:

shape (TensorShape) –

Return type:

HarmonicSampling

tensossht.layers.wigner.wigner_layer(is_forward=True, is_real=False, is_odd_spin=True, sampling='mw', theta_dim=-3, phi_dim=-2, gamma_dim=-1, spin_dim=-2, coeff_dim=-1, dtype=tf.float64, **kwargs)[source]

Factory for wigner transform layers.

Parameters:
  • is_forward (bool) – If True, performs image to Wigner space transform. If False, performs Wigner to image space transform.

  • is_real (bool) – If True, the image-space signal is real.

  • is_odd_spin (bool) – Only useful for inverse transforms to real image-space signals. If True, recovers an odd number of spin functions. Otherwise, recovers an even number of spin functions.

  • sampling (Union[str, ImageSamplingSchemes]) – Real-space sampling.

  • theta_dim (int) – Index of the \(\theta\) dimension.

  • phi_dim (int) – Index of the \(\phi\) dimension.

  • gamma_dim (int) – Index of the \(\gamma\) dimension.

  • spin_dim (int) – Index of the spin dimension.

  • coeff_dim (int) – Index of the coefficients dimension.

  • dtype (Union[str, dtype, DType]) – Underlying type of floating point operations. Only the bit-size of the floating point matters. A complex dtype can be given equivalently.

Return type:

Layer

class tensossht.layers.wigner.ForwardWignerLayer(*args, **kwargs)[source]

Performs a forward Wigner transform.

Parameters:
build(input_shape)[source]

Creates the variables of the layer (optional, for subclass implementers).

This is a method that implementers of subclasses of Layer or Model can override if they need a state-creation step in-between layer instantiation and layer call. It is invoked automatically before the first execution of call().

This is typically used to create the weights of Layer subclasses (at the discretion of the subclass implementer).

Parameters:

input_shape – Instance of TensorShape, or list of instances of TensorShape if the layer expects a list of inputs (one instance per input).

class tensossht.layers.wigner.InverseWignerLayer(*args, **kwargs)[source]

Performs an inverse Wigner transform.

Parameters:
build(input_shape)[source]

Creates the variables of the layer (optional, for subclass implementers).

This is a method that implementers of subclasses of Layer or Model can override if they need a state-creation step in-between layer instantiation and layer call. It is invoked automatically before the first execution of call().

This is typically used to create the weights of Layer subclasses (at the discretion of the subclass implementer).

Parameters:

input_shape – Instance of TensorShape, or list of instances of TensorShape if the layer expects a list of inputs (one instance per input).

class tensossht.layers.wigner.FourierLayer(*args, **kwargs)[source]

Performs a Fourier transform.

Performs a forward or inverse Fourier transform. The axis is moved back to the out_axis position, defaulting to last position to avoid unnecessary transforms.

Parameters:
  • is_forward (bool) –

  • is_real (bool) –

  • axis (int) –

  • out_axis (int) –

  • is_odd_spin (Optional[bool]) –

build(input_shape)[source]

Creates the variables of the layer (optional, for subclass implementers).

This is a method that implementers of subclasses of Layer or Model can override if they need a state-creation step in-between layer instantiation and layer call. It is invoked automatically before the first execution of call().

This is typically used to create the weights of Layer subclasses (at the discretion of the subclass implementer).

Parameters:

input_shape (TensorShape) – Instance of TensorShape, or list of instances of TensorShape if the layer expects a list of inputs (one instance per input).

compute_output_shape(input_shape)[source]

Computes the output shape of the layer.

This method will cause the layer’s state to be built, if that has not happened before. This requires that the layer will later be used with inputs that match the input shape provided here.

Parameters:

input_shape (TensorShape) – Shape tuple (tuple of integers) or tf.TensorShape, or structure of shape tuples / tf.TensorShape instances (one per output tensor of the layer). Shape tuples can include None for free dimensions, instead of an integer.

Returns:

A tf.TensorShape instance or structure of tf.TensorShape instances.

Return type:

TensorShape

get_config()[source]

Returns the config of the layer.

A layer config is a Python dictionary (serializable) containing the configuration of a layer. The same layer can be reinstantiated later (without its trained weights) from this configuration.

The config of a layer does not include connectivity information, nor the layer class name. These are handled by Network (one layer of abstraction above).

Note that get_config() does not guarantee to return a fresh copy of dict every time it is called. The callers should make a copy of the returned dict if they want to modify it.

Returns:

Python dictionary.

class tensossht.layers.memory_layout.PackingLayer(*args, **kwargs)[source]

Packs coefficients from (l, m) matrix to memory efficient format.

Parameters:
  • is_real (bool) –

  • coeff_dim (int) –

  • l_dim (int) –

  • m_dim (int) –

build(shape)[source]

Creates the variables of the layer (optional, for subclass implementers).

This is a method that implementers of subclasses of Layer or Model can override if they need a state-creation step in-between layer instantiation and layer call. It is invoked automatically before the first execution of call().

This is typically used to create the weights of Layer subclasses (at the discretion of the subclass implementer).

Parameters:
  • input_shape – Instance of TensorShape, or list of instances of TensorShape if the layer expects a list of inputs (one instance per input).

  • shape (TensorShape) –

call(inputs)[source]

Call layer.

Return type:

Tensor

get_config()[source]

Returns the config of the layer.

A layer config is a Python dictionary (serializable) containing the configuration of a layer. The same layer can be reinstantiated later (without its trained weights) from this configuration.

The config of a layer does not include connectivity information, nor the layer class name. These are handled by Network (one layer of abstraction above).

Note that get_config() does not guarantee to return a fresh copy of dict every time it is called. The callers should make a copy of the returned dict if they want to modify it.

Returns:

Python dictionary.

class tensossht.layers.memory_layout.UnpackingLayer(*args, **kwargs)[source]

Unpacks coefficients to a 2-matrix (l, m).

Parameters:
  • is_real (bool) –

  • coeff_dim (int) –

  • l_dim (int) –

  • m_dim (int) –

  • fill_value (Union[int, float, complex]) –

build(shape)[source]

Creates the variables of the layer (optional, for subclass implementers).

This is a method that implementers of subclasses of Layer or Model can override if they need a state-creation step in-between layer instantiation and layer call. It is invoked automatically before the first execution of call().

This is typically used to create the weights of Layer subclasses (at the discretion of the subclass implementer).

Parameters:
  • input_shape – Instance of TensorShape, or list of instances of TensorShape if the layer expects a list of inputs (one instance per input).

  • shape (TensorShape) –

call(inputs)[source]

Call layer.

Return type:

Tensor

get_config()[source]

Returns the config of the layer.

A layer config is a Python dictionary (serializable) containing the configuration of a layer. The same layer can be reinstantiated later (without its trained weights) from this configuration.

The config of a layer does not include connectivity information, nor the layer class name. These are handled by Network (one layer of abstraction above).

Note that get_config() does not guarantee to return a fresh copy of dict every time it is called. The callers should make a copy of the returned dict if they want to modify it.

Returns:

Python dictionary.