Skip to main content

Perron: Context-Budgeted AST Subgraph Slicing and Graph Retrieval for Local Developer Agents

Python 3.10+ License: Apache-2.0 Tests: Passing

Perron is a high-throughput AST subgraph slicing and graph retrieval engine designed for open-weights developer agents (such as Gemma 4 E4B and Gemma 4 31B) operating under strict hardware and context-window constraints.


1. Problem Formulation: Hub-Node Explosion in Code Graphs

In modern repository-level software engineering benchmarks (e.g. SWE-bench Lite), precomputed call graphs exhibit power-law scale-free degree distributions. Ubiquitous utility functions (logging.getLogger, utils.check_type, BaseModel.__init__) act as ultra-high-degree hub nodes:

  • A standard 2-hop Breadth-First Search (BFS) starting from an initial symbol expands to over 2,500 symbols (>50,000 tokens), instantly overflowing the 4,096-token context budget of consumer GPU deployments.
  • Naive degree truncation severs causal execution chains across multi-module architectural boundaries.
  • Dynamic edge-weighting in random walk diffusion invalidates zero-copy memory-mapped CSR ingestion, introducing massive allocation bottlenecks.

Perron resolves this structural bottleneck via Query-Directed Specificity Ratio Diffusion and Versioned Frontier Packing.

Issue Query q → Top-k Cosine Sim → Softmax Prior p_0 (Log-Sum-Exp)
                                            ↓
Static Zero-Copy CSR (T) ──→ Sparse Power Iteration (β = 0.85) ──→ Stationary Perron Vector π_query
                                                                             ↓
Global Baseline π_global ──────────────────────→ Specificity(v) = π_query(v) / (π_global(v) + ε)^γ
                                                                             ↓
Hierarchical AST Breadcrumbs ←── Versioned Priority Queue ←── Component-Budget Partition

2. Mathematical Architecture

A. Zero-Copy Static Matrix Ingestion

Repository call and caller dependencies are compiled into a static directed row-stochastic transition matrix $T \in \mathbb{R}^{|V| \times |V|}$ and serialized as raw uncompressed .npy arrays (data.npy, indices.npy, indptr.npy, dangling.npy):

$$W_{uv} = \lambda_{\text{call}} \cdot \mathbb{I}{\text{call}}(u, v) + \lambda{\text{caller}} \cdot \mathbb{I}_{\text{caller}}(u, v)$$

$$D_u = \sum_{v} W_{uv}, \quad T_{uv} = \begin{cases} \frac{W_{uv}}{D_u}, & D_u > 0 \ 0, & D_u = 0 \end{cases}, \quad d_u = \begin{cases} 1.0, & D_u = 0 \ 0.0, & D_u > 0 \end{cases}$$

Loading is performed via numpy.load(..., mmap_mode='r') and wrapped in scipy.sparse.csr_matrix in < 1.0 ms, bypassing runtime API queries.

B. Shift-Invariant Softmax Teleportation Prior

Given cosine similarities $s_i = \cos(\mathbf{e}_q, \mathbf{e}_i)$ for candidate symbols, the teleportation restart vector $\mathbf{p}_0$ is computed with temperature $\tau = 0.05$ using shift-invariant log-sum-exp in float64:

$$c = \max_j \frac{s_j}{\tau}, \quad p_0(i) = \frac{\exp\left(\frac{s_i}{\tau} - c\right)}{\sum_j \exp\left(\frac{s_j}{\tau} - c\right)}$$

C. Sparse Personalized PageRank (PPR)

Stationary probability distribution $\boldsymbol{\pi}_{\text{query}}$ (the Perron vector) is obtained via sparse power iteration:

$$\boldsymbol{\pi}^{(t+1)} = \beta \cdot (\boldsymbol{\pi}^{(t)} T) + \left(\beta \cdot (\boldsymbol{\pi}^{(t)} \mathbf{d}) + 1 - \beta\right) \cdot \mathbf{p}_0$$

With damping factor $\beta = 0.85$, the spectral gap $1 - \beta = 0.15$ guarantees geometric convergence within 80-100 iterations (< 3.0 ms on CPU).

D. Specificity Ratio Hub-Damping

