Skip to content

Measuring State Correlation

lpetronika edited this page Feb 16, 2026 · 3 revisions

Measuring State Correlation & Topology

1. The Mathematical Model (Vector Space)

Every state variable is treated as a vector in {0, 1}⁵⁰. Each dimension represents a single browser paint frame (Tick).

The Dot Product (Coincidence Count)

The dot product counts the number of frames where both variables updated together:

$$A \cdot B = \sum_{i=0}^{n-1} A_i B_i$$

In our binary system, this is just a count of simultaneous pulses.


2. Normalization via Cosine Similarity

To separate structural relationships from raw update frequency, we use Cosine Similarity. Without normalization, a high-frequency animation and a low-frequency toggle could look "synced" just because they overlapped once.

$$similarity = \frac{A \cdot B}{\Vert A \Vert \Vert B \Vert}$$

Where $|A|$ (Magnitude) is the square root of the total update count. Basis calculates this in $O(1)$ time using bit-flip delta logic during the heartbeat, so the math never needs a full buffer scan.

Score What it means
1.0 Identity: Updates are perfectly synchronized across the window.
0.0 Orthogonal: No updates happened in the same frames.

3. The 0.88 Threshold

Basis considers two variables behaviorally redundant (what we call diagnostic "linear dependence") if their similarity exceeds 0.88.

  • Geometric Meaning: This corresponds to an angle of roughly 28° in the 50-dimensional space.
  • Why 0.88? This was found through empirical testing on production codebases, including Excalidraw. It turned out to be the best balance between filtering browser scheduling jitter and still catching real structural synchronization.

4. v0.5.0 Optimization: Amortized Modulo Math

The engine performs Discrete Cross-Correlation by sampling similarity across three temporal planes ($\tau = 0, \pm 1$) to figure out the "Direction of Flow."

To keep this under a millisecond, we use Amortized Modulo math. Instead of doing expensive division inside the hot loop, we pre-normalize the circular offset once:

// O(1) Setup: Normalize the circular shift outside the loop
const baseOffset = ((headB - headA + offset) % L + L) % L;

for (let i = 0; i < L; i++) {
  let iB = i + baseOffset;
  if (iB >= L) iB -= L; // O(n) Execution: Single predictable branch
  // ... dot product math
}

This keeps the inner loop linearized. The CPU can do branch prediction efficiently, and the math stays correct even for negative temporal offsets.


5. Direct Sum Decomposition ($U \oplus W$)

In v0.5.0, the engine goes beyond a flat list of signals and performs a Subspace Audit. We model the state space as a composition of local signals ($U$) and global context signals ($W$).

The architectural ideal is a Direct Sum where the intersection is zero: $U \cap W = \lbrace 0 \rbrace$

  • Rule A (Context Mirroring): If a Local Basis vector ($U$) is found within the span of a Global Subspace ($W$), it gets flagged as a Shadow State. You're storing a local copy of something that already exists in context.
  • Rule B (Duplicate State): If two Local Basis vectors ($U_i, U_j$) overlap, they are managing the same logical dimension. One of them shouldn't exist.

6. Topological Analysis (v0.6.0)

Vector analysis finds correlation. v0.6 adds Graph Theory to find causality. The engine builds a Directed Adjacency Matrix $A$ that maps the flow of influence.

Nodes and Edges

  • Nodes ($V$): State Variables, Effects, and Implicit Events.
  • Edges ($E$): Causal links where $u \to v$ means "u triggered v".

Implicit Event Detection

Sometimes $A$ and $B$ update at the same time, but neither caused the other. To handle this, the engine detects external triggers (clicks, timers) and creates a Virtual Node ($Z$) in the graph:

Z → {A, B}

This lets the engine distinguish between a Dependency Chain ($A \to B$) and a Shared Source ($Z \to A, Z \to B$). Without this, every pair of simultaneous updates would look like a causal leak.


7. Spectral Influence Ranking (Prime Mover)

In a complex app you can get dozens of alerts. To solve this "Alert Fatigue" problem, Basis calculates Eigenvector Centrality on the causal graph to find the "Prime Mover" (Root Cause).

We solve for the principal eigenvector $x$ of the adjacency matrix $A$:

$\displaystyle Ax = \lambda x$

The Power Iteration Approximation

Exact diagonalization is $O(N^3)$, which is too expensive. We use Power Iteration ($O(k \cdot E)$) instead to approximate the influence score.

$$ x_{target} = \sum_{source \to target} x_{source} $$

The key idea: a node's influence isn't just about how many nodes it triggers, but about how important those triggered nodes are.

  • Leaf Node: Influence $\approx 0$.
  • Chain Link: Influence $\approx$ Local Impact.
  • Prime Mover: Influence $\approx$ Sum of the entire cascade.

8. Practical Capabilities

What it detects

  • Linear Dependence: Simultaneous updates that reveal redundant state.
  • Prime Movers: The specific hook or event driving the largest volume of downstream updates.
  • Global Event Fragmentation: A single user interaction forcing updates across unrelated files/contexts.
  • Volatility Guard: Variables pulsing in >50% of the window are classified as Streams (~) and filtered from causal alerts.

Known Constraints

  • Asynchronous Gaps: Logic chains with delays $&gt; 10s$ (e.g., slow API responses) are treated as independent signals. The engine can't hold the graph open that long without risking memory leaks.

Clone this wiki locally