jgot supports sparse, finite, reversible graphs. The solver stores graphs as
paired directed edges internally, but the user can construct them either from
symmetric weights or from directed rates.
All supported runtime graphs must be:
- finite,
- sparse,
- connected,
- reversible,
- explicitly paired so every positive directed edge has a reverse edge.
The stationary distribution is stored as a 1D array pi, and endpoint
densities are represented with respect to pi.
This constructor treats the input as a symmetric conductance / weight graph. The input is not treated as a pre-built Markov generator.
Given symmetric weights w_xy, the constructor:
- computes the weighted degree
d_x = sum_y w_xy, - sets the stationary distribution
pi(x)proportional tod_x, - stores off-diagonal directed weights as
q(x, y) = w_xy / d_x.
As a result:
- the off-diagonal row sum is
sum_{y != x} q(x, y) = 1, - the undirected constructor builds a unit-exit-rate random walk,
- if interpreted as a full generator, the implied diagonal would be
-1.
This normalization is a modeling choice for the symmetric-input path. It is why symmetric graph weights become a reversible directed edge model internally.
Use this path when you naturally have:
- a symmetric affinity graph,
- a conductance graph,
- a symmetric sparse kernel graph.
This constructor treats the input as off-diagonal directed rates.
That means:
- the input is not interpreted as a row-stochastic transition matrix,
- it is interpreted as the off-diagonal part of a continuous-time jump process,
- the full generator is implicit, with diagonal equal to minus the outgoing row sum.
If pi is omitted:
jgotinfers it under the reversibility assumption using sparse log-ratio propagation.
If reversibility fails:
- graph construction raises
ValueError.
Use this path when you already have a reversible directed rate graph.
If your pipeline comes from diffusion maps, the safe rule is:
- pass the last symmetric positive graph before row-normalization.
That usually means:
- pass the symmetric kernel / affinity matrix, or
- pass the symmetric alpha-normalized kernel,
- but do not pass the row-normalized diffusion kernel as if it were already a generator unless you intentionally want to reinterpret it as rates.
Recommended practice:
- if you still have the symmetric sparse graph, use
from_undirected_weights(...). - if you only have a reversible directed rate model, use
from_directed_rates(...).
In code, pi is stored as a 1D array.
Mathematically, the convention is:
piis the left stationary distribution,- the intended action is
pi @ Q, notQ @ pi.
So the correct mental model is:
piacts as a row vector in the stationary equations,- while
rhois the density with respect topi.
Endpoint densities rho_a and rho_b are not ordinary masses.
They are densities with respect to pi, and they must satisfy:
sum(pi * rho) == 1.
To convert ordinary node masses mass into the solver convention:
rho = mass / pi.
Internally, GraphSpec stores:
src,dst,rev,q,pi,out_rate.
This is a directed edge-list representation, not a dense matrix and not a CSR/COO wrapper class. The solver operates on these arrays directly.