Skip to main content

omnibias.graph.torch.ops.spectral

0.1.0a2AlphaPython >=3.10AGPL / commercial

Spectral graph operators and differentiable relaxations for ordering, assignment, and graph-structured learning.

import omnibias.graph.torch.ops.spectral
Module contract & mathematical conventions

Differentiable spectral graph operators (torch).

All operators take a weighted adjacency matrix A of shape (n, n) (assumed symmetric / undirected, non-negative, zero diagonal) and are differentiable in A. The three Laplacians follow the standard definitions with the isolated-node convention 0 for the inverse-degree factors:

L = D - A, \qquad L_{\mathrm{sym}} = I - D^{-1/2} A D^{-1/2}, \qquad L_{\mathrm{rw}} = I - D^{-1} A,

with D = \operatorname{diag}(A\mathbf 1). Spectral embedding, the heat kernel e^{-tL}, and the Rayleigh-Ritz cut relaxation are built from the symmetric eigendecomposition (torch.linalg.eigh), which is differentiable for matrices with simple spectrum.

Shared formulas do not imply universal floating-point identity. Verify dtype, shape, derivative-order support, and the stated numerical assumptions for the path you use.

CLASS

CutRelaxation​

omnibias.graph.torch.ops.CutRelaxationSource ↗
class CutRelaxation

Dataclass constructor fields; inherited fields, if any, are declared by the base class. A field(...) expression describes a default factory or field options, not a literal argument default.

Rayleigh-Ritz relaxation of the k-way ratio / normalized cut.

Attributes

embedding (n, k) matrix of the k smallest Laplacian eigenvectors -- the continuous relaxation of the cluster-indicator matrix. relaxed_cut_value sum of the k smallest eigenvalues: a lower bound on the discrete minimum cut (the relaxation only *loosens* the combinatorial problem). eigenvalues The k smallest eigenvalues (ascending).

Parameters

NameType & defaultContract
embeddingdataclass fieldTensorRequired(n, k) matrix of the k smallest Laplacian eigenvectors -- the continuous relaxation of the cluster-indicator matrix.
relaxed_cut_valuedataclass fieldTensorRequiredsum of the k smallest eigenvalues: a lower bound on the discrete minimum cut (the relaxation only *loosens* the combinatorial problem).
eigenvaluesdataclass fieldTensorRequiredThe k smallest eigenvalues (ascending).

Fields & methods

embeddingfield
embedding: Tensor

(n, k) matrix of the k smallest Laplacian eigenvectors -- the continuous relaxation of the cluster-indicator matrix.

View source ↗
relaxed_cut_valuefield
relaxed_cut_value: Tensor

sum of the k smallest eigenvalues: a lower bound on the discrete minimum cut (the relaxation only *loosens* the combinatorial problem).

View source ↗
eigenvaluesfield
eigenvalues: Tensor

The k smallest eigenvalues (ascending).

View source ↗
Read the implementation
class CutRelaxation:
    """Rayleigh-Ritz relaxation of the ``k``-way ratio / normalized cut.

    Attributes
    ----------
    embedding
        ``(n, k)`` matrix of the ``k`` smallest Laplacian eigenvectors -- the
        continuous relaxation of the cluster-indicator matrix.
    relaxed_cut_value
        ``sum`` of the ``k`` smallest eigenvalues: a lower bound on the discrete
        minimum cut (the relaxation only *loosens* the combinatorial problem).
    eigenvalues
        The ``k`` smallest eigenvalues (ascending).
    """

    embedding: Tensor
    relaxed_cut_value: Tensor
    eigenvalues: Tensor
Other public import paths
  • omnibias.graph.torch.ops.CutRelaxation
  • omnibias.graph.torch.ops.spectral.CutRelaxation
FUNCTION

degree​

omnibias.graph.torch.ops.degreeSource ↗
degree(adjacency: Tensor) -> Tensor

