Experiment code and raw results for the empirical validation of the Behavioral Credibility Trilemma via Best-of-N selection. This repository accompanies the manuscript
L. Lovén, N. Do, H. Mehmood, D. K. Sah, S. Tarkoma (2026). The Behavioral Credibility Trilemma: When Calibrated Autonomy Becomes Impossible. Preprint, arXiv:2605.25739. In preparation for submission to the Journal of Machine Learning Research.
A 540-configuration Best-of-N sweep on Qwen-2.5-7B (54,000 selected-task observations) testing five hypotheses derived from the Behavioral Perturbation Lemma, plus a descriptive analysis of the achievable-(H, C, A) surface geometry of the Confidence-Gated Decision Problem. All five hypotheses are confirmed at α = 0.05 after Bonferroni–Holm correction. The repository ships the full simulation code, the task set, the experiment configs, the hypothesis-results JSONs (the Table-1 values plus the robustness/replication siblings), the three Phase-0 calibration CSVs (qwen / gemma2 / yi binding sets), and the H3 figure. The raw per-completion CSVs are not a deterministic function of the code (the run is a stochastic LLM-in-the-loop process at temperature τ = 0.8), so they are regenerable via the pipeline (Stages 1–2) rather than shipped; re-runs reproduce the effects, not the exact records.
The pipeline is two-stage: a Stage-A pool generator that calls Ollama
once per (task, seed, draw) to capture per-completion text + per-token
logprobs + the parsed answer + oracle correctness; and a Stage-B selector
that runs Best-of-N argmax over the pool for each of the 540 weight-config
cells. Stage A is the expensive step (~14 h of local Ollama); Stage B is
purely deterministic and runs in seconds against the pool jsonls. Plus a
Phase-0 calibration step that estimates per-task base accuracy from a
disjoint held-out seed split.
Estimates per-task base accuracy $\hat{p}t$ and binding-set membership at
each threshold $r{\min} \in {0.5, 0.7, 0.9}$ using 20 held-out seeds
{1000..1019}, disjoint from the experimental seeds. Avoids circularity in
the H5 binding-state-specificity test.
python -m scripts.run --phase0
# Output: experiment_output/raw_runs/qwen_2.5/phase0_calibration.csv
# (the canonical Phase-0 artifact; this exact file is what
# scripts/regenerate_hypothesis_results.py reads)Estimated runtime: ~1 hour on a single machine.
Generates the per-completion pool jsonl files that Stage B consumes.
python -m scripts.generate_pool --smoke # ~30 s, 4 completions
python -m scripts.generate_pool --seed 0 # ~2-3 h, one seed
python -m scripts.generate_pool --all-seeds # ~14 h, the full sweep
# Output: experiment_output/raw_runs/logprob/pools/
# pool_meta_qwen2.5_7b_N32.json
# pool_qwen2.5_7b_seed{42,123,456,789,0}_N32.jsonlStage A is the only step that needs Ollama (qwen2.5:7b via the
OpenAI-compatible /v1/chat/completions endpoint with logprobs:true).
Re-runs at the same seeds are NOT byte-identical — llama.cpp build and
hardware perturb the per-token logprobs — so re-runs reproduce the reported
effects rather than exact records (manuscript §exp-compute).
Reads the pool jsonls + applies the oracle payoff
python -m scripts.select_from_pool --all --out-dir /tmp/regen \
--verify-against-shipped
# Output: 540 CSVs under /tmp/regen, matching the archived
# experiment_output/raw_runs/logprob/results/*.csv byte-for-byte
# when run against the archived pools. (Pools and per-config CSVs
# are retained locally, NOT shipped in the public repo — see the
# repository structure below — so this byte-equality check applies
# to the authors' archive or to a locally regenerated Stage-A run.)# Rebuild hypothesis_results.json end-to-end from raw CSVs
python -m scripts.regenerate_hypothesis_results
# Output: experiment_output/analysis/hypothesis_results.json (rewritten in place)
# Plot H3 achievable-region convexity violation rate by N (descriptive)
python -m scripts.plot_h3_convexity_by_N
# Output: experiment_output/analysis/figures/h3_convexity_by_N.{pdf,png}N_BOOT controls bootstrap-CI resamples for H1/H4/H5/H6 (default 10000,
matching the manuscript):
N_BOOT=2000 python -m scripts.regenerate_hypothesis_results # ~1 min, smoke
N_BOOT=10000 python -m scripts.regenerate_hypothesis_results # ~5 min, paperPlot script: seconds.
After Stage 3, the hypothesis_results.json keys should match Table 1 of
the manuscript. H1, H2, H4, H5, H6 are the five hypothesis
tests (all confirmed); H3 is reported as the descriptive
surface-geometry analysis of the achievable-(H, C, A) region, not a
confirmed test:
| Hypothesis |
|
Effect size |
|---|---|---|
| H1 Fixed-axis gating degradation | ||
| H2 Monotone inflation trend (Jonckheere–Terpstra) | ||
| H3 Achievable-region convexity (descriptive) | binomial test, 10% < 15% | — |
| H4 Threshold clustering | ||
| H5 Binding-state specificity | ||
| H6 Control ( |
LICENSE
README.md
requirements.txt
EXPERIMENT-PLAN.md # full protocol (§4.3 has Phase 0 details)
analysis/
hypothesis_tests.py # H1–H6 tests + helpers
metrics.py # Brier decomposition etc.
figures.py # general figure utilities
logprob_confidence.py # logprob-confidence geomean (manuscript Eq.)
configs/
params.yaml # weight grid, seeds, r_min
scripts/
run.py # --phase0 branch: load-bearing Stage-0 driver;
# non-phase0 branch: LEGACY verbalized-era
# pipeline, NOT the manuscript's experiment
generate_pool.py # Stage A: 32-completion pool jsonl per (task, seed)
select_from_pool.py # Stage B: Best-of-N argmax → 540 per-config CSVs
regenerate_hypothesis_results.py # rebuild JSON from per-config CSVs + Phase 0
plot_h3_convexity_by_N.py # H3 stratified-by-N figure
plot_model_points.py # Appendix cross-model figure
eval_logprob.py # one-cell driver for the cross-model figure
generate_tasks.py # task generation
src/
orchestrator.py # per-config runner (legacy verbalized path)
ollama_client.py # Ollama native /api/generate client
ollama_logprob_client.py # Ollama OpenAI-compat /v1 client w/ logprobs
parser.py # response parser (CONFIDENCE + ANSWER)
scorer.py # composite payoff
tasks/ # 100 tasks (arith/factual/code)
tests/ # pytest unit tests
experiment_output/
analysis/ # canonical results (paper's Table 1)
hypothesis_results.json # shipped; rewritten by Stage 3
figures/h3_convexity_by_N.{pdf,png} # shipped; manuscript Figure 2
README.md # flags this dir's untracked aggregate CSVs
# as verbalised-era (superseded; provenance only)
competence_probe/figures/model_points.* # shipped; cross-model figure
raw_runs/ # pools + per-config CSVs NOT shipped (gitignored,
# L73); ONLY the three phase0_calibration.csv
# files below are shipped
logprob/pools/ # Stage A output (regenerated by --all-seeds)
logprob/results/ # Stage B output (regenerated from pools)
qwen_2.5/phase0_calibration.csv # SHIPPED Phase-0, primary (binding 71/72/74)
logprob-gemma2_9b/phase0_calibration.csv # SHIPPED Phase-0, E-B replication (59/61/65)
logprob-yi_9b/phase0_calibration.csv # SHIPPED Phase-0, E-B replication (84/88/97)
docs/
REPRODUCING.md # step-by-step reproduction guide
The repository contains development/robustness machinery that no manuscript claim rests on; it is listed here so the inventory is complete (none of it contradicts the paper):
analysis/conditional_metrics.py— decoupled (H_conditional, C, A) triple; exploratory, unreported.analysis/difficulty_strat.py— task-difficulty tertile stratification; exploratory, unreported.- Spare model-registry slots in
configs/params.yaml(qwen14b,qwen32b,mistral7b,gemma2_9bconvenience keys) — probing candidates; the run models areprimary/secondary/tertiary/fallback*per EXPERIMENT-PLAN §11.E-B.1. levels:smoke modes inconfigs/params.yaml(unit_smoke,integration_smoke) — reduced grids for pipeline testing only; all reported numbers come from the full 540-config grid.
- Python 3.10 or newer (tested on 3.11)
- Ollama 0.4.2 or newer, with
qwen2.5:7bpulled (ollama pull qwen2.5:7b)- The paper uses the default Ollama quantization, Q4_K_M
- Inference via the OpenAI-compatible endpoint (
/v1/chat/completions) withlogprobs: true; temperature$\tau = 0.8$
- Python packages:
pip install -r requirements.txt
The per-completion confidence report
the geometric mean of per-token probabilities, clipped to src/parser.py for the exact
extraction code.
The oracle correctness label
- Arithmetic: exact-value comparison after numeric parsing
- Factual: matched against the curated reference file
tasks/factual_truth.csv - Code: Python test-case execution
See src/scorer.py for the full verification logic.
During experiments, we asked the model (here Qwen-2.5-7B) to state its confidence for each answer to help analyze the calibration tradeoff. However, the reported confidence was unreliably high -- the model consistently responded with 100% confidence regardless of correctness or task difficulty. This made meaningful calibration analysis impossible, which motivated our switch to token-level log probabilities as the confidence measure instead.
The per-config CSVs from that earlier verbalised run are retained in the authors' local archive under experiment_output/raw_runs/qwen_2.5/ (gitignored, not shipped in the public repository; that directory's tracked README documents the provenance). Their degeneracy statistics: selected confidence in the r_selected column is r = 1.0 for 93.3% of the 53,928 selected completions and r = 0.95 for the remaining 6.7%. The manuscript's hypothesis results rest on the log-probability pipeline alone; the verbalised run is documented as degenerate, not used as a robustness check.
Archival snapshots are deposited at Zenodo: concept DOI
10.5281/zenodo.20392749 (always resolves
to the latest version). This release is v1.3-jmlr-submission
(see CITATION.cff), the first version whose Zenodo deposit contains the
complete artifact set (cross-model replication, comonotone/rearrangement
robustness, Phase-0 calibration CSVs, E-C random/proxy control suites);
earlier deposited versions are v1.1-jmlr-submission
10.5281/zenodo.20646964 and
v1.0.
@misc{loven2026trilemma,
title = {The Behavioral Credibility Trilemma: When Calibrated Autonomy
Becomes Impossible},
author = {Lov{\'e}n, Lauri and Do, Nam and Mehmood, Hassan and
Sah, Dinesh Kumar and Tarkoma, Sasu},
year = {2026},
eprint = {2605.25739},
archivePrefix = {arXiv},
primaryClass = {cs.LG},
url = {https://arxiv.org/abs/2605.25739}
}MIT — see LICENSE.
Issues and questions: please open a GitHub issue.