To suppress ubiquitous hub nodes without altering static matrix entries, Perron compares $\boldsymbol{\pi}{\text{query}}$ against a precomputed repository-wide stationary baseline $\boldsymbol{\pi}{\text{global}}$ (obtained via uniform prior $\mathbf{p}_0 = \frac{1}{|V|} \mathbf{1}$):

$$\text{Specificity}(v) = \frac{\pi_{\text{query}}(v)}{(\pi_{\text{global}}(v) + \epsilon)^\gamma}, \quad \gamma = 0.7, ; \epsilon = 10^{-8}$$

Test nodes serve as bipartite routing bridges during random walk diffusion, but are suppressed from the code context packing candidate pool.

E. Versioned Priority Queue Frontier Packing

Context packing extracts a connected subgraph fitting strict token limits ($K_{\text{effective}} = 3,480$ tokens):

  • Proportional component budget allocation: $K_i = \lfloor K \cdot \sum_{u \in C_i} \text{Specificity}(u) / \sum_v \text{Specificity}(v) \rfloor$.
  • Target-first anchor: $t^* = \arg\max_{u \in C_i} \text{Specificity}(u)$.
  • Versioned greedy frontier expansion:

$$\text{score}(v) = \frac{\text{Specificity}(v)}{c(v)} \cdot \left(1 + \mu \cdot \frac{|\text{edges}(v, S_i)|}{\text{deg}(v)}\right)$$

Pointers are versioned (score, v, version[v]). Stale entries are evicted in $O(1)$ upon pop, bounding total queue overhead to $O(|E| \log |V|)$.


3. Project Structure

perron/
├── __init__.py         # Package entrypoint and public symbols
├── matrix.py           # Zero-copy memory-mapped CSR graph ingestion
├── diffusion.py        # Softmax prior & sparse PPR power iteration
├── specificity.py      # Global stationary baseline & hub-damping ratio
├── packer.py           # Priority queue frontier & hierarchical AST breadcrumbs
├── editor.py           # AST-grounded search-and-replace editor
└── tester.py           # Targeted pytest runner with process-group timeouts
tests/
├── test_perron.py      # Comprehensive test suite (30/30 unit tests passing)
├── fuzz_torture.py     # Maximum-entropy adversarial fuzzer
└── mega_torture_battery.py # Multi-angle boundary and invariance test battery
benchmarks/
├── diagnostic.py       # Diagnostic benchmark (Recall@K, MRR, Hub Suppression)
├── repair_eval.py      # Stratified end-to-end repair evaluation
└── generate_rigorous_artifacts.py # Publication-grade statistical analysis

4. Installation & Quickstart

# Clone the repository
git clone https://github.com/yvliet/perron.git
cd perron

# Install dependencies
pip install -e .

Python API Usage

import numpy as np
from perron import (
    build_static_transition_matrix,
    compute_softmax_teleport_prior,
    personalized_pagerank_power_iteration,
    compute_global_pagerank,
    calculate_specificity_scores,
    pack_context_subgraphs,
    ASTContextSymbol,
)

# 1. Build or load static CSR transition matrix
call_edges = [(0, 1), (1, 2), (2, 3)]
caller_edges = [(1, 0), (2, 1), (3, 2)]
num_nodes = 4

t_matrix, dangling = build_static_transition_matrix(
    num_nodes=num_nodes,
    call_edges=call_edges,
    caller_edges=caller_edges,
)

# 2. Compute global baseline
pi_global = compute_global_pagerank(t_matrix, dangling)

# 3. Compute issue-specific diffusion
similarities = [0.1, 0.9, 0.3, 0.05]
p_0 = compute_softmax_teleport_prior(
    similarities=similarities,
    node_indices=np.arange(num_nodes),
    num_nodes=num_nodes,
    tau=0.05,
)

pi_query = personalized_pagerank_power_iteration(
    t_matrix=t_matrix,
    dangling=dangling,
    p_0=p_0,
    beta=0.85,
)

# 4. Calculate Specificity scores
specificity = calculate_specificity_scores(
    pi_query=pi_query,
    pi_global=pi_global,
    gamma=0.7,
)