Weighted node degrees d_i = sum_j A_{ij} -> shape (n,).

Parameters

NameType & defaultContract
adjacencypositional-or-keywordTensorRequiredSee the operation contract and implementation below.
ReturnsTensor
Read the implementation
def degree(adjacency: Tensor) -> Tensor:
    r"""Weighted node degrees ``d_i = sum_j A_{ij}`` -> shape ``(n,)``."""
    _check_adjacency(adjacency)
    return adjacency.sum(dim=-1)
Other public import paths
  • omnibias.graph.torch.ops.degree
  • omnibias.graph.torch.ops.spectral.degree
FUNCTION

fiedler_vector​

omnibias.graph.torch.ops.fiedler_vectorSource ↗
fiedler_vector(adjacency: Tensor, *, normalized: bool=False) -> Tensor

The eigenvector of the second-smallest Laplacian eigenvalue -> (n,).

Parameters

NameType & defaultContract
adjacencypositional-or-keywordTensorRequiredSee the operation contract and implementation below.
normalizedkeyword-onlyboolDefault: FalseSee the operation contract and implementation below.
ReturnsTensor
Executed regression · fiedler separates blocks

Recorded coverage confirms this test executed the API body. This example belongs to the pinned upstream test suite. It may use fixtures and imports from its full file. Run it from that checkout with its test dependencies installed.

    def test_fiedler_separates_blocks(self) -> None:
        a = two_block_sbm(5, 1.0, 0.02)
        v = G.fiedler_vector(a).numpy()
        block0, block1 = v[:5], v[5:]
        # each block has a consistent sign; the two blocks have opposite sign
        assert np.all(np.sign(block0) == np.sign(block0[0]))
        assert np.all(np.sign(block1) == np.sign(block1[0]))
        assert np.sign(block0[0]) != np.sign(block1[0])
python -m pytest "packages/omnibias-graph/tests/test_spectral_torch.py::TestSBMFiedler::test_fiedler_separates_blocks" -q
Read the implementation
def fiedler_vector(adjacency: Tensor, *, normalized: bool = False) -> Tensor:
    r"""The eigenvector of the second-smallest Laplacian eigenvalue -> ``(n,)``."""
    _, evecs = laplacian_spectrum(adjacency, normalized=normalized)
    return evecs[:, 1]
Other public import paths
  • omnibias.graph.torch.ops.fiedler_vector
  • omnibias.graph.torch.ops.spectral.fiedler_vector
FUNCTION

graph_heat_kernel​

omnibias.graph.torch.ops.graph_heat_kernelSource ↗
graph_heat_kernel(adjacency: Tensor, t: float, *, normalized: bool=False) -> Tensor

Heat kernel H = exp(-t L) via the symmetric eigendecomposition.

H_{ij} is the amount of heat diffused from node j to node i after time t. For the combinatorial Laplacian each column sums to 1 only in the t -> inf equilibrium; H is symmetric positive-definite for all t >= 0.

Parameters

NameType & defaultContract
adjacencypositional-or-keywordTensorRequiredSee the operation contract and implementation below.
tpositional-or-keywordfloatRequiredSee the operation contract and implementation below.
normalizedkeyword-onlyboolDefault: FalseSee the operation contract and implementation below.
ReturnsTensor
Explicit validation & failure cases
  • When t < 0, raises ValueError(f'diffusion time t must be >= 0; got {t}').

These are explicit source guards. Backend operations may also raise for invalid shapes, dtypes, or unsupported devices.

Executed regression · negative time raises

Recorded coverage confirms this test executed the API body. This example belongs to the pinned upstream test suite. It may use fixtures and imports from its full file. Run it from that checkout with its test dependencies installed.

    def test_negative_time_raises(self) -> None:
        with pytest.raises(ValueError, match="t must be"):
            G.graph_heat_kernel(ring_adjacency(4), -0.1)
