This repository is the replication package for contrastive mechanistic interpretability experiments on small Python programs. It contains the stimuli, model patching and analysis code, generated paper tables and figures, and archived raw run artifacts.
| Section | Use it for |
|---|---|
| Stimuli | Snippet construction details and curation criteria; expand "Snippet construction criteria". |
| Method Summary | Response labels, residual-patching formulas, denominator handling, recovery thresholds, sensitivity checks, margin-gap robustness, and direction labels. |
| Paper Artifacts | Generated tables, manifests, figures, raw archives, and per-RQ output files. |
| Reproduction | Dependency setup and commands for rerunning model sweeps or regenerating analysis outputs. |
| Checks | Lightweight validation commands for metadata, stimulus rules, and tokenizer preflight. |
| Path | Contents |
|---|---|
snippets_working/ |
Active crossed stimulus pairs. |
config/experiment_config.json |
Model, prompt, and methodology defaults. |
scripts/ |
Generation, residual patching, and paper-analysis entry points. |
scripts/supplementary/ |
Stimulus checks, secondary inspection, and rerun preflight tools. |
notebooks/model_sweep_clean_results_analysis.ipynb |
Output-cleared notebook used for paper tables and figures. |
outputs/model_sweep/behavior_analysis/ |
Generated analysis tables, manifests, and paper figures. |
eval/model_sweep_clean_schema_archives/ |
Pair-level ZIP archives of raw model-run artifacts. |
The active stimuli live under snippets_working/ and use
two crossed families:
| Family | Contrast |
|---|---|
cue_varied/ |
Both programs execute to the same output, but the conflicting prompt changes the semantic cue. |
implementation_varied/ |
Both prompts keep the same cue-level behavior, but the conflicting implementation changes the runtime output. |
Each family contains pair_001 through pair_045. The same pair_id denotes
the same base stimulus in both families. For each active pair, the aligned
middle program is shared exactly, for example:
snippets_working/cue_varied/pair_001/aligned_cue.py
snippets_working/implementation_varied/pair_001/aligned_implementation.py
Each pair directory contains role-based snippet files plus meta.json, which
records the pair id, family, prompt role, runtime output, and cue-implied
output.
Snippet construction criteria
Short checklist:
- Snippets avoid explicit final-output cues such as "Expected final printed output", "final printed output", or "Output:".
- Semantic cues may be carried by comments/docstrings or by code names such as helper function names; comments/docstrings are not required.
- When comments/docstrings are used as cue carriers, each function has at most one.
- Snippets print through
compute(...); top-levelprint(...)calls do not call another task function directly. computemust contain the task logic rather than only wrapping another helper call.- In
cue_varied, the paired snippets execute to the same output, but the conflicting snippet changes the semantic cue. - In
cue_varied, cue-bearing text/code changes pointwise; when the cue is in the first comment, that comment changes by at most one word region. - In
implementation_varied, the paired snippets keep the semantic cue unchanged, but the conflicting snippet executes to a different output. - Pair edits are pointwise and minimal: at most three changed token regions and eight changed tokens.
The curation criteria are also encoded as executable checks:
check_methodology_metadata.pychecks the crossed design, pair ids, prompt roles, runtime/cue metadata, patch directions, and exact sharing ofaligned_cue.pyandaligned_implementation.py.check_stimulus_editing_rules.pychecks that snippets avoid explicit output cues, cap cue comments/docstrings when present, usecomputeas the printed entry point, avoid wrapper-onlycomputefunctions, and use pointwise/minimal edits.
Run:
python3 scripts/supplementary/check_methodology_metadata.py snippets_working
python3 scripts/supplementary/check_stimulus_editing_rules.py snippets_workingOptional generated catalog:
python3 scripts/supplementary/analyze_stimuli_catalog.pyModel outputs and generated unit-test expected values use the same label taxonomy:
both_consistentexecution_consistentcue_consistentneither_consistent
Accuracy is execution-grounded:
is_accurate = candidate == execution_output
Residual patching is defined over a direction with source_role,
destination_role, source_output, and destination_output. The margin is
computed at the first divergent continuation token:
margin = logit(source_token) - logit(destination_token)
Primary recovery metric:
signed_recovery = (patched_margin - baseline_margin) / (source_margin - baseline_margin)
Implementation references:
run_residual_patching_batch.pydefinesnormalize_recovery(...)andsigned_recovery_matrix(...). These artifact-generation helpers addeps = 1e-9to denominators for numerical stability.methodology_core.pydefines the strictermargin_values(...)helper. Ifabs(source_margin - baseline_margin) <= 1e-9, it raisesno_source_destination_margin_difference.- Post-hoc summary ratios use
safe_divide(...)inanalyze_rq1_2_carrier_tokens.py; zero denominators produce blank table cells rather than unstable numbers.
The primary intermediate-carrier threshold is 0.3: a site is selected only if
its maximum nonnegative signed recovery reaches the threshold. RQ1.2 also
materializes sensitivity checks at 0.2 and 0.5; RQ2.2 materializes the
primary thresholded site-group analysis plus native patch-unit and
direction-asymmetry robustness checks below.
RQ1.2 carrier threshold sensitivity aggregates
Direction-runs are model x pair-family stimulus x patch direction rows.
| Threshold | Direction-runs | Runs with carriers | Carrier tokens | Mean carriers/run | Median carriers/run | Mean carrier share | Median carrier share |
|---|---|---|---|---|---|---|---|
| 0.2 | 720 | 609 | 3840 | 5.333 | 2 | 0.123 | 0.067 |
| 0.3 | 720 | 530 | 2640 | 3.667 | 1 | 0.087 | 0.034 |
| 0.5 | 720 | 380 | 1468 | 2.039 | 1 | 0.051 | 0.012 |
By stimulus family:
| Family | Threshold | Direction-runs | Runs with carriers | Carrier tokens | Mean carriers/run | Median carriers/run | Median carrier share |
|---|---|---|---|---|---|---|---|
| Cue-varied | 0.2 | 360 | 323 | 2516 | 6.989 | 3 | 0.065 |
| Cue-varied | 0.3 | 360 | 290 | 1840 | 5.111 | 2 | 0.033 |
| Cue-varied | 0.5 | 360 | 205 | 1030 | 2.861 | 1 | 0.015 |
| Implementation-varied | 0.2 | 360 | 286 | 1324 | 3.678 | 2 | 0.071 |
| Implementation-varied | 0.3 | 360 | 240 | 800 | 2.222 | 1 | 0.034 |
| Implementation-varied | 0.5 | 360 | 175 | 438 | 1.217 | 0 | 0 |
By patch direction:
| Direction | Threshold | Direction-runs | Runs with carriers | Carrier tokens | Mean carriers/run | Median carriers/run | Median carrier share |
|---|---|---|---|---|---|---|---|
| forward | 0.2 | 360 | 298 | 2068 | 5.744 | 2 | 0.067 |
| forward | 0.3 | 360 | 264 | 1431 | 3.975 | 2 | 0.034 |
| forward | 0.5 | 360 | 194 | 742 | 2.061 | 1 | 0.012 |
| reverse | 0.2 | 360 | 311 | 1772 | 4.922 | 2 | 0.066 |
| reverse | 0.3 | 360 | 266 | 1209 | 3.358 | 1 | 0.033 |
| reverse | 0.5 | 360 | 186 | 726 | 2.017 | 1 | 0.011 |
Raw files:
rq1_2_carrier_sparsity_by_run.csvis the run-level source for these aggregates.rq1_2_carrier_tokens.csvis the token-level source and includescrosses_0_2,crosses_0_3, andcrosses_0_5.
The margin-gap robustness aggregate reports the base analysis plus reference-gap
thresholds 0.05, 0.10, and 0.25; tau_0_10_raw_0_10 combines a 0.10
reference-gap threshold with a 0.10 minimum direction-aligned raw effect.
Margin-gap robustness aggregate
| Specification | Reference-gap threshold | Min raw effect | Model-pair runs | Direction-runs | Carrier tokens | Mean carriers/run | Median carriers/run | Max carriers/run | Median changed depth | Median carrier depth | Median readout depth |
|---|---|---|---|---|---|---|---|---|---|---|---|
| base | 0 | 0 | 360 | 720 | 2640 | 3.6667 | 1 | 102 | 0.064516 | 0.354839 | 0.806451 |
| tau_0_05 | 0.05 | 0 | 328 | 656 | 1392 | 2.1220 | 1 | 42 | 0.064516 | 0.354839 | 0.822580 |
| tau_0_10 | 0.10 | 0 | 300 | 600 | 1150 | 1.9167 | 1 | 42 | 0.064516 | 0.354839 | 0.833333 |
| tau_0_25 | 0.25 | 0 | 251 | 502 | 817 | 1.6275 | 1 | 42 | 0.064516 | 0.387097 | 0.838710 |
| tau_0_10_raw_0_10 | 0.10 | 0.10 | 300 | 600 | 954 | 1.5900 | 1 | 26 | 0.064516 | 0.387097 | 0.833334 |
Raw file:
RQ2.2 robustness and localization checks
RQ2.2 uses the 0.3 threshold for threshold-qualified site groups, then checks
that the unit-test pathway interpretation is stable across native patch-unit
localization, final-output versus unit-test best-site comparisons, and
same-expression forward/reverse patching comparisons.
Site-group aggregate at threshold 0.3:
| Site group | Runs | Runs with selected units | Mean selected units | Median selected units | Mean selected share | Median best depth | Median max recovery |
|---|---|---|---|---|---|---|---|
| changed_prompt | 1741 | 1725 | 1.130 | 1 | 0.958 | 0.065 | 1.007 |
| prompt_carrier | 1741 | 1036 | 2.352 | 1 | 0.053 | 0.306 | 0.541 |
| response_carrier | 1398 | 747 | 1.640 | 1 | 0.046 | 0.419 | 0.548 |
| response_readout | 1398 | 1127 | 0.806 | 1 | 0.806 | 1.000 | 1.000 |
Final-output versus unit-test native patch-unit comparison:
| Family | Patch type | Final sites | Final depth | Final position | Unit sites | Unit depth | Unit position |
|---|---|---|---|---|---|---|---|
| Cue-varied | Changed region | 170 | 2 | 0.439 | 617 | 0 | 0.635 |
| Cue-varied | Token | 190 | 26 | 1.000 | 521 | 31 | 0.955 |
| Implementation-varied | Changed region | 179 | 3 | 0.531 | 724 | 0 | 0.644 |
| Implementation-varied | Token | 181 | 24 | 1.000 | 585 | 31 | 0.952 |
Forward/reverse direction-asymmetry rows that remain significant after Holm correction:
| Family | Site group | Metric | Paired rows | Mean diff | Median diff | Holm p |
|---|---|---|---|---|---|---|
| Cue-varied | changed_prompt | depth | 247 | 0.030 | 0 | 0.0043 |
| Cue-varied | prompt_carrier | depth | 137 | -0.025 | 0 | 0.0168 |
| Cue-varied | prompt_carrier | carrier_share | 255 | 0.018 | 0 | 0.0011 |
| Cue-varied | response_carrier | depth | 89 | -0.027 | 0 | 0.0051 |
| Cue-varied | response_readout | depth | 203 | 0.025 | 0 | 0.0153 |
| Implementation-varied | response_carrier | carrier_share | 291 | -0.009 | 0 | 0.0018 |
| Implementation-varied | response_readout | recovery | 233 | -0.117 | 0 | 0.0168 |
RQ2.2 direction-asymmetry pairing filter:
| Check | Count |
|---|---|
| All decisions | 896 |
| Same assertion expression | 553 |
| Same assertion index only | 343 |
| Reverse direction available | 871 |
| Paired eligible decisions | 553 |
Raw files:
rq2_2_site_group_analysis_manifest.jsonrq2_2_site_group_summary.csvstats_rq2_2_site_groups.csvrq2_2_final_vs_unit_native_patch_unit_comparison.csvrq2_2_best_site_layer_changed_vs_other_summary.csvrq2_2_top_k_native_patch_unit_summary.csvrq2_2_depth_position_recovery.csvrq2_2_weighted_layer_by_native_patch_unit.csvrq2_2_position_center_slope_by_run.csvstats_rq2_2.csvpatch_direction_asymmetry_manifest.jsonrq2_2_direction_asymmetry_pairs.csvstats_rq2_2_direction_asymmetry.csv
Forward/reverse comparisons use these direction labels:
forward:aligned_source_to_conflicting_destinationreverse:conflicting_source_to_aligned_destination- difference:
forward_minus_reverse
The paper-facing output root is
outputs/model_sweep/behavior_analysis/.
Manifests in this directory are the best entry points because they list the
generated tables and row counts.
Raw run artifacts
Raw model-run artifacts are stored as pair-level ZIP archives under
eval/model_sweep_clean_schema_archives/.
Each archive contains repository-relative paths and can be extracted from the
repository root:
find eval/model_sweep_clean_schema_archives -name 'pair_*.zip' -print0 | xargs -0 -n1 unzip -q -oThis restores eval/model_sweep_clean_schema/, which is large local working
data and should not be committed.
Analysis overview
RQ1.1 final-output behavior outputs
Core behavior tables:
Paper tables:
RQ2.1 unit-test behavior outputs
Manifest:
Core behavior tables:
rq2_1_unit_test_behavior.csvrq2_1_behavior_summary.csvrq2_1_assertions.csvrq2_1_pair_summary.csvrq2_1_unit_execution_long.csvrq2_1_unit_execution_summary.csv
Paper tables:
RQ1.2 carrier outputs
Manifest:
Core detailed tables:
rq1_2_carrier_tokens.csvrq1_2_carrier_count_by_run.csvrq1_2_carrier_count_summary.csvrq1_2_pair_level_carrier_count_summary.csvrq1_2_site_group_recovery_by_run.csvrq1_2_site_group_recovery_summary.csvstats_rq1_2_site_groups.csv
Sensitivity and overview tables:
rq1_2_carrier_sparsity_by_run.csvrq1_2_carrier_category_summary.csvrq1_2_carrier_layer_position_summary.csvrq1_2_carrier_propagation_summary.csvrq1_2_top_k_native_patch_unit_summary.csvrq1_2_best_site_layer_changed_vs_other_summary.csvrq1_2_depth_position_recovery.csvstats_rq1_2.csv
Aggregated threshold and robustness tables are embedded in Method Summary.
Robustness:
RQ2.2 site-group outputs
Aggregated robustness and localization tables are embedded in RQ2.2 robustness and localization checks.
Manifest:
Tables:
rq2_2_site_group_units.csvrq2_2_site_group_by_run.csvrq2_2_carrier_by_pair.csvrq2_2_site_group_summary.csvrq2_2_carrier_by_response_label.csvstats_rq2_2_site_groups.csvrq2_2_best_site_layer_changed_vs_other_summary.csvrq2_2_top_k_native_patch_unit_summary.csvrq2_2_depth_position_recovery.csvrq2_2_final_vs_unit_native_patch_unit_comparison.csvrq2_2_weighted_layer_by_native_patch_unit.csvrq2_2_native_patch_unit_depth_offsets.csvrq2_2_position_center_slope_by_run.csvstats_rq2_2.csv
Forward/reverse patching comparisons
Manifest:
RQ1.2:
rq1_2_direction_asymmetry_pairs.csvstats_rq1_2_direction_asymmetry.csvstats_rq1_2_direction_asymmetry_robustness.csv
RQ2.2:
Figures and LaTeX tables
- Registered manuscript figures are written to
paper_figures/. - Diagnostic and appendix plots are written under
paper_figures/appendix/. - LaTeX table exports are in
paper_tables/latex/. - Figure names and placement are controlled by
scripts/figure_paths.py.
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install -r requirements.txtFor local testing, we used a Python 3.13.5 environment with Apple MPS acceleration on a Mac Studio, while cluster experiments were run on a Linux cluster using a CUDA-based environment with the specified dependency image in the condor_submit files.
For gated Hugging Face models, set HF_TOKEN or HUGGING_FACE_HUB_TOKEN
before running model sweeps.
Run the current model-sweep stages:
python3 scripts/run_residual_patching_batch.py --allRun generated analysis tables and manifests:
python3 scripts/analyze_model_sweep_behavior.py
python3 scripts/analyze_rq2_1_cue_assertion_labels.py
python3 scripts/analyze_rq1_2_carrier_tokens.py
python3 scripts/analyze_rq2_2_site_groups.py
python3 scripts/analyze_patch_direction_asymmetry.pyCluster commands and useful selectors
Cluster split residual and unit-test-flow sweep:
condor_submit job_unit_flow_model_sweep.subCue-varied RQ2.2 cue-oracle rerun only:
condor_submit job_rq2_2_cue_oracle_rerun.subUseful selectors:
--pair-family cue_varied
--pair-family implementation_varied
--prompt-roles aligned_cue conflicting_cue
--patch-direction aligned_implementation_to_conflicting_implementationBecause pair_id values are shared across families, bare pair names are
ambiguous unless a family is specified:
python3 scripts/run_residual_patching_batch.py --pairs cue_varied/pair_001Run these from the repository root:
python3 scripts/supplementary/check_methodology_metadata.py snippets_working
python3 scripts/supplementary/check_stimulus_editing_rules.py snippets_workingTokenizer-alignment preflight for reruns that have access to configured model tokenizers:
python3 scripts/supplementary/check_transformerlens_tokenizer_alignment.py --models qwen2_5_7b_instruct --pair-family cue_varied --pairs pair_001See LICENSE.