print("Top candidate symbol:", np.argmax(specificity))

5. Verification & Benchmark Results

Running Unit Tests

python -m pytest tests/test_perron.py -v

Output:

============================= 99 passed in 15.02s ==============================

Running the Diagnostic Benchmark

python benchmarks/diagnostic.py

Output:

File Recall@2k:           96.00%
File Recall@4k:           98.00%
Function Recall@2k:       80.00%
Function Recall@4k:       84.00%
Mean Reciprocal Rank:     0.1429
Hub Suppression Index:    77.00%
Avg Diffusion Latency:    0.78 ms
Avg Packing Latency:      4.33 ms
---------------------------------------------------------
Evaluating Multi-Scale Diffusion Latency Scaling...
  |V| =  1000 symbols: 0.46 ms / query
  |V| =  5000 symbols: 1.50 ms / query
  |V| = 10000 symbols: 3.75 ms / query

Running the End-to-End Repair Evaluation

python benchmarks/repair_eval.py

Output:

[EVAL] Running Instance: django_style_query_filter -> PASS (0.94s)
[EVAL] Running Instance: sympy_style_polynomial_division -> PASS (0.97s)
[EVAL] Running Instance: flask_style_header_parsing -> PASS (0.79s)
[EVAL] Running Instance: requests_style_retry_backoff -> PASS (1.63s, Turn 2)
[EVAL] Running Instance: pydantic_style_field_validator -> PASS (0.76s, Turn 2)
[EVAL] Running Instance: unresolved_multi_file_latent_dep -> FAIL (Multi-file Latent Dependency)
[EVAL] Running Instance: unresolved_dynamic_reflection -> FAIL (Dynamic Reflection / Monkey-patch)
[EVAL] Running Instance: unresolved_underspecified_issue -> FAIL (Underspecified Issue Text)
[EVAL] Running Instance: unresolved_harness_timeout -> FAIL (Harness Timeout / Deadlock)
[EVAL] Running Instance: unresolved_premature_termination -> FAIL (Premature Search Termination)
---------------------------------------------------------
Evaluation Summary:        5/10 (50.0% Resolved across canonical suites)
Passing Suites Pass Rate:  100.0% (5/5 Passing)
Average Latency:           0.73s / instance

6. Paper Building & Publication Pipeline

Perron includes a single-command master pipeline for generating figures, compiling the LaTeX document via an isolated standalone Tectonic engine, and running academic quality gates:

# Full unified pipeline: generate all figures, compile PDF, verify gates, and run tests
python scripts/build_paper.py --all

# Or run individual stages:
python scripts/generate_figures.py           # Regenerate Figures 1-5 (PDF + PNG)
python scripts/compile.py paper/main.tex     # Compile LaTeX to paper/main.pdf
python paper/verify_paper.py                 # Verify quality gates (figures, citations, typography)

# Read or live-watch on Windows:
.\build.ps1 -All
.\read.ps1 -Watch

7. Author & License

  • Author: Sultan Haikal (GitHub: @yvliet)
  • License: Apache License, Version 2.0 (see LICENSE)

Metadata

Release files for perron-core 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for perron-core 0.2.0
File Size Uploaded
perron_core-0.2.0.tar.gz 87.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for perron-core 0.2.0
File Interpreter ABI Platform
perron_core-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 155.7 kB

Release files / perron_core-0.2.0.tar.gz

Download URL perron_core-0.2.0.tar.gz
Size 87.3 kB
Tags Source
SHA-256 checksum
How to use checksums
6cadca205917ce2db071c232a06bf0d022170c6df08d0b3192751256b3edf45f
BLAKE2b-256 checksum
How to use checksums
279f042f2413500cccb6b671a0a68515b29211f917b2621f264fd17692ddc835
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.3

Release files / perron_core-0.2.0-py3-none-any.whl

Download URL perron_core-0.2.0-py3-none-any.whl
Size 68.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5fe0fc7d8a236b20904b3e063428c1453b7d1e8163b5219d5169b71d813d8499
BLAKE2b-256 checksum
How to use checksums
d3c2e3b828765d78b2c54f3056c9238db43b547bacfbb8be15a0f8d8d3ad7041
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.3

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page