python -m pytest "packages/omnibias-graph/tests/test_spectral_torch.py::TestValidation::test_negative_time_raises" -q
Regression example · heat kernel parity

This example belongs to the pinned upstream test suite. It may use fixtures and imports from its full file. Run it from that checkout with its test dependencies installed.

def test_heat_kernel_parity(t: float) -> None:
    m = _sym_adjacency(5)
    _assert_close(GT.graph_heat_kernel(torch.tensor(m), t), GJ.graph_heat_kernel(jnp.asarray(m), t))
python -m pytest packages/omnibias-graph/tests/test_cross_backend.py -k test_heat_kernel_parity -q
Read the implementation
def graph_heat_kernel(
    adjacency: Tensor, t: float, *, normalized: bool = False
) -> Tensor:
    r"""Heat kernel ``H = exp(-t L)`` via the symmetric eigendecomposition.

    ``H_{ij}`` is the amount of heat diffused from node ``j`` to node ``i`` after
    time ``t``. For the combinatorial Laplacian each column sums to ``1`` only in
    the ``t -> inf`` equilibrium; ``H`` is symmetric positive-definite for all
    ``t >= 0``.
    """
    if t < 0:
        raise ValueError(f"diffusion time t must be >= 0; got {t}")
    evals, evecs = laplacian_spectrum(adjacency, normalized=normalized)
    damp = torch.exp(-t * evals)
    return (evecs * damp.unsqueeze(-2)) @ evecs.transpose(-1, -2)
Other public import paths
  • omnibias.graph.torch.ops.graph_heat_kernel
  • omnibias.graph.torch.ops.spectral.graph_heat_kernel
FUNCTION

graph_laplacian​

omnibias.graph.torch.ops.graph_laplacianSource ↗
graph_laplacian(adjacency: Tensor) -> Tensor

Combinatorial Laplacian L = D - A.

Parameters

NameType & defaultContract
adjacencypositional-or-keywordTensorRequiredSee the operation contract and implementation below.
ReturnsTensor
Executed regression · non square raises

Recorded coverage confirms this test executed the API body. This example belongs to the pinned upstream test suite. It may use fixtures and imports from its full file. Run it from that checkout with its test dependencies installed.

    def test_non_square_raises(self) -> None:
        with pytest.raises(ValueError, match="square"):
            G.graph_laplacian(torch.zeros(3, 4))
python -m pytest "packages/omnibias-graph/tests/test_spectral_torch.py::TestValidation::test_non_square_raises" -q
Regression example · graph laplacian definition

This example belongs to the pinned upstream test suite. It may use fixtures and imports from its full file. Run it from that checkout with its test dependencies installed.

    def test_graph_laplacian_definition(self) -> None:
        a = two_block_sbm(3, 1.0, 0.1)
        lap = G.graph_laplacian(a)
        d = a.sum(dim=-1)
        assert torch.allclose(torch.diag(lap), d)
        # off-diagonal is -A
        off = lap - torch.diag(torch.diag(lap))
        assert torch.allclose(off, -a + torch.diag(torch.diag(a)))
        # rows of the combinatorial Laplacian sum to zero
        assert torch.allclose(lap.sum(dim=-1), torch.zeros(6), atol=1e-12)
python -m pytest packages/omnibias-graph/tests/test_spectral_torch.py -k test_graph_laplacian_definition -q
Read the implementation
def graph_laplacian(adjacency: Tensor) -> Tensor:
    r"""Combinatorial Laplacian ``L = D - A``."""
    _check_adjacency(adjacency)
    d = adjacency.sum(dim=-1)
    return torch.diag(d) - adjacency
Other public import paths
  • omnibias.graph.torch.ops.graph_laplacian
  • omnibias.graph.torch.ops.spectral.graph_laplacian
FUNCTION

laplacian_spectrum​

omnibias.graph.torch.ops.laplacian_spectrumSource ↗
laplacian_spectrum(adjacency: Tensor, *, normalized: bool=False) -> tuple[Tensor, Tensor]

