spd_learn.modules.LogEig#

class spd_learn.modules.LogEig(upper=True, flatten=True, autograd=False, device=None, dtype=None)[source]#

Bases: Module

Logarithmic Eigenvalue Layer (LogEig).

This layer maps SPD matrices to the tangent space at the identity via the Riemannian logarithm under the affine-invariant metric [Huang and Van Gool, 2017]. The output is then vectorized.

\[\log(X) = U \log(\Lambda) U^\top\]

where \(X = U \Lambda U^\top\) is the eigendecomposition.

This operation embeds SPD matrices into the tangent space at the identity, which is a vector space, thereby enabling the use of standard Euclidean layers such as linear classifiers or fully connected networks for downstream tasks.

Parameters:
  • upper (bool, default=True) – If True, only the upper triangular part of the matrix is vectorized.

  • flatten (bool, default=True) – If True, the output is flattened.

  • autograd (bool, default=False) – Whether to use the autograd backend.

See also

ExpEig

Inverse operation, maps from tangent space back to manifold.

ReEig

Eigenvalue rectification to ensure numerical stability before LogEig.

BiMap

Bilinear mapping for dimensionality reduction on SPD matrices.

matrix_log()

Functional version of matrix logarithm.

log_euclidean_distance()

Distance computation in log-domain.

Examples

>>> import torch
>>> from spd_learn.modules import LogEig
>>> layer = LogEig(upper=True)
>>> X = torch.randn(2, 4, 4)
>>> X = X @ X.mT + 0.1 * torch.eye(4)  # Make SPD
>>> Y = layer(X)
>>> Y.shape
torch.Size([2, 10])
import torch
import numpy as np
import matplotlib.pyplot as plt
from spd_learn.modules import LogEig

torch.manual_seed(42)

# Create a 4x4 SPD matrix
n = 4
A = torch.randn(n, n)
X = A @ A.T + 0.1 * torch.eye(n)
X = X.unsqueeze(0)

# Apply LogEig
logeig_full = LogEig(upper=False, flatten=False)
logeig_vec = LogEig(upper=True, flatten=True)

log_matrix = logeig_full(X)
log_vector = logeig_vec(X)

fig, axes = plt.subplots(1, 3, figsize=(14, 4))

# Input SPD matrix
ax1 = axes[0]
im1 = ax1.imshow(X[0].numpy(), cmap='RdBu_r', aspect='auto')
ax1.set_title('Input SPD Matrix X')
plt.colorbar(im1, ax=ax1, shrink=0.8)

# Matrix logarithm
ax2 = axes[1]
im2 = ax2.imshow(log_matrix[0].numpy(), cmap='RdBu_r', aspect='auto')
ax2.set_title(r'log(X) (Tangent Space)')
plt.colorbar(im2, ax=ax2, shrink=0.8)

# Vectorized output
ax3 = axes[2]
vec = log_vector[0].numpy()
ax3.bar(range(len(vec)), vec, color='#2ecc71', alpha=0.8)
ax3.set_xlabel('Vector index')
ax3.set_ylabel('Value')
ax3.set_title(f'Vectorized (dim={len(vec)})')
ax3.grid(True, alpha=0.3)

plt.suptitle('LogEig: SPD to Tangent Space Mapping', fontweight='bold')
plt.tight_layout()
plt.show()

(Source code)

../../_images/spd_learn-modules-LogEig-1.png
autograd_: Tensor#
forward(X: Tensor) → Tensor[source]#

Forward pass of the LogEig layer.

Parameters:

X (torch.Tensor) – Input SPD matrix.

Returns:

The vectorized output in the tangent space.

Return type:

torch.Tensor