TorchPfaffian

torch_pfaffian.kernels package

Submodules

torch_pfaffian.kernels.det_magnitude module

class torch_pfaffian.kernels.det_magnitude.LogMagnitudePfaffian(*args, **kwargs)

Bases: Function

Differentiable log|Pf(A)| = 0.5 * log|det(A)| using a single factorization in each direction.

The forward computes one torch.linalg.slogdet(); the backward supplies the analytic gradient 0.5 * (A^{-1})^T (Wirtinger-conjugated for complex inputs) through a single non-raising torch.linalg.inv_ex(), instead of differentiating through slogdet. Differentiating through slogdet forms grad * A^{-T}, which is infinite at an exactly-singular input, so even the correct upstream multiplier of 0 would give 0 * inf = NaN. Here exactly-singular elements (det(A) == 0, so slogdet returns sign 0) keep the correct -inf value and receive a zero gradient: the identity is substituted before the inverse so the factorization stays finite, and the gradient is masked to zero there. Numerically singular elements (a round-off det with nonzero sign) are already finite and left unchanged. No host synchronization happens in either direction.

static backward(ctx: BackwardCFunction, grad_output: Tensor) → Tensor | None
static forward(ctx: FunctionCtx, matrix: Tensor) → Tensor
torch_pfaffian.kernels.det_magnitude.log_magnitude_pfaffian(matrix: Tensor) → Tensor

Log-magnitude log|Pf(A)| = 0.5 * log|det(A)| of a batch of skew-symmetric matrices.

The value is computed through torch.linalg.slogdet(), so it works in the log domain and stays finite (and keeps its gradient alive) even for tiny Pfaffians such as probabilities of order 2^{-k} that would underflow the linear-domain sqrt(|det|). The gradient uses the analytic LogMagnitudePfaffian, a single factorization each direction that is NaN-free at exactly-singular inputs (see that class). Odd-dimensional inputs are singular, so the log-magnitude is -inf (returned with an autograd connection so a backward through it yields a zero gradient rather than raising, and without reading the entries so inf / nan inputs stay -inf).

Parameters:

matrix – Skew-symmetric matrices of shape (..., m, m), real or complex floating point.

Returns:

The log-magnitude of shape (...,) in the input’s real floating dtype (real for complex inputs, since a magnitude is real-valued).

Return type:

torch.Tensor

torch_pfaffian.kernels.det_magnitude.magnitude_pfaffian(matrix: Tensor, *, epsilon: float = 0.0) → Tensor

Magnitude |Pf(A)| of a batch of skew-symmetric matrices, real-valued and differentiable.

The magnitude is obtained by exponentiating log_magnitude_pfaffian(). When epsilon is positive the log-magnitude is floored at 0.5 * log(epsilon) before exponentiation, reproducing the historical sqrt(clamp(|det|, epsilon)) floor as a function argument rather than a mutable global. Prefer the default 0.0 together with log_magnitude_pfaffian() for gradient-safe small probabilities.

Parameters:
  • matrix – Skew-symmetric matrices of shape (..., m, m), real or complex floating point.

  • epsilon – When positive, floors the result at sqrt(epsilon). The default 0.0 applies no floor.

Returns:

The magnitude |Pf(A)| of shape (...,) in the input’s real floating dtype.

Return type:

torch.Tensor

torch_pfaffian.kernels.parlett_reid_slog module

torch_pfaffian.kernels.parlett_reid_slog.parlett_reid_slog(matrix: Tensor) → tuple[torch.Tensor, torch.Tensor]

Log-domain signed Pfaffian of a batch of skew-symmetric matrices via Parlett-Reid elimination.

The batched skew-tridiagonalization with partial pivoting is run device-side with no host synchronization (no .item(), .any() or isfinite().all()), and the result is accumulated as (phase, log|Pf|) rather than a raw product. The log domain removes both the overflow at large dimensions and the underflow for tiny Pfaffians (probabilities of order 2^{-k}). Real and complex inputs share the single code path: phase is +-1 (or 0) for real inputs and a unit-modulus complex number for complex inputs. An exact zero pivot column terminates that batch element cleanly with (0, -inf), since its remaining rank-2 updates vanish, so no epsilon threshold is needed.

Parameters:

matrix – Skew-symmetric matrices of shape (..., m, m), real or complex floating point.

Returns:

A pair (phase, log_abs) where phase has shape (...,) and matches the input dtype (with Pf = phase * exp(log_abs), and phase = 0 when Pf = 0), and log_abs has shape (...,) in the matching real dtype (-inf when Pf = 0). Odd m gives (0, -inf) and m = 0 gives (1, 0).

Return type:

tuple[torch.Tensor, torch.Tensor]

torch_pfaffian.kernels.small_n module

torch_pfaffian.kernels.small_n.small_pfaffian(matrix: Tensor) → Tensor

Signed/complex Pfaffian of a batch (..., m, m) for even m <= SMALL_N_MAX via unrolled matchings.

This is the compile-time form of the recursive expansion: the perfect matchings are enumerated once per dimension and evaluated as one batched gather followed by unrolled elementwise products and a matvec with the sign vector, with no elimination loop or data-dependent control flow. The factor product is unrolled (rather than torch.prod()) so that zero entries still get correct product-rule gradients, making the kernel exact and autograd-friendly for the supported sizes.

Parameters:

matrix – Skew-symmetric matrices of shape (..., m, m) with even m <= SMALL_N_MAX.

Returns:

The signed Pfaffian of shape (...,), sharing the input backend, dtype and device.

Return type:

torch.Tensor

Module contents