Ascending eigenvalues / eigenvectors of the (symmetric) Laplacian.

Returns (eigenvalues (n,), eigenvectors (n, n)) with columns as eigenvectors, sorted by ascending eigenvalue (torch.linalg.eigh).

Parameters

NameType & defaultContract
adjacencypositional-or-keywordTensorRequiredSee the operation contract and implementation below.
normalizedkeyword-onlyboolDefault: FalseSee the operation contract and implementation below.
Returnstuple[Tensor, Tensor]
Executed regression · heat kernel parity

Recorded coverage confirms this test executed the API body. This example belongs to the pinned upstream test suite. It may use fixtures and imports from its full file. Run it from that checkout with its test dependencies installed.

def test_heat_kernel_parity(t: float) -> None:
    m = _sym_adjacency(5)
    _assert_close(GT.graph_heat_kernel(torch.tensor(m), t), GJ.graph_heat_kernel(jnp.asarray(m), t))
python -m pytest "packages/omnibias-graph/tests/test_cross_backend.py::test_heat_kernel_parity[0.1]" -q
Regression example · eigenvalue parity

This example belongs to the pinned upstream test suite. It may use fixtures and imports from its full file. Run it from that checkout with its test dependencies installed.

def test_eigenvalue_parity(normalized: bool) -> None:
    m = _sym_adjacency(3)
    et, _ = GT.laplacian_spectrum(torch.tensor(m), normalized=normalized)
    ej, _ = GJ.laplacian_spectrum(jnp.asarray(m), normalized=normalized)
    _assert_close(et, ej)
python -m pytest packages/omnibias-graph/tests/test_cross_backend.py -k test_eigenvalue_parity -q
Read the implementation
def laplacian_spectrum(
    adjacency: Tensor, *, normalized: bool = False
) -> tuple[Tensor, Tensor]:
    r"""Ascending eigenvalues / eigenvectors of the (symmetric) Laplacian.

    Returns ``(eigenvalues (n,), eigenvectors (n, n))`` with columns as
    eigenvectors, sorted by ascending eigenvalue (`torch.linalg.eigh`).
    """
    lap = _symmetric_laplacian(adjacency, normalized=normalized)
    lap = 0.5 * (lap + lap.transpose(-1, -2))
    evals, evecs = torch.linalg.eigh(lap)
    return evals, evecs
Other public import paths
  • omnibias.graph.torch.ops.laplacian_spectrum
  • omnibias.graph.torch.ops.spectral.laplacian_spectrum
FUNCTION

normalized_laplacian​

omnibias.graph.torch.ops.normalized_laplacianSource ↗
normalized_laplacian(adjacency: Tensor) -> Tensor

Symmetric normalized Laplacian L_sym = I - D^{-1/2} A D^{-1/2}.

Isolated nodes (d_i = 0) contribute 0 to the normalization. The spectrum lies in [0, 2].

Parameters

NameType & defaultContract
adjacencypositional-or-keywordTensorRequiredSee the operation contract and implementation below.
ReturnsTensor
Executed regression · laplacian parity

Recorded coverage confirms this test executed the API body. This example belongs to the pinned upstream test suite. It may use fixtures and imports from its full file. Run it from that checkout with its test dependencies installed.

def test_laplacian_parity(seed: int, name: str) -> None:
    m = _sym_adjacency(seed)
    _assert_close(getattr(GT, name)(torch.tensor(m)), getattr(GJ, name)(jnp.asarray(m)))
python -m pytest "packages/omnibias-graph/tests/test_cross_backend.py::test_laplacian_parity[normalized_laplacian-0]" -q
Regression example · normalized spectrum in 0 2

