Image and Harmonic Space Samplings

This modules contains helpers to define the different harmonic-space and image space samplings.

Creating Samplings

tensossht.sampling.equiangular(lmax, dtype=tf.float64)[source]

Equiangular samples.

Example

>>> from tensossht.sampling import equiangular
>>> equiangular(3).numpy().round(2)
array([[[0.63, 0.63, 0.63, 0.63, 0.63],
        [1.88, 1.88, 1.88, 1.88, 1.88],
        [3.14, 3.14, 3.14, 3.14, 3.14]],

       [[0.  , 1.26, 2.51, 3.77, 5.03],
        [0.  , 1.26, 2.51, 3.77, 5.03],
        [0.  , 1.26, 2.51, 3.77, 5.03]]])
Parameters:
  • lmax (int) –

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

Return type:

ndarray

tensossht.sampling.image_sampling_scheme(sampling)[source]
tensossht.sampling.image_sampling_scheme(sampling)
tensossht.sampling.image_sampling_scheme(sampling)
tensossht.sampling.image_sampling_scheme(sampling)
Return type:

ImageSamplingSchemes

tensossht.sampling.harmonic_sampling_scheme(lmax=None, lmin=None, mmax=None, mmin=None, smax=None, smin=None, spin=None, labels=None, compact_spin=None)[source]

Basis functions in harmonic space.

Parameters:
  • lmax (Optional[Union[int, ndarray, HarmonicSampling]]) – Maximum degree, \(l < l_\mathrm{max}\). For practical purposes, the first argument of the function can also be the 3 by n tensor labels, or a HarmonicSampling instance.

  • lmin (Optional[int]) – minimum degree, \(l \geq l_\mathrm{min}\)

  • mmax (Optional[int]) – Maximum order, \(m \leq m_\mathrm{max}\)

  • mmin (Optional[int]) – Maximum order, \(m \geq m_\mathrm{min}\)

  • smax (Optional[int]) – Maximum spin, \(s \leq s_\mathrm{max}\)

  • smin (Optional[int]) – Maximum spin, \(s \geq s_\mathrm{min}\)

  • spin (Optional[int]) – shortcut for \(s = s_\mathrm{min} = s_\mathrm{max}\)

  • labels (Optional[ndarray]) – 3 by n tensor listing the \((l, m, s)\) triplets. Alternative to specifying lmax and friends.

  • compact_spin (Optional[bool]) – If True, the representation will be memory efficient. If False, the representation is sucht the spin dimension can be separated from the other coefficients. The latter is generally more practical, especially for for smaller smax. Ignored if labels are given.

Return type:

HarmonicSampling

tensossht.sampling.wignerd_labels(lmax, lmin=0, mmax=None, mmin=None, mpmin=None, mpmax=None)[source]

tensor with all (l, m, m’) constrained by the input arguments

More specifically, we compute all values:

\[\left\{ (l, m, m'); l \in [l_\text{min}, l_\text{max}[, m \in [m_\text{min}, m_\text{max}] \cap [-l, l] m' \in [m'_\text{min}, m'_\text{max}] \cap [-l, l] \right\}\]

Generates a tensor with all \(l, m, m'\) as per requirements. The first row corresponds to \(l\), the second to \(m\), and the third to \(m'\).

Example

>>> from tensossht import wignerd_labels
>>> labels = wignerd_labels(lmax=6, lmin=3, mmax=2, mmin=-1, mpmax=1, mpmin=-2)
>>> labels
<tf.Tensor: shape=(3, 48), dtype=int32, numpy=
array([[ 3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,
         4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
         5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5],
       [-1, -1, -1, -1,  0,  0,  0,  0,  1,  1,  1,  1,  2,  2,  2,  2,
        -1, -1, -1, -1,  0,  0,  0,  0,  1,  1,  1,  1,  2,  2,  2,  2,
        -1, -1, -1, -1,  0,  0,  0,  0,  1,  1,  1,  1,  2,  2,  2,  2],
       [-2, -1,  0,  1, -2, -1,  0,  1, -2, -1,  0,  1, -2, -1,  0,  1,
        -2, -1,  0,  1, -2, -1,  0,  1, -2, -1,  0,  1, -2, -1,  0,  1,
        -2, -1,  0,  1, -2, -1,  0,  1, -2, -1,  0,  1, -2, -1,  0,  1]],
      dtype=int32)>
Parameters:
  • lmax (int) –

  • lmin (int) –

  • mmax (Optional[int]) –

  • mmin (Optional[int]) –

  • mpmin (Optional[int]) –

  • mpmax (Optional[int]) –

Return type:

ndarray

tensossht.sampling.symmetric_labels(labels, dtype=tf.int32)[source]

Symmetrize wigner-d (l, m, m’) labels so m >= m’ >= 0.

Returns a tuple with the symetrized labels and the sign factor.

Example

First we create the labels:

>>> from tensossht import wignerd_labels
>>> from tensossht.sampling import symmetric_labels
>>> labels = wignerd_labels(8)
>>> symlabs, factors = symmetric_labels(labels)

The we can verify the labels have been symmtrized:

>>> assert tf.reduce_all(symlabs[1] >= symlabs[-1])
>>> assert tf.reduce_all(symlabs[-1] >= 0)

We can check that the wigner-ds are equal to a factor, using the naive multi-precision implementation:

>>> from pytest import approx
>>> from tensossht.specialfunctions.naive import wignerd
>>> for i in range(labels.shape[1]):
...     expected = wignerd(*labels[:, i].numpy())
...     actual = factors[i].numpy() * wignerd(*symlabs[:, i].numpy())
...     assert actual == approx(expected)
Parameters:
  • labels (ndarray) –

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

Return type:

Tuple[ndarray, ndarray]

tensossht.sampling.spin_legendre_labels(lmax, lmin=0, mmax=None, mmin=0, smax=None, smin=0, dtype=tf.int32, compact_spin=True)[source]

tensor with all (l, m, s) constrained by lmin, lmax and mmin, mmax, smin, smax.

More specifically, we compute all values:

\[\left\{ (l, m, s); s \in [s_\text{min}, s_\text{max}] l \in [l_\text{min}, l_\text{max}[,\ l \geq |s|, m \in [m_\text{min}, m_\text{max}],\ -l \geq m \geq l \right\}\]

Generates a tensor with all ls and ms, as per requirements. All pairs (l, m) for a given s are contiguous, e.g. s` is the outermost index. ``m is the innermost index, e.g. the most rapidly changing. This setup implies we could expand the labels into a 2-dimensional ragged tensor s vs (l, m).

If compact_spin is True then the labels are arranged such that memory is optimized. If it false, then the tensor can be reshaped so that the spins are in a separate dimension.

Example

The followin illustrates a compact label representation.

>>> from tensossht import spin_legendre_labels
>>> labels = spin_legendre_labels(lmax=3, smin=-1, smax=1)
>>> labels
<tf.Tensor: shape=(3, 16), dtype=int32, numpy=
array([[ 1,  1,  2,  2,  2,  0,  1,  1,  2,  2,  2,  1,  1,  2,  2,  2],
       [ 0,  1,  0,  1,  2,  0,  0,  1,  0,  1,  2,  0,  1,  0,  1,  2],
       [-1, -1, -1, -1, -1,  0,  0,  0,  0,  0,  0,  1,  1,  1,  1,  1]],
      dtype=int32)>
>>> l, m, s = labels
>>> l.numpy()
array([1, 1, 2, 2, 2, 0, 1, 1, 2, 2, 2, 1, 1, 2, 2, 2], dtype=int32)

However, a less compact representation is available:

>>> labels = spin_legendre_labels(lmax=3, smin=-1, smax=1, compact_spin=False)
>>> tf.reshape(labels, (3, 3, -1))
<tf.Tensor: shape=(3, 3, 6), dtype=int32, numpy=
array([[[ 0,  1,  1,  2,  2,  2],
        [ 0,  1,  1,  2,  2,  2],
        [ 0,  1,  1,  2,  2,  2]],

       [[ 0,  0,  1,  0,  1,  2],
        [ 0,  0,  1,  0,  1,  2],
        [ 0,  0,  1,  0,  1,  2]],

       [[-1, -1, -1, -1, -1, -1],
        [ 0,  0,  0,  0,  0,  0],
        [ 1,  1,  1,  1,  1,  1]]], dtype=int32)>
Parameters:
  • lmax (int) –

  • lmin (int) –

  • mmax (Optional[int]) –

  • mmin (Optional[int]) –

  • smax (Optional[int]) –

  • smin (Optional[int]) –

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

  • compact_spin (bool) –

Return type:

ndarray

tensossht.sampling.legendre_labels(lmax, lmin=0, mmax=None, mmin=0)[source]

tensor with all (l, m) constrained by lmin, lmax and mmin, mmax.

More specifically, we compute all values:

\[\left\{ (l, m); l \in [l_\text{min}, l_\text{max}[, m \in [m_\text{min}, m_\text{max}] \cap [-l, l] \right\}\]

Generates a tensor with all ls and ms, as per requirements. ls are first and ms are second as illustrated below.

Example

>>> from tensossht.sampling import legendre_labels
>>> labels = legendre_labels(lmax=6, lmin=3, mmax=2, mmin=1)
>>> labels
<tf.Tensor: shape=(2, 6), dtype=int32, numpy=
array([[3, 3, 4, 4, 5, 5],
       [1, 2, 1, 2, 1, 2]], dtype=int32)>
>>> l, m = labels
>>> l.numpy()
array([3, 3, 4, 4, 5, 5], dtype=int32)
Parameters:
  • lmax (int) –

  • lmin (Optional[int]) –

  • mmax (Optional[int]) –

  • mmin (Optional[int]) –

Return type:

ndarray

Manipulating Samplings

tensossht.sampling.transpose(tensor, axes, *shifted_axes)[source]

Transpose tensor so that given axes are pushed to the end.

Parameters:
  • tensor (ndarray) –

  • axes (Union[ImageAxes, HarmonicAxes]) –

  • shifted_axes (Union[str, Axis, HarmonicAxes, ImageAxes]) –

Return type:

ndarray

tensossht.sampling.equiangular_shape(lmax)[source]

Shape of equiangular samples.

Example

>>> from tensossht.sampling import (
...     equiangular_phi, equiangular_theta, equiangular_shape
... )
>>> len(equiangular_theta(5)) == equiangular_shape(5)[0]
True
>>> len(equiangular_theta(5)) == equiangular_shape(5).theta
True
>>> len(equiangular_phi(5)) == equiangular_shape(5)[1]
True
>>> len(equiangular_phi(5)) == equiangular_shape(5).phi
True
Parameters:

lmax (int) –

Return type:

EquiangularShape

Data structures

class tensossht.sampling.HarmonicAxes(coeff, spin=None)[source]

Keeps track of the dimensions of an spherical harmonic tensor.

Example

Harmonics axes contain two components corresponding to the coefficient and spin dimensions. Spin can be missing, in which case it is None.

>>> from tensossht.sampling import HarmonicAxes
>>> axes = HarmonicAxes(coeff=-1, spin=-2)
>>> axes
HarmonicAxes(coeff=-1, spin=-2)
>>> axes[Axis.COEFF]
-1
>>> axes["spin"]
-2
>>> axes[1]
-2
>>> axes % 5
HarmonicAxes(coeff=4, spin=3)

One or more axis can be shifted to the end (e.g. most rapidly incrementing dimension in tensorflow):

>>> axes.shift(3, "coeff")
HarmonicAxes(coeff=-1, spin=-2)
>>> axes.shift(3, "spin")
HarmonicAxes(coeff=-2, spin=-1)
>>> axes.shift(3)
HarmonicAxes(coeff=-1, spin=-2)
>>> axes.shift(5, Axis.COEFF, "spin")
HarmonicAxes(coeff=-2, spin=-1)

And a tensor can then be tranposed accordingly:

>>> tensor = tf.zeros((2, 3, 4, 5, 6))
>>> assert axes.transpose(tensor).shape == tensor.shape
>>> assert axes.transpose(tensor, "spin").shape == (2, 3, 4, 6, 5)
>>> assert axes.transpose(tensor, "spin", Axis.COEFF).shape == (2, 3, 4, 5, 6)
>>> assert axes.transpose(tensor, Axis.COEFF, "spin").shape == (2, 3, 4, 6, 5)

It is also possible to compute the permutation to go from one axis ordering to another:

>>> HarmonicAxes(coeff=0).permutation(HarmonicAxes(coeff=-1), ndims=3)
[1, 2, 0]
>>> HarmonicAxes(coeff=1, spin=-1).permutation(
...     HarmonicAxes(coeff=-1, spin=1), ndims=4
... )
[0, 3, 2, 1]
Parameters:
  • coeff (int) –

  • spin (Optional[int]) –

class tensossht.sampling.MW(lmax, dtype=tf.float64)[source]

McEwen - Viaux image-space sampling.

Parameters:
  • lmax (int) –

  • dtype (DType) –

class tensossht.sampling.MWSS(lmax, dtype=tf.float64)[source]

McEwen - Viaux symmetric image-space sampling.

Parameters:
  • lmax (int) –

  • dtype (DType) –

class tensossht.sampling.EquiangularShape(theta, phi)[source]
Parameters:
  • theta (int) –

  • phi (int) –

class tensossht.sampling.ImageSamplingBase(lmax, dtype=tf.float64)[source]
Parameters:
  • lmax (int) –

  • dtype (DType) –