Eigen

The eigenvalues and eigenvectors of a square symmetric matrix, or of its Laplacian.

Eigen py
input
values
vectors
Type name
signal:Eigen
Plane
signal
Tags
analysisconnectivity
Language
python
Tier
in-process
Bundle
signal
Source
node-bundles/signal/eigen.py
Availability
available

Slots

SlotDirectionType
inputinputARRAY
valuesoutputARRAY
vectorsoutputARRAY

Parameters

eigen

NameTypeDefaultRangeDoc
laplacianstringnonenone | unnormalized | normalizedRead the matrix as it is, or as the graph Laplacian built from it.
orderstringdescendingdescending | ascendingWhich end comes first. A Laplacian is usually read the other way round.

common

NameTypeDefaultRangeDoc
autotriggerboolfalseRun on the node's own schedule, instead of waiting for an input frame. Turn this on for sources; leave it off for transforms driven by their input.
max_frequencyfloat00 … 100Rate cap for this node, read through `frequency_mode`. 0 means uncapped — the node runs as often as the scheduler and its inputs allow.
frequency_modestringupdates_per_secondupdates_per_second | seconds_per_updateHow to read `max_frequency`: as a rate in Hz (updates per second), or as a period in seconds between updates — convenient for very slow nodes.

Source

The current source of this node, as it stands in the goofi repository at node-bundles/signal/eigen.py.

node-bundles/signal/eigen.py
"""Eigen — the axes of a square symmetric matrix, strongest first.

A connectivity matrix says how every pair of channels relates; its eigenvectors say which
combinations of channels move together, and the eigenvalues how much each one carries. The
Laplacian modes read the matrix as a graph instead, where the smallest values are the ones that
say how it splits.
"""

import numpy as np
import goofi


class Eigen(goofi.Node):
    """The eigenvalues and eigenvectors of a square symmetric matrix, or of its Laplacian."""

    TAGS = ["analysis", "connectivity"]
    INPUTS = {"input": goofi.InputSlot(goofi.DataType.ARRAY, required=True)}
    OUTPUTS = {"values": goofi.DataType.ARRAY, "vectors": goofi.DataType.ARRAY}
    PARAMS = {
        "eigen": {
            "laplacian": goofi.StringParam(
                "none",
                options=["none", "unnormalized", "normalized"],
                doc="Read the matrix as it is, or as the graph Laplacian built from it.",
            ),
            "order": goofi.StringParam(
                "descending",
                options=["descending", "ascending"],
                doc="Which end comes first. A Laplacian is usually read the other way round.",
            ),
        }
    }

    def process(self, input):
        p = self.params.eigen
        x = np.asarray(input.data, dtype=np.float64)
        if x.ndim != 2 or x.shape[0] != x.shape[1]:
            raise ValueError(f"needs a square matrix, got {list(x.shape)}")
        x = (x + x.T) / 2

        if p.laplacian != "none":
            degree = np.abs(x).sum(axis=1)
            x = np.diag(degree) - x
            if p.laplacian == "normalized":
                scale = 1 / np.sqrt(np.where(degree > 0, degree, 1))
                x = x * scale[:, None] * scale[None, :]

        values, vectors = np.linalg.eigh(x)
        if p.order == "descending":
            values, vectors = values[::-1], vectors[:, ::-1]

        axes = input.meta.get("channels", {})
        return {
            "values": (values.astype(np.float32), {}),
            "vectors": (np.ascontiguousarray(vectors, dtype=np.float32), {"channels": axes}),
        }

← All nodes

This reference describes goofi 3.1.0(537cd394), generated from a running instance on 2026-09-06.