This example belongs to the pinned upstream test suite. It may use fixtures and imports from its full file. Run it from that checkout with its test dependencies installed.

    def test_normalized_spectrum_in_0_2(self) -> None:
        a = ring_adjacency(10)
        ln = G.normalized_laplacian(a)
        ev = torch.linalg.eigvalsh(ln)
        assert float(ev.min()) > -1e-10
        assert float(ev.max()) < 2.0 + 1e-10
        # smallest eigenvalue is 0 (connected graph)
        assert abs(float(ev.min())) < 1e-10
python -m pytest packages/omnibias-graph/tests/test_spectral_torch.py -k test_normalized_spectrum_in_0_2 -q
Read the implementation
def normalized_laplacian(adjacency: Tensor) -> Tensor:
    r"""Symmetric normalized Laplacian ``L_sym = I - D^{-1/2} A D^{-1/2}``.

    Isolated nodes (``d_i = 0``) contribute ``0`` to the normalization. The
    spectrum lies in ``[0, 2]``.
    """
    _check_adjacency(adjacency)
    d = adjacency.sum(dim=-1)
    d_inv_sqrt = torch.where(
        d > 0, d.rsqrt(), torch.zeros_like(d)
    )
    a_norm = d_inv_sqrt.unsqueeze(-1) * adjacency * d_inv_sqrt.unsqueeze(-2)
    n = adjacency.shape[0]
    eye = torch.eye(n, dtype=adjacency.dtype, device=adjacency.device)
    return eye - a_norm
Other public import paths
  • omnibias.graph.torch.ops.normalized_laplacian
  • omnibias.graph.torch.ops.spectral.normalized_laplacian
FUNCTION

random_walk_laplacian​

omnibias.graph.torch.ops.random_walk_laplacianSource ↗
random_walk_laplacian(adjacency: Tensor) -> Tensor

Random-walk Laplacian L_rw = I - D^{-1} A (row-stochastic transition).

Not symmetric in general; isolated nodes contribute 0.

Parameters

NameType & defaultContract
adjacencypositional-or-keywordTensorRequiredSee the operation contract and implementation below.
ReturnsTensor
Executed regression · laplacian parity

Recorded coverage confirms this test executed the API body. This example belongs to the pinned upstream test suite. It may use fixtures and imports from its full file. Run it from that checkout with its test dependencies installed.

def test_laplacian_parity(seed: int, name: str) -> None:
    m = _sym_adjacency(seed)
    _assert_close(getattr(GT, name)(torch.tensor(m)), getattr(GJ, name)(jnp.asarray(m)))
python -m pytest "packages/omnibias-graph/tests/test_cross_backend.py::test_laplacian_parity[random_walk_laplacian-0]" -q
Regression example · random walk rows of transition sum to one

This example belongs to the pinned upstream test suite. It may use fixtures and imports from its full file. Run it from that checkout with its test dependencies installed.

    def test_random_walk_rows_of_transition_sum_to_one(self) -> None:
        a = two_block_sbm(3, 1.0, 0.2)
        lrw = G.random_walk_laplacian(a)
        # L_rw = I - P, so P = I - L_rw has rows summing to 1
        n = a.shape[0]
        p = torch.eye(n) - lrw
        assert torch.allclose(p.sum(dim=-1), torch.ones(n), atol=1e-12)
python -m pytest packages/omnibias-graph/tests/test_spectral_torch.py -k test_random_walk_rows_of_transition_sum_to_one -q
Read the implementation
def random_walk_laplacian(adjacency: Tensor) -> Tensor:
    r"""Random-walk Laplacian ``L_rw = I - D^{-1} A`` (row-stochastic transition).

    Not symmetric in general; isolated nodes contribute ``0``.
    """
    _check_adjacency(adjacency)
    d = adjacency.sum(dim=-1)
    d_inv = torch.where(d > 0, d.reciprocal(), torch.zeros_like(d))
    n = adjacency.shape[0]
    eye = torch.eye(n, dtype=adjacency.dtype, device=adjacency.device)
    return eye - d_inv.unsqueeze(-1) * adjacency
