This project simplifies attribution graphs in two phases:
The resulting skeleton is saved back into the original graph format via qParams (pinned nodes + supernodes), so downstream visualization tools can render simplified graphs without losing original node metadata.
Pruning is implemented in mapper/utils.py and is invoked by both pipeline variants via prune_graph.
Current behavior:
Two grouping pipelines are available, selected via --grouping:
embedding (default) — MapperPipelineGroups nodes using a Mapper-style cover over a 1D lens:
TopImpactScorer scores.SupernodeLens (sentence-transformer embeddings, PCA-reduced to 1D) clusters nodes within each interval.cluster_1d_dict) groups nodes whose semantic values lie within a fixed distance.llm — LLMGroupingPipelineGroups nodes by asking an LLM to form semantically coherent clusters in a single call:
--n_groups_hint to softly guide the number of groups.Both pipelines produce a skeleton nx.Graph with the same structure (nodes carry nodes=[...] membership and label), so all downstream code is interchangeable.
LLMs are used in two separate roles, each independently switchable between closed-source (OpenAI) and open-source (local HuggingFace) models:
| Role | Triggered by | Controlled by |
|---|---|---|
Auto-interpretation (rerun_auto_interpretation) | Empty/missing node labels in SupernodeLens or LLMGroupingPipeline | --open_source_labeling |
Umbrella term (get_umbrella_term) | Multi-node supernodes in save_graph_with_qparams that have no pre-set label | --open_source_labeling |
LLM grouping (llm_group_nodes) | --grouping llm | --open_source_grouping |
⚠️ API key required for closed-source models. By default all three LLM roles route requests through the OpenAI API. You must set
OPENAI_API_KEYat the top of data/supernode_label.py before running. If you don't have an OpenAI key, pass--open_source_labelingand/or--open_source_groupingto use a local open-weight model (e.g. DeepSeek, Qwen, Llama) instead — no API key needed, but a GPU with sufficient VRAM is required.
Model configuration (model names, API key) lives in data/supernode_label.py (CLOSED_SOURCE_MODEL, OPEN_SOURCE_MODEL, OPENAI_API_KEY).
Implemented in lenses/supernode.py. Encodes each node label with a sentence-transformer model, then reduces the embeddings to a single scalar via PCA. This 1D value is used as the clustering coordinate inside MapperPipeline. Accepts use_closed_source_labeling to control which LLM is used for re-labeling nodes whose label is empty.
Implemented in mapper/top_impact.py. Not a lens — a standalone node-selection function that scores every node in the graph by its causal importance for the predicted output token. Used by both pipelines to decide which nodes survive pruning.
How it works (high level):
1.0), embedding nodes (0.5), error nodes (0.05), etc.score = prior × (w_importance · importance + w_competition · competition + …) using fixed linear weights defined in VNSWeights.min_importance_for_selection = 0.035) are kept; the rest are pruned.SupernodeLens (semantic similarity for clustering)Install Python dependencies:
pip install -r graph_skel/requirements.txt
If you plan to build graphs from prompts (instead of using existing JSONs), you also need the circuit_tracer package from the bundled submodule:
pip install -e graph_skel/external/Faithfulness
python -m graph_skel.experiments.run_experiment --graph graph_skel/data/sample_graphs/<SAMPLE>.json
python -m graph_skel.experiments.run_experiment --graph_dir graph_skel/data/sample_graphs/
python -m graph_skel.experiments.run_experiment \
--graph graph_skel/data/sample_graphs/<SAMPLE>.json \
--grouping llm
python -m graph_skel.experiments.run_experiment \
--graph graph_skel/data/sample_graphs/<SAMPLE>.json \
--grouping llm \
--open_source_grouping
python -m graph_skel.experiments.run_experiment \
--graph graph_skel/data/sample_graphs/<SAMPLE>.json \
--open_source_labeling
python -m graph_skel.experiments.run_experiment \
--graph graph_skel/data/sample_graphs/<SAMPLE>.json \
--grouping llm \
--open_source_labeling
python -m graph_skel.experiments.run_experiment --prompt "Your prompt here"
| Flag | Type | Default | Description |
|---|---|---|---|
--graph | str | — | Path to a single input graph JSON |
--graph_dir | str | — | Path to a directory of graph JSON files |
--prompt | str | — | Build attribution graph from this prompt (requires circuit_tracer) |
--grouping | embedding|llm | embedding | Grouping method: PCA-based Mapper or LLM-based clustering |
--open_source_grouping | flag | off | Use local HuggingFace model for LLM-based grouping instead of OpenAI |
--open_source_labeling | flag | off | Use local HuggingFace model for auto-interpretation and umbrella terms instead of OpenAI |
--n_groups_hint | int | 0 | Soft hint for number of groups when --grouping llm (0 = no hint) |
--desired_logit_prob | float | 0.95 | Target cumulative logit probability (prompt mode) |
--max_n_logits | int | 10 | Max logit nodes (prompt mode) |
--max_feature_nodes | int | 8192 | Max feature nodes (prompt mode) |
--graph_batch_size | int | 256 | Batch size for attribution graph construction (prompt mode) |
--node_threshold | float | 0.5 | Node pruning threshold (prompt mode) |
--edge_threshold | float | 0.8 | Edge pruning threshold (prompt mode) |
Exactly one of --graph, --graph_dir, or --prompt must be provided.
The output is a JSON graph with a qParams section that contains:
pinnedIds: all nodes kept in the skeletonsupernodes: grouped clusters with generated labelsThe original nodes and edges are preserved, so visualization tools can overlay the supernode structure.
Each graph JSON includes:
nodes: list of node objects with fields such as node_id, label/clerp, feature_type, layer, ctx_idx, token_prob, etc.links: list of directed edges with source, target, weight.metadata: prompt and run metadata (optional but used by the pipeline).[1] Rosen, P., Hajij, M., & Wang, B. (2023, October). Homology-preserving multi-scale graph skeletonization using mapper on graphs. In 2023 Topological Data Analysis and Visualization (TopoInVis) (pp. 10-20). IEEE.
40 commits
Python
99.2%
This project simplifies attribution graphs in two phases:
The resulting skeleton is saved back into the original graph format via qParams (pinned nodes + supernodes), so downstream visualization tools can render simplified graphs without losing original node metadata.
Pruning is implemented in mapper/utils.py and is invoked by both pipeline variants via prune_graph.
Current behavior:
Two grouping pipelines are available, selected via --grouping:
embedding (default) — MapperPipelineGroups nodes using a Mapper-style cover over a 1D lens:
TopImpactScorer scores.SupernodeLens (sentence-transformer embeddings, PCA-reduced to 1D) clusters nodes within each interval.cluster_1d_dict) groups nodes whose semantic values lie within a fixed distance.llm — LLMGroupingPipelineGroups nodes by asking an LLM to form semantically coherent clusters in a single call:
--n_groups_hint to softly guide the number of groups.Both pipelines produce a skeleton nx.Graph with the same structure (nodes carry nodes=[...] membership and label), so all downstream code is interchangeable.
LLMs are used in two separate roles, each independently switchable between closed-source (OpenAI) and open-source (local HuggingFace) models:
| Role | Triggered by | Controlled by |
|---|---|---|
Auto-interpretation (rerun_auto_interpretation) | Empty/missing node labels in SupernodeLens or LLMGroupingPipeline | --open_source_labeling |
Umbrella term (get_umbrella_term) | Multi-node supernodes in save_graph_with_qparams that have no pre-set label | --open_source_labeling |
LLM grouping (llm_group_nodes) | --grouping llm | --open_source_grouping |
⚠️ API key required for closed-source models. By default all three LLM roles route requests through the OpenAI API. You must set
OPENAI_API_KEYat the top of data/supernode_label.py before running. If you don't have an OpenAI key, pass--open_source_labelingand/or--open_source_groupingto use a local open-weight model (e.g. DeepSeek, Qwen, Llama) instead — no API key needed, but a GPU with sufficient VRAM is required.
Model configuration (model names, API key) lives in data/supernode_label.py (CLOSED_SOURCE_MODEL, OPEN_SOURCE_MODEL, OPENAI_API_KEY).
Implemented in lenses/supernode.py. Encodes each node label with a sentence-transformer model, then reduces the embeddings to a single scalar via PCA. This 1D value is used as the clustering coordinate inside MapperPipeline. Accepts use_closed_source_labeling to control which LLM is used for re-labeling nodes whose label is empty.
Implemented in mapper/top_impact.py. Not a lens — a standalone node-selection function that scores every node in the graph by its causal importance for the predicted output token. Used by both pipelines to decide which nodes survive pruning.
How it works (high level):
1.0), embedding nodes (0.5), error nodes (0.05), etc.score = prior × (w_importance · importance + w_competition · competition + …) using fixed linear weights defined in VNSWeights.min_importance_for_selection = 0.035) are kept; the rest are pruned.SupernodeLens (semantic similarity for clustering)Install Python dependencies:
pip install -r graph_skel/requirements.txt
If you plan to build graphs from prompts (instead of using existing JSONs), you also need the circuit_tracer package from the bundled submodule:
pip install -e graph_skel/external/Faithfulness
python -m graph_skel.experiments.run_experiment --graph graph_skel/data/sample_graphs/<SAMPLE>.json
python -m graph_skel.experiments.run_experiment --graph_dir graph_skel/data/sample_graphs/
python -m graph_skel.experiments.run_experiment \
--graph graph_skel/data/sample_graphs/<SAMPLE>.json \
--grouping llm
python -m graph_skel.experiments.run_experiment \
--graph graph_skel/data/sample_graphs/<SAMPLE>.json \
--grouping llm \
--open_source_grouping
python -m graph_skel.experiments.run_experiment \
--graph graph_skel/data/sample_graphs/<SAMPLE>.json \
--open_source_labeling
python -m graph_skel.experiments.run_experiment \
--graph graph_skel/data/sample_graphs/<SAMPLE>.json \
--grouping llm \
--open_source_labeling
python -m graph_skel.experiments.run_experiment --prompt "Your prompt here"
| Flag | Type | Default | Description |
|---|---|---|---|
--graph | str | — | Path to a single input graph JSON |
--graph_dir | str | — | Path to a directory of graph JSON files |
--prompt | str | — | Build attribution graph from this prompt (requires circuit_tracer) |
--grouping | embedding|llm | embedding | Grouping method: PCA-based Mapper or LLM-based clustering |
--open_source_grouping | flag | off | Use local HuggingFace model for LLM-based grouping instead of OpenAI |
--open_source_labeling | flag | off | Use local HuggingFace model for auto-interpretation and umbrella terms instead of OpenAI |
--n_groups_hint | int | 0 | Soft hint for number of groups when --grouping llm (0 = no hint) |
--desired_logit_prob | float | 0.95 | Target cumulative logit probability (prompt mode) |
--max_n_logits | int | 10 | Max logit nodes (prompt mode) |
--max_feature_nodes | int | 8192 | Max feature nodes (prompt mode) |
--graph_batch_size | int | 256 | Batch size for attribution graph construction (prompt mode) |
--node_threshold | float | 0.5 | Node pruning threshold (prompt mode) |
--edge_threshold | float | 0.8 | Edge pruning threshold (prompt mode) |
Exactly one of --graph, --graph_dir, or --prompt must be provided.
The output is a JSON graph with a qParams section that contains:
pinnedIds: all nodes kept in the skeletonsupernodes: grouped clusters with generated labelsThe original nodes and edges are preserved, so visualization tools can overlay the supernode structure.
Each graph JSON includes:
nodes: list of node objects with fields such as node_id, label/clerp, feature_type, layer, ctx_idx, token_prob, etc.links: list of directed edges with source, target, weight.metadata: prompt and run metadata (optional but used by the pipeline).[1] Rosen, P., Hajij, M., & Wang, B. (2023, October). Homology-preserving multi-scale graph skeletonization using mapper on graphs. In 2023 Topological Data Analysis and Visualization (TopoInVis) (pp. 10-20). IEEE.
40 commits
Python
99.2%