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 aHarmonicSamplinginstance.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
lmaxand friends.compact_spin (Optional[bool]) – If
True, the representation will be memory efficient. IfFalse, the representation is sucht the spin dimension can be separated from the other coefficients. The latter is generally more practical, especially for for smallersmax. Ignored iflabelsare 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 givensare contiguous, e.g.s` is the outermost index. ``mis the innermost index, e.g. the most rapidly changing. This setup implies we could expand the labels into a 2-dimensional ragged tensorsvs(l, m).If compact_spin is
Truethen 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:
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) –