Other public import paths
  • omnibias.graph.torch.ops.random_walk_laplacian
  • omnibias.graph.torch.ops.spectral.random_walk_laplacian
FUNCTION

spectral_clustering_relaxation​

omnibias.graph.torch.ops.spectral_clustering_relaxationSource ↗
spectral_clustering_relaxation(adjacency: Tensor, k: int, *, normalized: bool=True) -> CutRelaxation

Continuous eigenvector relaxation of the k-way graph cut.

The discrete balanced k-cut is NP-hard; its standard convex *relaxation* min_Y tr(Y^T L Y) s.t. Y^T Y = I is solved exactly by the k smallest Laplacian eigenvectors (Rayleigh-Ritz). This returns that relaxation only -- the discrete rounding step (k-means on the embedding) is **not** performed and exact balanced partitioning is out of scope (see the graph-limitation cookbook).

Parameters

NameType & defaultContract
adjacencypositional-or-keywordTensorRequiredSee the operation contract and implementation below.
kpositional-or-keywordintRequiredSee the operation contract and implementation below.
normalizedkeyword-onlyboolDefault: TrueSee the operation contract and implementation below.
ReturnsCutRelaxation
Explicit validation & failure cases
  • When k < 1, raises ValueError(f'k must be >= 1; got {k}').
  • When k > adjacency.shape[0], raises ValueError(f'k={k} exceeds the number of nodes {adjacency.shape[0]}').

These are explicit source guards. Backend operations may also raise for invalid shapes, dtypes, or unsupported devices.

Executed regression · bad k raises

Recorded coverage confirms this test executed the API body. This example belongs to the pinned upstream test suite. It may use fixtures and imports from its full file. Run it from that checkout with its test dependencies installed.

    def test_bad_k_raises(self) -> None:
        with pytest.raises(ValueError, match="exceeds"):
            G.spectral_clustering_relaxation(ring_adjacency(4), k=5)
python -m pytest "packages/omnibias-graph/tests/test_spectral_torch.py::TestValidation::test_bad_k_raises" -q
Regression example · relaxed value lower bounds are nonnegative

This example belongs to the pinned upstream test suite. It may use fixtures and imports from its full file. Run it from that checkout with its test dependencies installed.

    def test_relaxed_value_lower_bounds_are_nonnegative(self) -> None:
        a = ring_adjacency(9)
        rel = G.spectral_clustering_relaxation(a, k=3, normalized=True)
        assert float(rel.relaxed_cut_value) >= -1e-12
python -m pytest packages/omnibias-graph/tests/test_spectral_torch.py -k test_relaxed_value_lower_bounds_are_nonnegative -q
Read the implementation
def spectral_clustering_relaxation(
    adjacency: Tensor, k: int, *, normalized: bool = True
) -> CutRelaxation:
    r"""Continuous eigenvector relaxation of the ``k``-way graph cut.

    The discrete balanced ``k``-cut is NP-hard; its standard convex *relaxation*
    ``min_Y tr(Y^T L Y)`` s.t. ``Y^T Y = I`` is solved exactly by the ``k``
    smallest Laplacian eigenvectors (Rayleigh-Ritz). This returns that relaxation
    only -- the discrete rounding step (k-means on the embedding) is **not**
    performed and exact balanced partitioning is out of scope (see the
    graph-limitation cookbook).
    """
    if k < 1:
        raise ValueError(f"k must be >= 1; got {k}")
    if k > adjacency.shape[0]:
        raise ValueError(f"k={k} exceeds the number of nodes {adjacency.shape[0]}")
    evals, evecs = laplacian_spectrum(adjacency, normalized=normalized)
    return CutRelaxation(
        embedding=evecs[:, :k],
        relaxed_cut_value=evals[:k].sum(),
        eigenvalues=evals[:k],
    )
Other public import paths
  • omnibias.graph.torch.ops.spectral_clustering_relaxation
  • omnibias.graph.torch.ops.spectral.spectral_clustering_relaxation
FUNCTION

spectral_embedding​

omnibias.graph.torch.ops.spectral_embeddingSource ↗
spectral_embedding(adjacency: Tensor, n_components: int, *, normalized: bool=True, drop_first: bool=True) -> Tensor

Laplacian-eigenmaps embedding: the smallest non-trivial eigenvectors.

Returns an (n, n_components) matrix whose columns are the eigenvectors of the Laplacian for the smallest eigenvalues. With drop_first=True the (constant, eigenvalue-0) null vector of a connected graph is skipped so the first returned column is the **Fiedler vector**.

Parameters

NameType & defaultContract
adjacencypositional-or-keywordTensorRequiredSee the operation contract and implementation below.
n_componentspositional-or-keywordintRequiredSee the operation contract and implementation below.
normalizedkeyword-onlyboolDefault: TrueSee the operation contract and implementation below.
drop_firstkeyword-onlyboolDefault: TrueSee the operation contract and implementation below.
ReturnsTensor
Explicit validation & failure cases
  • When n_components < 1, raises ValueError(f'n_components must be >= 1; got {n_components}').
  • When start + n_components > n, raises ValueError(f'cannot take {n_components} components (drop_first={drop_first}) from an {n}-node graph').

These are explicit source guards. Backend operations may also raise for invalid shapes, dtypes, or unsupported devices.

Executed regression · too many components raises

Recorded coverage confirms this test executed the API body. This example belongs to the pinned upstream test suite. It may use fixtures and imports from its full file. Run it from that checkout with its test dependencies installed.

    def test_too_many_components_raises(self) -> None:
        with pytest.raises(ValueError, match="cannot take"):
            G.spectral_embedding(ring_adjacency(3), n_components=3, drop_first=True)
python -m pytest "packages/omnibias-graph/tests/test_spectral_torch.py::TestValidation::test_too_many_components_raises" -q
Regression example · embedding shape and drop first

This example belongs to the pinned upstream test suite. It may use fixtures and imports from its full file. Run it from that checkout with its test dependencies installed.

    def test_embedding_shape_and_drop_first(self) -> None:
        a = two_block_sbm(4, 1.0, 0.05)
        emb = G.spectral_embedding(a, n_components=2, normalized=True)
        assert emb.shape == (8, 2)
        # with drop_first the constant null vector is excluded: columns are
        # orthogonal to the all-ones vector up to normalization scale
        col0 = emb[:, 0]
        assert abs(float(col0.sum())) < 1e-6
python -m pytest packages/omnibias-graph/tests/test_spectral_torch.py -k test_embedding_shape_and_drop_first -q
Read the implementation
def spectral_embedding(
    adjacency: Tensor,
    n_components: int,
    *,
    normalized: bool = True,
    drop_first: bool = True,
) -> Tensor:
    r"""Laplacian-eigenmaps embedding: the smallest non-trivial eigenvectors.

    Returns an ``(n, n_components)`` matrix whose columns are the eigenvectors of
    the Laplacian for the smallest eigenvalues. With ``drop_first=True`` the
    (constant, eigenvalue-``0``) null vector of a connected graph is skipped so
    the first returned column is the **Fiedler vector**.
    """
    if n_components < 1:
        raise ValueError(f"n_components must be >= 1; got {n_components}")
    n = adjacency.shape[0]
    start = 1 if drop_first else 0
    if start + n_components > n:
        raise ValueError(
            f"cannot take {n_components} components (drop_first={drop_first}) "
            f"from an {n}-node graph"
        )
    _, evecs = laplacian_spectrum(adjacency, normalized=normalized)
    return evecs[:, start : start + n_components]
Other public import paths
  • omnibias.graph.torch.ops.spectral_embedding
  • omnibias.graph.torch.ops.spectral.spectral_embedding