agentinputlab 0.1.0a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agentinputlab/__init__.py +28 -0
- agentinputlab/algorithms/__init__.py +6 -0
- agentinputlab/algorithms/acquire/__init__.py +145 -0
- agentinputlab/algorithms/acquire/adaptive_rag/__init__.py +43 -0
- agentinputlab/algorithms/acquire/adaptive_rag/replay.py +367 -0
- agentinputlab/algorithms/acquire/adaptive_rag/runtime.py +1202 -0
- agentinputlab/algorithms/acquire/adaptive_rag_common_support.py +542 -0
- agentinputlab/algorithms/acquire/hyde.py +296 -0
- agentinputlab/algorithms/acquire/hyde_controlled.py +200 -0
- agentinputlab/algorithms/acquire/hyde_external.py +148 -0
- agentinputlab/algorithms/acquire/ircot.py +28 -0
- agentinputlab/algorithms/acquire/ircot_common_support.py +478 -0
- agentinputlab/algorithms/acquire/ircot_native.py +355 -0
- agentinputlab/algorithms/acquire/ircot_replay.py +327 -0
- agentinputlab/algorithms/acquire/qrag/__init__.py +79 -0
- agentinputlab/algorithms/acquire/qrag/bridge.py +828 -0
- agentinputlab/algorithms/acquire/qrag/common_support.py +891 -0
- agentinputlab/algorithms/acquire/qrag/native_axis.py +71 -0
- agentinputlab/algorithms/acquire/qrag/reader_adapter.py +139 -0
- agentinputlab/algorithms/acquire/r2ag/__init__.py +57 -0
- agentinputlab/algorithms/acquire/r2ag/bridge.py +1312 -0
- agentinputlab/algorithms/acquire/r2ag/common_support.py +1029 -0
- agentinputlab/algorithms/acquire/r2ag/socket_runtime.py +255 -0
- agentinputlab/algorithms/clarify/__init__.py +61 -0
- agentinputlab/algorithms/clarify/_structured.py +64 -0
- agentinputlab/algorithms/clarify/active_task_disambiguation.py +371 -0
- agentinputlab/algorithms/clarify/ask_or_assume/__init__.py +17 -0
- agentinputlab/algorithms/clarify/ask_or_assume/monitor.py +410 -0
- agentinputlab/algorithms/clarify/future_turns.py +379 -0
- agentinputlab/algorithms/clarify/udcs.py +255 -0
- agentinputlab/algorithms/clarify/udcs_deterministic.py +196 -0
- agentinputlab/algorithms/curate/__init__.py +65 -0
- agentinputlab/algorithms/curate/acon/__init__.py +27 -0
- agentinputlab/algorithms/curate/acon/common.py +284 -0
- agentinputlab/algorithms/curate/acon/common_support.py +136 -0
- agentinputlab/algorithms/curate/acon/history_bridge.py +309 -0
- agentinputlab/algorithms/curate/acon/observation_bridge.py +290 -0
- agentinputlab/algorithms/curate/llmlingua/__init__.py +30 -0
- agentinputlab/algorithms/curate/llmlingua/adapters.py +537 -0
- agentinputlab/algorithms/curate/llmlingua/budget.py +48 -0
- agentinputlab/algorithms/curate/llmlingua/controlled.py +175 -0
- agentinputlab/algorithms/curate/llmlingua/multilingual.py +105 -0
- agentinputlab/algorithms/curate/llmlingua/span_map.py +167 -0
- agentinputlab/algorithms/curate/recomp/__init__.py +47 -0
- agentinputlab/algorithms/curate/recomp/external.py +1129 -0
- agentinputlab/algorithms/curate/recomp/selective.py +304 -0
- agentinputlab/algorithms/decompose/__init__.py +107 -0
- agentinputlab/algorithms/decompose/decomp/__init__.py +53 -0
- agentinputlab/algorithms/decompose/decomp/common_support.py +478 -0
- agentinputlab/algorithms/decompose/decomp/planner_component.py +199 -0
- agentinputlab/algorithms/decompose/decomp/worker_bridge.py +416 -0
- agentinputlab/algorithms/decompose/llm_compiler/__init__.py +35 -0
- agentinputlab/algorithms/decompose/llm_compiler/bridge.py +520 -0
- agentinputlab/algorithms/decompose/llm_compiler/common_support.py +504 -0
- agentinputlab/algorithms/decompose/plan_and_budget/__init__.py +43 -0
- agentinputlab/algorithms/decompose/plan_and_budget/cached_soft_budget.py +362 -0
- agentinputlab/algorithms/decompose/plan_and_budget/decomposer_component.py +107 -0
- agentinputlab/algorithms/decompose/plan_and_budget/full.py +205 -0
- agentinputlab/algorithms/decompose/plan_and_budget/hard_cap.py +246 -0
- agentinputlab/algorithms/decompose/plan_and_budget/regenerated_soft_budget.py +448 -0
- agentinputlab/algorithms/decompose/plan_and_solve/__init__.py +33 -0
- agentinputlab/algorithms/decompose/plan_and_solve/plan_component.py +192 -0
- agentinputlab/algorithms/decompose/plan_and_solve/ps_prompt.py +127 -0
- agentinputlab/algorithms/decompose/self_ask/__init__.py +27 -0
- agentinputlab/algorithms/decompose/self_ask/loop.py +362 -0
- agentinputlab/algorithms/decompose/self_ask/planner_component.py +117 -0
- agentinputlab/algorithms/reformulate/__init__.py +85 -0
- agentinputlab/algorithms/reformulate/infocqr/__init__.py +40 -0
- agentinputlab/algorithms/reformulate/infocqr/cache_replay.py +362 -0
- agentinputlab/algorithms/reformulate/infocqr/external.py +675 -0
- agentinputlab/algorithms/reformulate/infocqr/student.py +189 -0
- agentinputlab/algorithms/reformulate/query2doc/__init__.py +42 -0
- agentinputlab/algorithms/reformulate/query2doc/controlled_live.py +190 -0
- agentinputlab/algorithms/reformulate/query2doc/models.py +95 -0
- agentinputlab/algorithms/reformulate/query2doc/official_cached.py +335 -0
- agentinputlab/algorithms/reformulate/query2doc/querygym_bridge.py +223 -0
- agentinputlab/algorithms/reformulate/query2doc/querygym_runtime.py +326 -0
- agentinputlab/algorithms/reformulate/reformer/__init__.py +23 -0
- agentinputlab/algorithms/reformulate/reformer/external.py +417 -0
- agentinputlab/algorithms/reformulate/rrr.py +696 -0
- agentinputlab/annotation/__init__.py +5 -0
- agentinputlab/annotation/independent.py +428 -0
- agentinputlab/annotation/server.py +233 -0
- agentinputlab/annotation/static/app.js +121 -0
- agentinputlab/annotation/static/index.html +60 -0
- agentinputlab/annotation/static/review.css +2 -0
- agentinputlab/annotation/static/review.html +10 -0
- agentinputlab/annotation/static/review.js +11 -0
- agentinputlab/annotation/static/styles.css +55 -0
- agentinputlab/annotation/store.py +444 -0
- agentinputlab/api.py +368 -0
- agentinputlab/artifacts/__init__.py +15 -0
- agentinputlab/artifacts/json_file.py +104 -0
- agentinputlab/artifacts/manifest.py +98 -0
- agentinputlab/artifacts/store.py +157 -0
- agentinputlab/backends/__init__.py +98 -0
- agentinputlab/backends/embedding/__init__.py +25 -0
- agentinputlab/backends/embedding/base.py +145 -0
- agentinputlab/backends/embedding/callable.py +75 -0
- agentinputlab/backends/embedding/replay.py +129 -0
- agentinputlab/backends/llm/__init__.py +31 -0
- agentinputlab/backends/llm/base.py +149 -0
- agentinputlab/backends/llm/callable.py +108 -0
- agentinputlab/backends/llm/decomp_completion.py +88 -0
- agentinputlab/backends/llm/final_content_stop.py +72 -0
- agentinputlab/backends/llm/gpt_oss_final_channel.py +84 -0
- agentinputlab/backends/llm/openai_compatible.py +332 -0
- agentinputlab/backends/llm/transformers.py +171 -0
- agentinputlab/backends/retrieval/__init__.py +35 -0
- agentinputlab/backends/retrieval/base.py +50 -0
- agentinputlab/backends/retrieval/dense.py +777 -0
- agentinputlab/backends/retrieval/memory.py +119 -0
- agentinputlab/backends/retrieval/sqlite_fts.py +155 -0
- agentinputlab/backends/user/__init__.py +15 -0
- agentinputlab/backends/user/base.py +66 -0
- agentinputlab/backends/user/callable.py +54 -0
- agentinputlab/backends/user/replay.py +89 -0
- agentinputlab/backends/vector.py +38 -0
- agentinputlab/benchmarks/__init__.py +246 -0
- agentinputlab/benchmarks/acquisition.py +106 -0
- agentinputlab/benchmarks/ale.py +540 -0
- agentinputlab/benchmarks/appworld_ul.py +669 -0
- agentinputlab/benchmarks/assets.py +226 -0
- agentinputlab/benchmarks/base.py +68 -0
- agentinputlab/benchmarks/beir_archive.py +235 -0
- agentinputlab/benchmarks/beir_sample.py +269 -0
- agentinputlab/benchmarks/bfcl_native.py +245 -0
- agentinputlab/benchmarks/bfcl_v3.py +607 -0
- agentinputlab/benchmarks/cache.py +218 -0
- agentinputlab/benchmarks/clariq.py +203 -0
- agentinputlab/benchmarks/data/registry.json +321 -0
- agentinputlab/benchmarks/data/smoke_assets.json +511 -0
- agentinputlab/benchmarks/data/statebench_v0_8_1_task_ids.json +475 -0
- agentinputlab/benchmarks/data/toolsandbox_165848b_scenario_names.json +1 -0
- agentinputlab/benchmarks/execution.py +1111 -0
- agentinputlab/benchmarks/interactive.py +257 -0
- agentinputlab/benchmarks/ir_query.py +204 -0
- agentinputlab/benchmarks/longbench.py +203 -0
- agentinputlab/benchmarks/paired_replay.py +479 -0
- agentinputlab/benchmarks/preprocess.py +2313 -0
- agentinputlab/benchmarks/production.py +236 -0
- agentinputlab/benchmarks/qa.py +963 -0
- agentinputlab/benchmarks/registry.py +110 -0
- agentinputlab/benchmarks/retrieval.py +459 -0
- agentinputlab/benchmarks/runtime.py +509 -0
- agentinputlab/benchmarks/statebench.py +938 -0
- agentinputlab/benchmarks/tau3.py +725 -0
- agentinputlab/benchmarks/toolsandbox.py +626 -0
- agentinputlab/benchmarks/trec_dl_sample.py +263 -0
- agentinputlab/campaign.py +654 -0
- agentinputlab/campaign_runner.py +953 -0
- agentinputlab/cli/__init__.py +6 -0
- agentinputlab/cli/main.py +933 -0
- agentinputlab/connectors/__init__.py +46 -0
- agentinputlab/connectors/knowledge/__init__.py +4 -0
- agentinputlab/connectors/tools/__init__.py +30 -0
- agentinputlab/connectors/tools/base.py +57 -0
- agentinputlab/connectors/tools/mcp.py +222 -0
- agentinputlab/connectors/tools/memory.py +41 -0
- agentinputlab/connectors/tools/openapi.py +695 -0
- agentinputlab/connectors/tools/schema.py +102 -0
- agentinputlab/contracts/__init__.py +91 -0
- agentinputlab/contracts/availability.py +24 -0
- agentinputlab/contracts/dependency.py +58 -0
- agentinputlab/contracts/enums.py +82 -0
- agentinputlab/contracts/errors.py +63 -0
- agentinputlab/contracts/evidence.py +43 -0
- agentinputlab/contracts/intent.py +110 -0
- agentinputlab/contracts/lifecycle.py +164 -0
- agentinputlab/contracts/patch.py +167 -0
- agentinputlab/contracts/serialization.py +93 -0
- agentinputlab/contracts/state.py +127 -0
- agentinputlab/contracts/trace.py +59 -0
- agentinputlab/contracts/worker.py +42 -0
- agentinputlab/core/__init__.py +7 -0
- agentinputlab/data/__init__.py +32 -0
- agentinputlab/data/checksums.py +9 -0
- agentinputlab/data/downloader.py +235 -0
- agentinputlab/data/http.py +171 -0
- agentinputlab/data/index.py +21 -0
- agentinputlab/data/overlap.py +28 -0
- agentinputlab/data/preprocess.py +32 -0
- agentinputlab/data/storage.py +57 -0
- agentinputlab/data/trec_sample.py +427 -0
- agentinputlab/evaluation/__init__.py +5 -0
- agentinputlab/evaluation/outcomes.py +32 -0
- agentinputlab/experiments/__init__.py +278 -0
- agentinputlab/experiments/analysis.py +981 -0
- agentinputlab/experiments/artifacts.py +687 -0
- agentinputlab/experiments/automatic_measurement.py +678 -0
- agentinputlab/experiments/auxiliary_matrix.py +203 -0
- agentinputlab/experiments/bindings.py +430 -0
- agentinputlab/experiments/blueprints.py +147 -0
- agentinputlab/experiments/builders/__init__.py +45 -0
- agentinputlab/experiments/builders/executable_external_payload.py +423 -0
- agentinputlab/experiments/builders/external_process_arena.py +1018 -0
- agentinputlab/experiments/builders/registry_intervention.py +463 -0
- agentinputlab/experiments/cli.py +158 -0
- agentinputlab/experiments/controls.py +340 -0
- agentinputlab/experiments/driver_guard.py +205 -0
- agentinputlab/experiments/drivers/__init__.py +89 -0
- agentinputlab/experiments/drivers/external_arena.py +2908 -0
- agentinputlab/experiments/drivers/method_process.py +662 -0
- agentinputlab/experiments/drivers/parity_process.py +1484 -0
- agentinputlab/experiments/drivers/static_production.py +1574 -0
- agentinputlab/experiments/executor.py +448 -0
- agentinputlab/experiments/loader.py +605 -0
- agentinputlab/experiments/measurement.py +675 -0
- agentinputlab/experiments/paper_analysis.py +1818 -0
- agentinputlab/experiments/parity_identity.py +605 -0
- agentinputlab/experiments/plan.py +277 -0
- agentinputlab/experiments/production_builders.py +3739 -0
- agentinputlab/experiments/readiness.py +432 -0
- agentinputlab/experiments/schema.py +514 -0
- agentinputlab/experiments/secrets.py +177 -0
- agentinputlab/experiments/stateful_analysis.py +1738 -0
- agentinputlab/experiments/transfer_analysis.py +1316 -0
- agentinputlab/experiments/unseen_analysis.py +1025 -0
- agentinputlab/integrations/__init__.py +1 -0
- agentinputlab/integrations/action_space/__init__.py +1 -0
- agentinputlab/integrations/external_workers/__init__.py +76 -0
- agentinputlab/integrations/external_workers/ace.py +431 -0
- agentinputlab/integrations/external_workers/base.py +181 -0
- agentinputlab/integrations/external_workers/decomp_live.py +249 -0
- agentinputlab/integrations/external_workers/decomp_planner.py +30 -0
- agentinputlab/integrations/external_workers/infocqr_student.py +280 -0
- agentinputlab/integrations/external_workers/ircot.py +576 -0
- agentinputlab/integrations/external_workers/llmcompiler_live.py +360 -0
- agentinputlab/integrations/external_workers/longllmlingua.py +162 -0
- agentinputlab/integrations/external_workers/method.py +314 -0
- agentinputlab/integrations/external_workers/process.py +678 -0
- agentinputlab/integrations/external_workers/supervisor.py +1071 -0
- agentinputlab/integrations/full_architecture/__init__.py +1 -0
- agentinputlab/integrations/full_architecture/hyde.py +212 -0
- agentinputlab/integrations/full_architecture/plan_and_budget.py +188 -0
- agentinputlab/integrations/live_history.py +222 -0
- agentinputlab/integrations/prompt_optimization/__init__.py +1 -0
- agentinputlab/integrations/training/__init__.py +1 -0
- agentinputlab/metrics/__init__.py +50 -0
- agentinputlab/metrics/catalog.py +313 -0
- agentinputlab/metrics/harm.py +95 -0
- agentinputlab/metrics/paired.py +40 -0
- agentinputlab/metrics/qa.py +76 -0
- agentinputlab/metrics/text.py +33 -0
- agentinputlab/metrics/third_party.py +161 -0
- agentinputlab/models/__init__.py +4 -0
- agentinputlab/observability/__init__.py +56 -0
- agentinputlab/observability/budget.py +316 -0
- agentinputlab/observability/instrumentation.py +754 -0
- agentinputlab/observability/models.py +403 -0
- agentinputlab/observability/monitor.py +380 -0
- agentinputlab/optimizers/__init__.py +1 -0
- agentinputlab/optimizers/clarify/__init__.py +12 -0
- agentinputlab/optimizers/context/__init__.py +22 -0
- agentinputlab/optimizers/decompose/__init__.py +64 -0
- agentinputlab/optimizers/evidence/__init__.py +22 -0
- agentinputlab/optimizers/rewrite/__init__.py +30 -0
- agentinputlab/parity/__init__.py +5 -0
- agentinputlab/parity/base.py +206 -0
- agentinputlab/production_campaign.py +763 -0
- agentinputlab/py.typed +1 -0
- agentinputlab/recipes/__init__.py +333 -0
- agentinputlab/recipes/ace.py +418 -0
- agentinputlab/recipes/ace_bfcl.py +273 -0
- agentinputlab/recipes/ace_common_support.py +169 -0
- agentinputlab/recipes/ace_native_plan.py +322 -0
- agentinputlab/recipes/ace_online.py +711 -0
- agentinputlab/recipes/ace_retrieval.py +330 -0
- agentinputlab/recipes/ace_statebench.py +382 -0
- agentinputlab/recipes/ace_tau.py +463 -0
- agentinputlab/recipes/ace_toolsandbox.py +432 -0
- agentinputlab/recipes/acon_guideline.py +22 -0
- agentinputlab/recipes/adaptive_rag.py +822 -0
- agentinputlab/recipes/llmlingua2_inference.py +146 -0
- agentinputlab/recipes/llmlingua2_training.py +796 -0
- agentinputlab/recipes/mce.py +1486 -0
- agentinputlab/recipes/mce_final.py +335 -0
- agentinputlab/recipes/mce_text_fit.py +499 -0
- agentinputlab/recipes/mce_text_only.py +298 -0
- agentinputlab/recipes/qrag.py +729 -0
- agentinputlab/recipes/qrag_inference.py +364 -0
- agentinputlab/recipes/r2ag_features.py +236 -0
- agentinputlab/recipes/r2ag_inference.py +503 -0
- agentinputlab/recipes/r2ag_training.py +948 -0
- agentinputlab/recipes/recomp_abstractive.py +592 -0
- agentinputlab/recipes/recomp_extractive.py +525 -0
- agentinputlab/recipes/reformer.py +17 -0
- agentinputlab/recipes/rqrag.py +1516 -0
- agentinputlab/recipes/rqrag_common_support.py +193 -0
- agentinputlab/recipes/rqrag_completion.py +147 -0
- agentinputlab/recipes/skillopt.py +462 -0
- agentinputlab/recipes/skillopt_common_support.py +154 -0
- agentinputlab/recipes/skillopt_environment.py +165 -0
- agentinputlab/recipes/skillopt_environment_cli.py +5 -0
- agentinputlab/recipes/skillopt_retrieval.py +188 -0
- agentinputlab/recipes/smartsearch.py +897 -0
- agentinputlab/recipes/smartsearch_curriculum.py +283 -0
- agentinputlab/recipes/smartsearch_grpo_contracts.py +58 -0
- agentinputlab/recipes/smartsearch_grpo_data.py +314 -0
- agentinputlab/recipes/smartsearch_grpo_meter.py +324 -0
- agentinputlab/recipes/smartsearch_grpo_prefix.py +144 -0
- agentinputlab/recipes/smartsearch_grpo_rollout.py +119 -0
- agentinputlab/recipes/smartsearch_grpo_tools.py +364 -0
- agentinputlab/recipes/smartsearch_grpo_training.py +305 -0
- agentinputlab/recipes/smartsearch_grpo_transport.py +248 -0
- agentinputlab/recipes/smartsearch_grpo_worker.py +364 -0
- agentinputlab/recipes/smartsearch_inference.py +524 -0
- agentinputlab/recipes/smartsearch_native_data.py +172 -0
- agentinputlab/recipes/smartsearch_policy.py +616 -0
- agentinputlab/recipes/smartsearch_policy_worker.py +9 -0
- agentinputlab/recipes/smartsearch_sampling.py +232 -0
- agentinputlab/recipes/smartsearch_teacher.py +203 -0
- agentinputlab/recipes/smartsearch_training.py +265 -0
- agentinputlab/registry/__init__.py +46 -0
- agentinputlab/registry/beta.py +727 -0
- agentinputlab/registry/data/algorithms.json +1959 -0
- agentinputlab/registry/data/auxiliary_method_profiles.json +34 -0
- agentinputlab/registry/loader.py +100 -0
- agentinputlab/registry/result_manifest.py +184 -0
- agentinputlab/registry/runtime.py +195 -0
- agentinputlab/registry/schema.py +449 -0
- agentinputlab/runner/__init__.py +30 -0
- agentinputlab/runner/balance.py +248 -0
- agentinputlab/runner/cache.py +135 -0
- agentinputlab/runner/composite.py +130 -0
- agentinputlab/runner/engine.py +285 -0
- agentinputlab/runner/runtime_guard.py +268 -0
- agentinputlab/runner/worker.py +80 -0
- agentinputlab/runtime_bootstrap.py +288 -0
- agentinputlab/selectors/__init__.py +196 -0
- agentinputlab/selectors/advanced.py +1514 -0
- agentinputlab/selectors/artifacts.py +1087 -0
- agentinputlab/selectors/base.py +112 -0
- agentinputlab/selectors/campaigns.py +1893 -0
- agentinputlab/selectors/catalog.py +86 -0
- agentinputlab/selectors/discovery.py +1860 -0
- agentinputlab/selectors/evaluation.py +1348 -0
- agentinputlab/selectors/features.py +383 -0
- agentinputlab/selectors/learned.py +502 -0
- agentinputlab/selectors/no_op.py +28 -0
- agentinputlab/selectors/outcome_table.py +475 -0
- agentinputlab/selectors/producer.py +853 -0
- agentinputlab/selectors/rules.py +109 -0
- agentinputlab/selectors/runtime.py +420 -0
- agentinputlab/studies/__init__.py +16 -0
- agentinputlab/studies/design.py +165 -0
- agentinputlab/studies/paired.py +322 -0
- agentinputlab/studio/__init__.py +6 -0
- agentinputlab/studio/campaigns.py +819 -0
- agentinputlab/studio/engine.py +1408 -0
- agentinputlab/studio/model_backends.py +425 -0
- agentinputlab/studio/prepared.py +410 -0
- agentinputlab/studio/server.py +613 -0
- agentinputlab/studio/static/app.js +397 -0
- agentinputlab/studio/static/campaigns.html +175 -0
- agentinputlab/studio/static/campaigns.js +426 -0
- agentinputlab/studio/static/index.html +192 -0
- agentinputlab/studio/static/result.html +103 -0
- agentinputlab/studio/static/result.js +273 -0
- agentinputlab/studio/static/styles.css +467 -0
- agentinputlab-0.1.0a1.data/data/share/agentinputlab/CITATION.cff +22 -0
- agentinputlab-0.1.0a1.dist-info/METADATA +392 -0
- agentinputlab-0.1.0a1.dist-info/RECORD +469 -0
- agentinputlab-0.1.0a1.dist-info/WHEEL +5 -0
- agentinputlab-0.1.0a1.dist-info/entry_points.txt +6 -0
- agentinputlab-0.1.0a1.dist-info/licenses/LICENSE +21 -0
- agentinputlab-0.1.0a1.dist-info/licenses/NOTICE +31 -0
- agentinputlab-0.1.0a1.dist-info/licenses/THIRD_PARTY_NOTICES.md +94 -0
- agentinputlab-0.1.0a1.dist-info/top_level.txt +2 -0
- workers/README.md +94 -0
- workers/ace/README.md +261 -0
- workers/ace/__init__.py +1 -0
- workers/ace/compatibility_manifest.json +39 -0
- workers/ace/driver.py +1682 -0
- workers/acon/README.md +215 -0
- workers/acon/__init__.py +5 -0
- workers/acon/compatibility_manifest.json +93 -0
- workers/acon/driver.py +1859 -0
- workers/acon/fitted.py +395 -0
- workers/acon/guideline.py +470 -0
- workers/acon/guideline_source_manifest.json +20 -0
- workers/adaptive_rag/README.md +96 -0
- workers/adaptive_rag/__init__.py +15 -0
- workers/adaptive_rag/compatibility_manifest.json +61 -0
- workers/adaptive_rag/driver.py +1470 -0
- workers/adaptive_rag/fixture_runtime.py +163 -0
- workers/adaptive_rag/runtime.py +252 -0
- workers/ask_or_assume/README.md +124 -0
- workers/ask_or_assume/__init__.py +1 -0
- workers/ask_or_assume/compatibility_manifest.json +36 -0
- workers/ask_or_assume/driver.py +1227 -0
- workers/decomp/README.md +133 -0
- workers/decomp/UPSTREAM_LICENSE.Apache-2.0.txt +201 -0
- workers/decomp/__init__.py +5 -0
- workers/decomp/compatibility_manifest.json +205 -0
- workers/decomp/driver.py +1015 -0
- workers/decomp/live.py +137 -0
- workers/decomp/runtime.py +231 -0
- workers/infocqr/README.md +106 -0
- workers/infocqr/__init__.py +1 -0
- workers/infocqr/compatibility_manifest.json +84 -0
- workers/infocqr/driver.py +1766 -0
- workers/ircot/README.md +167 -0
- workers/ircot/__init__.py +7 -0
- workers/ircot/compatibility_manifest.json +121 -0
- workers/ircot/controlled.py +457 -0
- workers/ircot/driver.py +1373 -0
- workers/ircot/fixture_bindings.json +39 -0
- workers/ircot/fixture_config.json +48 -0
- workers/ircot/fixture_prompt.txt +2 -0
- workers/ircot/native_qa.py +398 -0
- workers/ircot/qa_compatibility_manifest.json +57 -0
- workers/ircot/runtime.py +97 -0
- workers/llmcompiler/README.md +160 -0
- workers/llmcompiler/__init__.py +1 -0
- workers/llmcompiler/compatibility_manifest.json +45 -0
- workers/llmcompiler/driver.py +1514 -0
- workers/llmcompiler/export_prompts.py +74 -0
- workers/llmcompiler/live.py +174 -0
- workers/llmcompiler/patch_manifest.json +147 -0
- workers/mce/README.md +198 -0
- workers/mce/__init__.py +29 -0
- workers/mce/compatibility_manifest.json +115 -0
- workers/mce/driver.py +2585 -0
- workers/mce/final_eval.py +173 -0
- workers/official_arenas/README.md +25 -0
- workers/official_arenas/__init__.py +1 -0
- workers/official_arenas/capture_records.py +110 -0
- workers/official_arenas/driver.py +619 -0
- workers/official_arenas/protocol.py +334 -0
- workers/official_arenas/providers/__init__.py +1 -0
- workers/official_arenas/providers/_live_history.py +228 -0
- workers/official_arenas/providers/_message_compat.py +77 -0
- workers/official_arenas/providers/appworld_ul.py +196 -0
- workers/official_arenas/providers/fixture.py +112 -0
- workers/official_arenas/providers/statebench.py +960 -0
- workers/official_arenas/providers/tau3.py +1672 -0
- workers/official_arenas/providers/tau_retail.py +586 -0
- workers/official_arenas/providers/toolsandbox.py +1321 -0
- workers/official_arenas/providers/tooltalk.py +699 -0
- workers/official_arenas/retail_capture.py +188 -0
- workers/official_arenas/retail_driver.py +275 -0
- workers/qrag/README.md +172 -0
- workers/qrag/__init__.py +1 -0
- workers/qrag/compatibility_manifest.json +64 -0
- workers/qrag/driver.py +673 -0
- workers/r2ag/README.md +154 -0
- workers/r2ag/__init__.py +5 -0
- workers/r2ag/compatibility_manifest.json +55 -0
- workers/r2ag/driver.py +1287 -0
- workers/r2ag/feature_schema.json +8 -0
- workers/r2ag/runtime.py +589 -0
- workers/recomp/README.md +120 -0
- workers/recomp/__init__.py +1 -0
- workers/recomp/compatibility_manifest.json +43 -0
- workers/recomp/driver.py +1653 -0
- workers/reformer/README.md +125 -0
- workers/reformer/__init__.py +1 -0
- workers/reformer/compatibility_manifest.json +68 -0
- workers/reformer/driver.py +1459 -0
- workers/reformer/induction.py +457 -0
- workers/rrr/README.md +126 -0
- workers/rrr/__init__.py +1 -0
- workers/rrr/compatibility_manifest.json +57 -0
- workers/rrr/driver.py +1133 -0
- workers/smartsearch/README.md +331 -0
- workers/smartsearch/__init__.py +1 -0
- workers/smartsearch/compatibility_manifest.json +41 -0
- workers/smartsearch/driver.py +1643 -0
|
@@ -0,0 +1,1818 @@
|
|
|
1
|
+
"""Protocol-checked analyses for the frozen KDD paper experiment blocks.
|
|
2
|
+
|
|
3
|
+
The general analysis helpers intentionally accept many matched-arm layouts.
|
|
4
|
+
This module adds the stricter paper contracts: five separate three-arm
|
|
5
|
+
family-native runs, one six-arm common-support run, four matched five-arm
|
|
6
|
+
retriever-factorial runs, and the 526-group/14-policy Selector campaign.
|
|
7
|
+
|
|
8
|
+
All entry points require complete, checksum-verified run directories. They
|
|
9
|
+
never execute experiments, mutate artifacts, or authorize paper claims.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import math
|
|
15
|
+
import random
|
|
16
|
+
from collections import defaultdict
|
|
17
|
+
from collections.abc import Mapping, Sequence
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from statistics import fmean
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
from agentinputlab.contracts.enums import ProfileKind, SemanticFamily
|
|
24
|
+
from agentinputlab.contracts.errors import ArtifactError, ValidationError
|
|
25
|
+
from agentinputlab.contracts.serialization import content_hash, freeze_mapping
|
|
26
|
+
|
|
27
|
+
from .analysis import (
|
|
28
|
+
_arm_metrics,
|
|
29
|
+
_cluster_bootstrap_interval,
|
|
30
|
+
_cluster_id,
|
|
31
|
+
_collapse_confirmatory_clusters,
|
|
32
|
+
_holm_adjust,
|
|
33
|
+
_linear_slope,
|
|
34
|
+
_load_manifest,
|
|
35
|
+
_paired_cluster_bootstrap_slope_interval,
|
|
36
|
+
_permutation_slope_p_value,
|
|
37
|
+
_pre_action_defect_scores,
|
|
38
|
+
_primary_families,
|
|
39
|
+
_required_record_text,
|
|
40
|
+
_validate_resample_arguments,
|
|
41
|
+
analyze_confirmatory_interactions,
|
|
42
|
+
load_verified_cell_results,
|
|
43
|
+
)
|
|
44
|
+
from .schema import ExperimentManifest, ExperimentStage
|
|
45
|
+
|
|
46
|
+
PAPER_FAMILY_NATIVE_PROTOCOL = "paper-family-native-v1"
|
|
47
|
+
PAPER_COMMON_SUPPORT_PROTOCOL = "paper-common-support-v1"
|
|
48
|
+
PAPER_RETRIEVER_FACTORIAL_PROTOCOL = "paper-retriever-factorial-v1"
|
|
49
|
+
PAPER_SELECTOR_PROTOCOL = "paper-selector-14-policy-v1"
|
|
50
|
+
LEGACY_CONFIRMATORY_PROTOCOL = "legacy-11-arm-v1"
|
|
51
|
+
|
|
52
|
+
_CENSUS_PROTOCOL = "agentinputlab.experiment_census.v1"
|
|
53
|
+
_FAMILY_NATIVE_KIND = "family_native"
|
|
54
|
+
_COMMON_SUPPORT_KIND = "common_support"
|
|
55
|
+
_RETRIEVER_FACTORIAL_KIND = "retriever_factorial_cell"
|
|
56
|
+
_RETRIEVER_FACTORIAL_PRIMARY_KIND = "retriever_factorial_primary_cell"
|
|
57
|
+
_RETRIEVER_FACTORIAL_AUXILIARY_KIND = "retriever_factorial_auxiliary_cell"
|
|
58
|
+
_PAPER_SELECTOR_POLICIES = frozenset(
|
|
59
|
+
{
|
|
60
|
+
"no_op",
|
|
61
|
+
"random_eligible",
|
|
62
|
+
"fixed_reformulate",
|
|
63
|
+
"fixed_decompose",
|
|
64
|
+
"fixed_acquire",
|
|
65
|
+
"fixed_clarify",
|
|
66
|
+
"fixed_curate",
|
|
67
|
+
"best_fixed_global",
|
|
68
|
+
"defect_rule",
|
|
69
|
+
"zero_shot_router",
|
|
70
|
+
"penalized_t_learner",
|
|
71
|
+
"gbdt_t_learner",
|
|
72
|
+
"eligibility_aware_selector",
|
|
73
|
+
"observed_action_oracle",
|
|
74
|
+
}
|
|
75
|
+
)
|
|
76
|
+
_PAPER_RETRIEVERS = ("BAAI/bge-m3", "intfloat/e5-large-v2")
|
|
77
|
+
_FORMAL_PAPER_RETRIEVERS = (
|
|
78
|
+
"bm25",
|
|
79
|
+
"intfloat/e5-large-v2",
|
|
80
|
+
"BAAI/bge-m3",
|
|
81
|
+
"Qwen/Qwen3-Embedding-0.6B",
|
|
82
|
+
)
|
|
83
|
+
_PAPER_TOP_K = (5, 20)
|
|
84
|
+
_SELECTOR_GATE_ARTIFACT = "agentinputlab.paper_selector_claim_gates"
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@dataclass(frozen=True, slots=True)
|
|
88
|
+
class _VerifiedPaperRun:
|
|
89
|
+
root: Path
|
|
90
|
+
manifest: ExperimentManifest
|
|
91
|
+
records: tuple[Mapping[str, Any], ...]
|
|
92
|
+
campaign_kind: str
|
|
93
|
+
independent_groups: int
|
|
94
|
+
expected_plan_cells: int
|
|
95
|
+
expected_agent_episodes: int
|
|
96
|
+
cluster_ids: frozenset[str]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def infer_paper_run_protocol(run_dir: str | Path) -> str:
|
|
100
|
+
"""Infer a paper protocol only from a frozen manifest census declaration."""
|
|
101
|
+
|
|
102
|
+
manifest = _load_manifest(Path(run_dir).expanduser().resolve())
|
|
103
|
+
census = manifest.metadata.get("experiment_census")
|
|
104
|
+
if not isinstance(census, Mapping):
|
|
105
|
+
return LEGACY_CONFIRMATORY_PROTOCOL
|
|
106
|
+
kind = census.get("campaign_kind")
|
|
107
|
+
if kind == _FAMILY_NATIVE_KIND:
|
|
108
|
+
return PAPER_FAMILY_NATIVE_PROTOCOL
|
|
109
|
+
if kind == _COMMON_SUPPORT_KIND:
|
|
110
|
+
return PAPER_COMMON_SUPPORT_PROTOCOL
|
|
111
|
+
if kind in {
|
|
112
|
+
_RETRIEVER_FACTORIAL_KIND,
|
|
113
|
+
_RETRIEVER_FACTORIAL_PRIMARY_KIND,
|
|
114
|
+
_RETRIEVER_FACTORIAL_AUXILIARY_KIND,
|
|
115
|
+
}:
|
|
116
|
+
return PAPER_RETRIEVER_FACTORIAL_PROTOCOL
|
|
117
|
+
return LEGACY_CONFIRMATORY_PROTOCOL
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def analyze_confirmatory_protocol(
|
|
121
|
+
run_dirs: Sequence[str | Path],
|
|
122
|
+
*,
|
|
123
|
+
protocol: str = "auto",
|
|
124
|
+
metric: str = "valid_success",
|
|
125
|
+
algorithm_failure_value: float | None = None,
|
|
126
|
+
bootstrap_samples: int = 2_000,
|
|
127
|
+
permutation_samples: int = 2_000,
|
|
128
|
+
random_seed: int = 2027,
|
|
129
|
+
) -> Mapping[str, Any]:
|
|
130
|
+
"""Route old and paper-v1 confirmatory layouts without weakening either."""
|
|
131
|
+
|
|
132
|
+
roots = _run_roots(run_dirs)
|
|
133
|
+
selected = protocol
|
|
134
|
+
if selected == "auto":
|
|
135
|
+
inferred = {infer_paper_run_protocol(root) for root in roots}
|
|
136
|
+
if len(inferred) != 1:
|
|
137
|
+
raise ValidationError("confirmatory run directories mix incompatible protocols")
|
|
138
|
+
selected = next(iter(inferred))
|
|
139
|
+
if selected == LEGACY_CONFIRMATORY_PROTOCOL:
|
|
140
|
+
if len(roots) != 1:
|
|
141
|
+
raise ValidationError("legacy 11-arm confirmation accepts exactly one run directory")
|
|
142
|
+
return analyze_confirmatory_interactions(
|
|
143
|
+
roots[0],
|
|
144
|
+
metric=metric,
|
|
145
|
+
algorithm_failure_value=algorithm_failure_value,
|
|
146
|
+
bootstrap_samples=bootstrap_samples,
|
|
147
|
+
permutation_samples=permutation_samples,
|
|
148
|
+
random_seed=random_seed,
|
|
149
|
+
)
|
|
150
|
+
if selected == PAPER_FAMILY_NATIVE_PROTOCOL:
|
|
151
|
+
return analyze_paper_family_confirmation(
|
|
152
|
+
roots,
|
|
153
|
+
metric=metric,
|
|
154
|
+
algorithm_failure_value=algorithm_failure_value,
|
|
155
|
+
bootstrap_samples=bootstrap_samples,
|
|
156
|
+
permutation_samples=permutation_samples,
|
|
157
|
+
random_seed=random_seed,
|
|
158
|
+
)
|
|
159
|
+
if selected == PAPER_COMMON_SUPPORT_PROTOCOL:
|
|
160
|
+
if len(roots) != 1:
|
|
161
|
+
raise ValidationError("paper common-support analysis accepts exactly one run directory")
|
|
162
|
+
return analyze_paper_common_support(
|
|
163
|
+
roots[0],
|
|
164
|
+
metric=metric,
|
|
165
|
+
algorithm_failure_value=algorithm_failure_value,
|
|
166
|
+
bootstrap_samples=bootstrap_samples,
|
|
167
|
+
random_seed=random_seed,
|
|
168
|
+
)
|
|
169
|
+
if selected == PAPER_RETRIEVER_FACTORIAL_PROTOCOL:
|
|
170
|
+
analyzer = (
|
|
171
|
+
analyze_paper_dual_arena_retriever_factorial
|
|
172
|
+
if len(roots) == 16
|
|
173
|
+
else analyze_paper_retriever_factorial
|
|
174
|
+
)
|
|
175
|
+
return analyzer(
|
|
176
|
+
roots,
|
|
177
|
+
metric=metric,
|
|
178
|
+
algorithm_failure_value=algorithm_failure_value,
|
|
179
|
+
bootstrap_samples=bootstrap_samples,
|
|
180
|
+
random_seed=random_seed,
|
|
181
|
+
)
|
|
182
|
+
raise ValidationError(f"unsupported confirmatory analysis protocol: {selected!r}")
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def analyze_paper_family_confirmation(
|
|
186
|
+
run_dirs: Sequence[str | Path],
|
|
187
|
+
*,
|
|
188
|
+
metric: str,
|
|
189
|
+
algorithm_failure_value: float | None = None,
|
|
190
|
+
bootstrap_samples: int = 2_000,
|
|
191
|
+
permutation_samples: int = 2_000,
|
|
192
|
+
random_seed: int = 2027,
|
|
193
|
+
) -> Mapping[str, Any]:
|
|
194
|
+
"""Analyze the exact five-run, three-arm family-native paper protocol."""
|
|
195
|
+
|
|
196
|
+
_validate_resample_arguments(
|
|
197
|
+
bootstrap_samples=bootstrap_samples,
|
|
198
|
+
permutation_samples=permutation_samples,
|
|
199
|
+
random_seed=random_seed,
|
|
200
|
+
)
|
|
201
|
+
roots = _run_roots(run_dirs)
|
|
202
|
+
if len(roots) != 5:
|
|
203
|
+
raise ValidationError("paper family confirmation requires exactly five run directories")
|
|
204
|
+
runs = tuple(
|
|
205
|
+
_load_and_validate_paper_run(root, expected_kind=_FAMILY_NATIVE_KIND) for root in roots
|
|
206
|
+
)
|
|
207
|
+
by_family: dict[SemanticFamily, _VerifiedPaperRun] = {}
|
|
208
|
+
all_clusters: set[str] = set()
|
|
209
|
+
for run in runs:
|
|
210
|
+
family = _family_native_arm_contract(run.manifest)
|
|
211
|
+
if family in by_family:
|
|
212
|
+
raise ArtifactError(f"duplicate family-native run for {family.value}")
|
|
213
|
+
overlap = all_clusters.intersection(run.cluster_ids)
|
|
214
|
+
if overlap:
|
|
215
|
+
raise ArtifactError(
|
|
216
|
+
"paper family-native runs reuse benchmark/unit groups across families"
|
|
217
|
+
)
|
|
218
|
+
all_clusters.update(run.cluster_ids)
|
|
219
|
+
by_family[family] = run
|
|
220
|
+
expected_families = set(_primary_families())
|
|
221
|
+
if set(by_family) != expected_families:
|
|
222
|
+
missing = sorted(family.value for family in expected_families - set(by_family))
|
|
223
|
+
raise ArtifactError(f"paper family confirmation is missing families: {missing}")
|
|
224
|
+
|
|
225
|
+
observations: dict[str, list[tuple[str, str, float, float]]] = {
|
|
226
|
+
family.value: [] for family in _primary_families()
|
|
227
|
+
}
|
|
228
|
+
model_effects: dict[str, dict[str, list[float]]] = defaultdict(lambda: defaultdict(list))
|
|
229
|
+
for family in _primary_families():
|
|
230
|
+
run = by_family[family]
|
|
231
|
+
baseline_arm_id, treatment_arm_ids = _family_native_arm_ids(run.manifest, family)
|
|
232
|
+
for record in run.records:
|
|
233
|
+
values = _arm_metrics(
|
|
234
|
+
record,
|
|
235
|
+
metric=metric,
|
|
236
|
+
algorithm_failure_value=algorithm_failure_value,
|
|
237
|
+
)
|
|
238
|
+
if values is None: # pragma: no cover - verified all-succeeded invariant
|
|
239
|
+
raise ArtifactError("family-native record is not a completed cell")
|
|
240
|
+
cell = record["cell"]
|
|
241
|
+
cluster_id = _cluster_id(cell)
|
|
242
|
+
model_slot = _required_record_text(cell, "model_slot", label="experiment cell")
|
|
243
|
+
defect = _pre_action_defect_scores(record)[family.value]
|
|
244
|
+
baseline = values[baseline_arm_id]
|
|
245
|
+
effect = fmean(values[arm_id] - baseline for arm_id in treatment_arm_ids)
|
|
246
|
+
observations[family.value].append((cluster_id, model_slot, defect, effect))
|
|
247
|
+
model_effects[model_slot][family.value].append(effect)
|
|
248
|
+
|
|
249
|
+
rng = random.Random(random_seed)
|
|
250
|
+
contrasts: dict[str, Any] = {}
|
|
251
|
+
raw_p_values: dict[str, float] = {}
|
|
252
|
+
for family in _primary_families():
|
|
253
|
+
family_name = family.value
|
|
254
|
+
collapsed = _collapse_confirmatory_clusters(observations[family_name])
|
|
255
|
+
effects = [row[2] for row in collapsed]
|
|
256
|
+
defects = [row[1] for row in collapsed]
|
|
257
|
+
slope = _linear_slope(defects, effects)
|
|
258
|
+
raw_p = _permutation_slope_p_value(
|
|
259
|
+
defects,
|
|
260
|
+
effects,
|
|
261
|
+
observed=slope,
|
|
262
|
+
samples=permutation_samples,
|
|
263
|
+
rng=rng,
|
|
264
|
+
)
|
|
265
|
+
raw_p_values[family_name] = raw_p
|
|
266
|
+
contrasts[family_name] = {
|
|
267
|
+
"cluster_count": len(collapsed),
|
|
268
|
+
"family_effect_mean": fmean(effects),
|
|
269
|
+
"family_effect_cluster_bootstrap_95ci": list(
|
|
270
|
+
_cluster_bootstrap_interval(effects, samples=bootstrap_samples, rng=rng)
|
|
271
|
+
),
|
|
272
|
+
"matched_defect_slope": slope,
|
|
273
|
+
"matched_defect_slope_cluster_bootstrap_95ci": list(
|
|
274
|
+
_paired_cluster_bootstrap_slope_interval(
|
|
275
|
+
defects,
|
|
276
|
+
effects,
|
|
277
|
+
samples=bootstrap_samples,
|
|
278
|
+
rng=rng,
|
|
279
|
+
)
|
|
280
|
+
),
|
|
281
|
+
"permutation_p_value_raw": raw_p,
|
|
282
|
+
}
|
|
283
|
+
for family_name, adjusted in _holm_adjust(raw_p_values).items():
|
|
284
|
+
contrasts[family_name]["holm_adjusted_p_value"] = adjusted
|
|
285
|
+
|
|
286
|
+
return {
|
|
287
|
+
"schema_version": 1,
|
|
288
|
+
"analysis": "paper_five_family_native_confirmation",
|
|
289
|
+
"analysis_protocol": PAPER_FAMILY_NATIVE_PROTOCOL,
|
|
290
|
+
"metric": metric,
|
|
291
|
+
"run_count": 5,
|
|
292
|
+
"independent_groups": 3_000,
|
|
293
|
+
"verified_plan_cells": 6_000,
|
|
294
|
+
"verified_agent_episodes": 18_000,
|
|
295
|
+
"arm_layout": "five disjoint runs: NO_OP + two frozen representatives",
|
|
296
|
+
"cluster_definition": "benchmark_id/split/unit_id",
|
|
297
|
+
"replicate_handling": (
|
|
298
|
+
"equal-weight mean over two frozen implementations, model slots, and "
|
|
299
|
+
"seeds within each benchmark/unit cluster"
|
|
300
|
+
),
|
|
301
|
+
"bootstrap_samples": bootstrap_samples,
|
|
302
|
+
"permutation_samples": permutation_samples,
|
|
303
|
+
"random_seed": random_seed,
|
|
304
|
+
"algorithm_failure_value": algorithm_failure_value,
|
|
305
|
+
"contrasts": contrasts,
|
|
306
|
+
"model_strata": {
|
|
307
|
+
model_slot: {
|
|
308
|
+
family: {
|
|
309
|
+
"count": len(values),
|
|
310
|
+
"mean_effect_vs_no_op": fmean(values),
|
|
311
|
+
}
|
|
312
|
+
for family, values in sorted(families.items())
|
|
313
|
+
}
|
|
314
|
+
for model_slot, families in sorted(model_effects.items())
|
|
315
|
+
},
|
|
316
|
+
"multiplicity": "Holm family-wise correction across the five matched contrasts",
|
|
317
|
+
"paper_claim_authorized": False,
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def analyze_paper_common_support(
|
|
322
|
+
run_dir: str | Path,
|
|
323
|
+
*,
|
|
324
|
+
metric: str,
|
|
325
|
+
algorithm_failure_value: float | None = None,
|
|
326
|
+
bootstrap_samples: int = 2_000,
|
|
327
|
+
random_seed: int = 2027,
|
|
328
|
+
) -> Mapping[str, Any]:
|
|
329
|
+
"""Analyze the exact 526-group, six-arm horizontal-comparison block."""
|
|
330
|
+
|
|
331
|
+
_validate_resample_arguments(
|
|
332
|
+
bootstrap_samples=bootstrap_samples,
|
|
333
|
+
permutation_samples=1,
|
|
334
|
+
random_seed=random_seed,
|
|
335
|
+
)
|
|
336
|
+
run = _load_and_validate_paper_run(run_dir, expected_kind=_COMMON_SUPPORT_KIND)
|
|
337
|
+
baseline, arms = _common_support_arm_contract(run.manifest)
|
|
338
|
+
rows_by_cluster: dict[str, list[Mapping[str, float]]] = defaultdict(list)
|
|
339
|
+
model_effects: dict[str, dict[str, list[float]]] = defaultdict(lambda: defaultdict(list))
|
|
340
|
+
for record in run.records:
|
|
341
|
+
values = _arm_metrics(
|
|
342
|
+
record,
|
|
343
|
+
metric=metric,
|
|
344
|
+
algorithm_failure_value=algorithm_failure_value,
|
|
345
|
+
)
|
|
346
|
+
if values is None: # pragma: no cover
|
|
347
|
+
raise ArtifactError("common-support record is not a completed cell")
|
|
348
|
+
cluster_id = _cluster_id(record["cell"])
|
|
349
|
+
model_slot = _required_record_text(
|
|
350
|
+
record["cell"],
|
|
351
|
+
"model_slot",
|
|
352
|
+
label="experiment cell",
|
|
353
|
+
)
|
|
354
|
+
row = {"no_op": values[baseline]}
|
|
355
|
+
for family, arm_id in arms.items():
|
|
356
|
+
row[family.value] = values[arm_id]
|
|
357
|
+
model_effects[model_slot][family.value].append(values[arm_id] - values[baseline])
|
|
358
|
+
rows_by_cluster[cluster_id].append(row)
|
|
359
|
+
collapsed = {
|
|
360
|
+
cluster_id: {
|
|
361
|
+
key: fmean(float(row[key]) for row in rows)
|
|
362
|
+
for key in ("no_op", *(family.value for family in _primary_families()))
|
|
363
|
+
}
|
|
364
|
+
for cluster_id, rows in sorted(rows_by_cluster.items())
|
|
365
|
+
}
|
|
366
|
+
if len(collapsed) != 526:
|
|
367
|
+
raise ArtifactError("paper common-support analysis requires exactly 526 clusters")
|
|
368
|
+
|
|
369
|
+
rng = random.Random(random_seed)
|
|
370
|
+
family_summaries: dict[str, Any] = {}
|
|
371
|
+
for family in _primary_families():
|
|
372
|
+
effects = [row[family.value] - row["no_op"] for row in collapsed.values()]
|
|
373
|
+
family_summaries[family.value] = {
|
|
374
|
+
"cluster_count": len(effects),
|
|
375
|
+
"mean_value": fmean(row[family.value] for row in collapsed.values()),
|
|
376
|
+
"mean_effect_vs_no_op": fmean(effects),
|
|
377
|
+
"cluster_bootstrap_95ci": list(
|
|
378
|
+
_cluster_bootstrap_interval(effects, samples=bootstrap_samples, rng=rng)
|
|
379
|
+
),
|
|
380
|
+
}
|
|
381
|
+
pairwise: dict[str, Any] = {}
|
|
382
|
+
families = _primary_families()
|
|
383
|
+
for left_index, left in enumerate(families):
|
|
384
|
+
for right in families[left_index + 1 :]:
|
|
385
|
+
effects = [row[left.value] - row[right.value] for row in collapsed.values()]
|
|
386
|
+
pairwise[f"{left.value}_vs_{right.value}"] = {
|
|
387
|
+
"mean_difference": fmean(effects),
|
|
388
|
+
"cluster_bootstrap_95ci": list(
|
|
389
|
+
_cluster_bootstrap_interval(effects, samples=bootstrap_samples, rng=rng)
|
|
390
|
+
),
|
|
391
|
+
}
|
|
392
|
+
ranking = sorted(
|
|
393
|
+
(
|
|
394
|
+
{
|
|
395
|
+
"family": family.value,
|
|
396
|
+
"mean_value": family_summaries[family.value]["mean_value"],
|
|
397
|
+
}
|
|
398
|
+
for family in families
|
|
399
|
+
),
|
|
400
|
+
key=lambda row: (-float(row["mean_value"]), str(row["family"])),
|
|
401
|
+
)
|
|
402
|
+
return {
|
|
403
|
+
"schema_version": 1,
|
|
404
|
+
"analysis": "paper_common_support_horizontal_comparison",
|
|
405
|
+
"analysis_protocol": PAPER_COMMON_SUPPORT_PROTOCOL,
|
|
406
|
+
"metric": metric,
|
|
407
|
+
"run_count": 1,
|
|
408
|
+
"independent_groups": 526,
|
|
409
|
+
"verified_plan_cells": 1_052,
|
|
410
|
+
"verified_agent_episodes": 6_312,
|
|
411
|
+
"arm_layout": "NO_OP + one frozen representative per family",
|
|
412
|
+
"minimum_groups_for_ranking": 400,
|
|
413
|
+
"ranking_allowed": len(collapsed) >= 400,
|
|
414
|
+
"family_summaries": family_summaries,
|
|
415
|
+
"pairwise_family_differences": pairwise,
|
|
416
|
+
"descriptive_ranking": ranking,
|
|
417
|
+
"model_strata": {
|
|
418
|
+
model_slot: {
|
|
419
|
+
family: {
|
|
420
|
+
"count": len(values),
|
|
421
|
+
"mean_effect_vs_no_op": fmean(values),
|
|
422
|
+
}
|
|
423
|
+
for family, values in sorted(by_family.items())
|
|
424
|
+
}
|
|
425
|
+
for model_slot, by_family in sorted(model_effects.items())
|
|
426
|
+
},
|
|
427
|
+
"scope_note": (
|
|
428
|
+
"Bounded common-support population; family-native effects remain primary. "
|
|
429
|
+
"No multiplicity-adjusted superiority claim is authorized here."
|
|
430
|
+
),
|
|
431
|
+
"bootstrap_samples": bootstrap_samples,
|
|
432
|
+
"random_seed": random_seed,
|
|
433
|
+
"algorithm_failure_value": algorithm_failure_value,
|
|
434
|
+
"paper_claim_authorized": False,
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def analyze_paper_retriever_factorial(
|
|
439
|
+
run_dirs: Sequence[str | Path],
|
|
440
|
+
*,
|
|
441
|
+
metric: str,
|
|
442
|
+
algorithm_failure_value: float | None = None,
|
|
443
|
+
bootstrap_samples: int = 2_000,
|
|
444
|
+
random_seed: int = 2027,
|
|
445
|
+
) -> Mapping[str, Any]:
|
|
446
|
+
"""Analyze four matched retriever-by-top-k cells with shared unit IDs."""
|
|
447
|
+
|
|
448
|
+
_validate_resample_arguments(
|
|
449
|
+
bootstrap_samples=bootstrap_samples,
|
|
450
|
+
permutation_samples=1,
|
|
451
|
+
random_seed=random_seed,
|
|
452
|
+
)
|
|
453
|
+
roots = _run_roots(run_dirs)
|
|
454
|
+
if len(roots) != 4:
|
|
455
|
+
raise ValidationError("paper retriever factorial requires exactly four run directories")
|
|
456
|
+
runs = tuple(
|
|
457
|
+
_load_and_validate_paper_run(root, expected_kind=_RETRIEVER_FACTORIAL_KIND)
|
|
458
|
+
for root in roots
|
|
459
|
+
)
|
|
460
|
+
by_cell: dict[tuple[str, int], _VerifiedPaperRun] = {}
|
|
461
|
+
reference_arms: tuple[tuple[str, str, str], ...] | None = None
|
|
462
|
+
reference_clusters: frozenset[str] | None = None
|
|
463
|
+
reference_targets: tuple[tuple[str, str], ...] | None = None
|
|
464
|
+
reference_models: tuple[str, ...] | None = None
|
|
465
|
+
reference_seeds: tuple[int, ...] | None = None
|
|
466
|
+
for run in runs:
|
|
467
|
+
key = _retriever_dependency_cell(run.manifest)
|
|
468
|
+
if key in by_cell:
|
|
469
|
+
raise ArtifactError(f"duplicate retriever-factorial dependency cell: {key}")
|
|
470
|
+
_retriever_arm_contract(run.manifest)
|
|
471
|
+
arm_identity = tuple(
|
|
472
|
+
(arm.arm_id, arm.release_id, arm.family.value) for arm in run.manifest.arms
|
|
473
|
+
)
|
|
474
|
+
targets = tuple((target.cell_id, target.split) for target in run.manifest.benchmarks)
|
|
475
|
+
if reference_arms is None:
|
|
476
|
+
reference_arms = arm_identity
|
|
477
|
+
reference_clusters = run.cluster_ids
|
|
478
|
+
reference_targets = targets
|
|
479
|
+
reference_models = run.manifest.model_slots
|
|
480
|
+
reference_seeds = run.manifest.seeds
|
|
481
|
+
elif (
|
|
482
|
+
arm_identity != reference_arms
|
|
483
|
+
or run.cluster_ids != reference_clusters
|
|
484
|
+
or targets != reference_targets
|
|
485
|
+
or run.manifest.model_slots != reference_models
|
|
486
|
+
or run.manifest.seeds != reference_seeds
|
|
487
|
+
):
|
|
488
|
+
raise ArtifactError(
|
|
489
|
+
"retriever-factorial cells must share arms, groups, targets, model slots, and seeds"
|
|
490
|
+
)
|
|
491
|
+
by_cell[key] = run
|
|
492
|
+
expected_cells = {
|
|
493
|
+
(retriever, top_k) for retriever in _PAPER_RETRIEVERS for top_k in _PAPER_TOP_K
|
|
494
|
+
}
|
|
495
|
+
if set(by_cell) != expected_cells:
|
|
496
|
+
raise ArtifactError("retriever-factorial runs do not form the frozen 2x2 dependency grid")
|
|
497
|
+
dependency_receipts = {key: _retriever_runtime_receipt(run) for key, run in by_cell.items()}
|
|
498
|
+
dependency_balance = _validate_retriever_factorial_balance(dependency_receipts)
|
|
499
|
+
|
|
500
|
+
effects: dict[tuple[str, int], dict[str, dict[str, float]]] = {}
|
|
501
|
+
cell_summaries: dict[str, Any] = {}
|
|
502
|
+
rng = random.Random(random_seed)
|
|
503
|
+
for key in sorted(by_cell):
|
|
504
|
+
run = by_cell[key]
|
|
505
|
+
baseline, treatment_arms = _retriever_arm_contract(run.manifest)
|
|
506
|
+
by_cluster: dict[str, dict[str, float]] = {}
|
|
507
|
+
for record in run.records:
|
|
508
|
+
values = _arm_metrics(
|
|
509
|
+
record,
|
|
510
|
+
metric=metric,
|
|
511
|
+
algorithm_failure_value=algorithm_failure_value,
|
|
512
|
+
)
|
|
513
|
+
if values is None: # pragma: no cover
|
|
514
|
+
raise ArtifactError("retriever-factorial record is not a completed cell")
|
|
515
|
+
cluster_id = _cluster_id(record["cell"])
|
|
516
|
+
by_cluster[cluster_id] = {
|
|
517
|
+
arm_id: values[arm_id] - values[baseline] for arm_id in treatment_arms
|
|
518
|
+
}
|
|
519
|
+
effects[key] = by_cluster
|
|
520
|
+
label = f"{key[0]}::k{key[1]}"
|
|
521
|
+
cell_summaries[label] = {
|
|
522
|
+
"retriever_id": key[0],
|
|
523
|
+
"top_k": key[1],
|
|
524
|
+
"independent_groups": len(by_cluster),
|
|
525
|
+
"arms": {
|
|
526
|
+
arm_id: {
|
|
527
|
+
"mean_effect_vs_no_op": fmean(row[arm_id] for row in by_cluster.values()),
|
|
528
|
+
"cluster_bootstrap_95ci": list(
|
|
529
|
+
_cluster_bootstrap_interval(
|
|
530
|
+
[row[arm_id] for row in by_cluster.values()],
|
|
531
|
+
samples=bootstrap_samples,
|
|
532
|
+
rng=rng,
|
|
533
|
+
)
|
|
534
|
+
),
|
|
535
|
+
}
|
|
536
|
+
for arm_id in treatment_arms
|
|
537
|
+
},
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
assert reference_clusters is not None # established by four non-empty runs
|
|
541
|
+
assert reference_arms is not None
|
|
542
|
+
treatment_arm_ids = tuple(item[0] for item in reference_arms if item[2] != "no_op")
|
|
543
|
+
interactions: dict[str, Any] = {}
|
|
544
|
+
bge, e5 = _PAPER_RETRIEVERS
|
|
545
|
+
k5, k20 = _PAPER_TOP_K
|
|
546
|
+
for arm_id in treatment_arm_ids:
|
|
547
|
+
retriever_contrast = []
|
|
548
|
+
top_k_contrast = []
|
|
549
|
+
joint_contrast = []
|
|
550
|
+
for cluster_id in sorted(reference_clusters):
|
|
551
|
+
bge_k5 = effects[(bge, k5)][cluster_id][arm_id]
|
|
552
|
+
bge_k20 = effects[(bge, k20)][cluster_id][arm_id]
|
|
553
|
+
e5_k5 = effects[(e5, k5)][cluster_id][arm_id]
|
|
554
|
+
e5_k20 = effects[(e5, k20)][cluster_id][arm_id]
|
|
555
|
+
retriever_contrast.append((bge_k5 + bge_k20 - e5_k5 - e5_k20) / 2.0)
|
|
556
|
+
top_k_contrast.append((bge_k20 + e5_k20 - bge_k5 - e5_k5) / 2.0)
|
|
557
|
+
joint_contrast.append((bge_k20 - bge_k5) - (e5_k20 - e5_k5))
|
|
558
|
+
interactions[arm_id] = {
|
|
559
|
+
"retriever_bge_minus_e5": _effect_summary(
|
|
560
|
+
retriever_contrast,
|
|
561
|
+
bootstrap_samples=bootstrap_samples,
|
|
562
|
+
rng=rng,
|
|
563
|
+
),
|
|
564
|
+
"top_k_20_minus_5": _effect_summary(
|
|
565
|
+
top_k_contrast,
|
|
566
|
+
bootstrap_samples=bootstrap_samples,
|
|
567
|
+
rng=rng,
|
|
568
|
+
),
|
|
569
|
+
"retriever_by_top_k": _effect_summary(
|
|
570
|
+
joint_contrast,
|
|
571
|
+
bootstrap_samples=bootstrap_samples,
|
|
572
|
+
rng=rng,
|
|
573
|
+
),
|
|
574
|
+
}
|
|
575
|
+
return {
|
|
576
|
+
"schema_version": 1,
|
|
577
|
+
"analysis": "paper_retriever_by_top_k_factorial",
|
|
578
|
+
"analysis_protocol": PAPER_RETRIEVER_FACTORIAL_PROTOCOL,
|
|
579
|
+
"metric": metric,
|
|
580
|
+
"run_count": 4,
|
|
581
|
+
"independent_groups": 240,
|
|
582
|
+
"verified_plan_cells": 960,
|
|
583
|
+
"verified_agent_episodes": 4_800,
|
|
584
|
+
"dependency_cells": cell_summaries,
|
|
585
|
+
"dependency_balance": dependency_balance,
|
|
586
|
+
"interactions": interactions,
|
|
587
|
+
"cluster_definition": "same benchmark_id/split/unit_id replayed in all four cells",
|
|
588
|
+
"bootstrap_samples": bootstrap_samples,
|
|
589
|
+
"random_seed": random_seed,
|
|
590
|
+
"algorithm_failure_value": algorithm_failure_value,
|
|
591
|
+
"paper_claim_authorized": False,
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
def analyze_paper_dual_arena_retriever_factorial(
|
|
596
|
+
run_dirs: Sequence[str | Path],
|
|
597
|
+
*,
|
|
598
|
+
metric: str,
|
|
599
|
+
algorithm_failure_value: float | None = None,
|
|
600
|
+
bootstrap_samples: int = 2_000,
|
|
601
|
+
random_seed: int = 2027,
|
|
602
|
+
) -> Mapping[str, Any]:
|
|
603
|
+
"""Analyze the formal 4-retriever x 2-top-k design in τ³ and BRIGHT.
|
|
604
|
+
|
|
605
|
+
τ³ is the primary estimand. BRIGHT is reported separately as a directional
|
|
606
|
+
auxiliary validation; its observations are never pooled with τ³.
|
|
607
|
+
"""
|
|
608
|
+
|
|
609
|
+
_validate_resample_arguments(
|
|
610
|
+
bootstrap_samples=bootstrap_samples,
|
|
611
|
+
permutation_samples=1,
|
|
612
|
+
random_seed=random_seed,
|
|
613
|
+
)
|
|
614
|
+
roots = _run_roots(run_dirs)
|
|
615
|
+
if len(roots) != 16:
|
|
616
|
+
raise ValidationError("formal retriever factorial requires exactly sixteen run directories")
|
|
617
|
+
by_role: dict[str, list[_VerifiedPaperRun]] = {"primary": [], "auxiliary": []}
|
|
618
|
+
for root in roots:
|
|
619
|
+
manifest = _load_manifest(root)
|
|
620
|
+
dependency = manifest.metadata.get("dependency_cell")
|
|
621
|
+
if not isinstance(dependency, Mapping):
|
|
622
|
+
raise ArtifactError("formal retriever-factorial run lacks dependency metadata")
|
|
623
|
+
role = dependency.get("arena_role")
|
|
624
|
+
if role not in by_role:
|
|
625
|
+
raise ArtifactError("formal retriever-factorial arena role is invalid")
|
|
626
|
+
expected_kind = (
|
|
627
|
+
_RETRIEVER_FACTORIAL_PRIMARY_KIND
|
|
628
|
+
if role == "primary"
|
|
629
|
+
else _RETRIEVER_FACTORIAL_AUXILIARY_KIND
|
|
630
|
+
)
|
|
631
|
+
by_role[str(role)].append(_load_and_validate_paper_run(root, expected_kind=expected_kind))
|
|
632
|
+
if any(len(runs) != 8 for runs in by_role.values()):
|
|
633
|
+
raise ArtifactError("formal retriever factorial requires eight cells in each arena")
|
|
634
|
+
|
|
635
|
+
rng = random.Random(random_seed)
|
|
636
|
+
analyses = {
|
|
637
|
+
role: _analyze_formal_retriever_arena(
|
|
638
|
+
runs,
|
|
639
|
+
role=role,
|
|
640
|
+
metric=metric,
|
|
641
|
+
algorithm_failure_value=algorithm_failure_value,
|
|
642
|
+
bootstrap_samples=bootstrap_samples,
|
|
643
|
+
rng=rng,
|
|
644
|
+
)
|
|
645
|
+
for role, runs in by_role.items()
|
|
646
|
+
}
|
|
647
|
+
return {
|
|
648
|
+
"schema_version": 2,
|
|
649
|
+
"analysis": "paper_dual_arena_retriever_by_top_k_factorial",
|
|
650
|
+
"analysis_protocol": PAPER_RETRIEVER_FACTORIAL_PROTOCOL,
|
|
651
|
+
"metric": metric,
|
|
652
|
+
"run_count": 16,
|
|
653
|
+
"primary": analyses["primary"],
|
|
654
|
+
"auxiliary": analyses["auxiliary"],
|
|
655
|
+
"claim_order": [
|
|
656
|
+
"tau3_primary_retriever_factorial",
|
|
657
|
+
"bright_auxiliary_directional_validation",
|
|
658
|
+
],
|
|
659
|
+
"cross_arena_pooling": False,
|
|
660
|
+
"bootstrap_samples": bootstrap_samples,
|
|
661
|
+
"random_seed": random_seed,
|
|
662
|
+
"algorithm_failure_value": algorithm_failure_value,
|
|
663
|
+
"paper_claim_authorized": False,
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
|
|
667
|
+
def _analyze_formal_retriever_arena(
|
|
668
|
+
runs: Sequence[_VerifiedPaperRun],
|
|
669
|
+
*,
|
|
670
|
+
role: str,
|
|
671
|
+
metric: str,
|
|
672
|
+
algorithm_failure_value: float | None,
|
|
673
|
+
bootstrap_samples: int,
|
|
674
|
+
rng: random.Random,
|
|
675
|
+
) -> Mapping[str, Any]:
|
|
676
|
+
by_cell: dict[tuple[str, int], _VerifiedPaperRun] = {}
|
|
677
|
+
reference: _VerifiedPaperRun | None = None
|
|
678
|
+
for run in runs:
|
|
679
|
+
key = _retriever_dependency_cell(run.manifest, formal=True)
|
|
680
|
+
if key in by_cell:
|
|
681
|
+
raise ArtifactError(f"duplicate formal retriever-factorial cell: {key}")
|
|
682
|
+
_retriever_arm_contract(run.manifest)
|
|
683
|
+
if reference is None:
|
|
684
|
+
reference = run
|
|
685
|
+
elif (
|
|
686
|
+
tuple((arm.arm_id, arm.release_id, arm.family.value) for arm in run.manifest.arms)
|
|
687
|
+
!= tuple(
|
|
688
|
+
(arm.arm_id, arm.release_id, arm.family.value) for arm in reference.manifest.arms
|
|
689
|
+
)
|
|
690
|
+
or run.cluster_ids != reference.cluster_ids
|
|
691
|
+
or run.manifest.model_slots != reference.manifest.model_slots
|
|
692
|
+
or run.manifest.seeds != reference.manifest.seeds
|
|
693
|
+
or tuple((cell.cell_id, cell.split) for cell in run.manifest.benchmarks)
|
|
694
|
+
!= tuple((cell.cell_id, cell.split) for cell in reference.manifest.benchmarks)
|
|
695
|
+
):
|
|
696
|
+
raise ArtifactError(
|
|
697
|
+
"formal retriever-factorial cells must share arms, groups, targets, models, and seeds"
|
|
698
|
+
)
|
|
699
|
+
by_cell[key] = run
|
|
700
|
+
expected = {
|
|
701
|
+
(retriever, top_k) for retriever in _FORMAL_PAPER_RETRIEVERS for top_k in _PAPER_TOP_K
|
|
702
|
+
}
|
|
703
|
+
if set(by_cell) != expected:
|
|
704
|
+
raise ArtifactError("formal retriever-factorial cells do not form the frozen 4x2 grid")
|
|
705
|
+
dependency_balance = _validate_formal_retriever_factorial_balance(
|
|
706
|
+
{key: _formal_retriever_runtime_receipt(run) for key, run in by_cell.items()}
|
|
707
|
+
)
|
|
708
|
+
assert reference is not None
|
|
709
|
+
baseline, treatment_arms = _retriever_arm_contract(reference.manifest)
|
|
710
|
+
effects: dict[tuple[str, int], dict[str, dict[str, dict[str, float]]]] = {}
|
|
711
|
+
cell_summaries: dict[str, Any] = {}
|
|
712
|
+
for key, run in sorted(by_cell.items()):
|
|
713
|
+
by_group: dict[str, dict[str, dict[str, float]]] = defaultdict(dict)
|
|
714
|
+
for record in run.records:
|
|
715
|
+
values = _arm_metrics(
|
|
716
|
+
record,
|
|
717
|
+
metric=metric,
|
|
718
|
+
algorithm_failure_value=algorithm_failure_value,
|
|
719
|
+
)
|
|
720
|
+
if values is None: # pragma: no cover
|
|
721
|
+
raise ArtifactError("formal retriever-factorial record is incomplete")
|
|
722
|
+
cell = record["cell"]
|
|
723
|
+
group_id = _cluster_id(cell)
|
|
724
|
+
model_slot = _required_record_text(cell, "model_slot", label="experiment cell")
|
|
725
|
+
by_group[group_id][model_slot] = {
|
|
726
|
+
arm_id: values[arm_id] - values[baseline] for arm_id in treatment_arms
|
|
727
|
+
}
|
|
728
|
+
if any(set(models) != set(run.manifest.model_slots) for models in by_group.values()):
|
|
729
|
+
raise ArtifactError("formal retriever cell lacks the exact two-model group grid")
|
|
730
|
+
effects[key] = by_group
|
|
731
|
+
cell_summaries[f"{key[0]}::k{key[1]}"] = {
|
|
732
|
+
"retriever_id": key[0],
|
|
733
|
+
"top_k": key[1],
|
|
734
|
+
"independent_groups": len(by_group),
|
|
735
|
+
"model_slots": list(run.manifest.model_slots),
|
|
736
|
+
"arms": {
|
|
737
|
+
arm_id: _effect_summary(
|
|
738
|
+
[
|
|
739
|
+
fmean(model_values[arm_id] for model_values in models.values())
|
|
740
|
+
for models in by_group.values()
|
|
741
|
+
],
|
|
742
|
+
bootstrap_samples=bootstrap_samples,
|
|
743
|
+
rng=rng,
|
|
744
|
+
)
|
|
745
|
+
for arm_id in treatment_arms
|
|
746
|
+
},
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
interactions: dict[str, Any] = {}
|
|
750
|
+
reference_retriever = "BAAI/bge-m3"
|
|
751
|
+
for arm_id in treatment_arms:
|
|
752
|
+
top_k_contrast: list[float] = []
|
|
753
|
+
retriever_contrasts: dict[str, list[float]] = {
|
|
754
|
+
retriever: []
|
|
755
|
+
for retriever in _FORMAL_PAPER_RETRIEVERS
|
|
756
|
+
if retriever != reference_retriever
|
|
757
|
+
}
|
|
758
|
+
for group_id in sorted(reference.cluster_ids):
|
|
759
|
+
group_top_k_contrasts: list[float] = []
|
|
760
|
+
reference_mean: float | None = None
|
|
761
|
+
for retriever in _FORMAL_PAPER_RETRIEVERS:
|
|
762
|
+
low = fmean(values[arm_id] for values in effects[(retriever, 5)][group_id].values())
|
|
763
|
+
high = fmean(
|
|
764
|
+
values[arm_id] for values in effects[(retriever, 20)][group_id].values()
|
|
765
|
+
)
|
|
766
|
+
if retriever == reference_retriever:
|
|
767
|
+
reference_mean = (low + high) / 2.0
|
|
768
|
+
group_top_k_contrasts.append(high - low)
|
|
769
|
+
top_k_contrast.append(fmean(group_top_k_contrasts))
|
|
770
|
+
if reference_mean is None: # pragma: no cover - frozen grid contains BGE-M3
|
|
771
|
+
raise ArtifactError("formal retriever grid lacks its BGE-M3 reference")
|
|
772
|
+
for retriever in retriever_contrasts:
|
|
773
|
+
candidate = fmean(
|
|
774
|
+
fmean(values[arm_id] for values in effects[(retriever, k)][group_id].values())
|
|
775
|
+
for k in _PAPER_TOP_K
|
|
776
|
+
)
|
|
777
|
+
retriever_contrasts[retriever].append(candidate - reference_mean)
|
|
778
|
+
interactions[arm_id] = {
|
|
779
|
+
"top_k_20_minus_5": _effect_summary(
|
|
780
|
+
top_k_contrast,
|
|
781
|
+
bootstrap_samples=bootstrap_samples,
|
|
782
|
+
rng=rng,
|
|
783
|
+
),
|
|
784
|
+
"retriever_minus_bge_m3": {
|
|
785
|
+
retriever: _effect_summary(
|
|
786
|
+
values,
|
|
787
|
+
bootstrap_samples=bootstrap_samples,
|
|
788
|
+
rng=rng,
|
|
789
|
+
)
|
|
790
|
+
for retriever, values in retriever_contrasts.items()
|
|
791
|
+
},
|
|
792
|
+
}
|
|
793
|
+
return {
|
|
794
|
+
"arena_role": role,
|
|
795
|
+
"benchmark_id": reference.manifest.benchmarks[0].cell_id,
|
|
796
|
+
"run_count": 8,
|
|
797
|
+
"independent_groups": reference.independent_groups,
|
|
798
|
+
"verified_plan_cells": sum(run.expected_plan_cells for run in runs),
|
|
799
|
+
"verified_agent_episodes": sum(run.expected_agent_episodes for run in runs),
|
|
800
|
+
"dependency_cells": cell_summaries,
|
|
801
|
+
"dependency_balance": dependency_balance,
|
|
802
|
+
"interactions": interactions,
|
|
803
|
+
"cluster_definition": "same benchmark/split/unit replayed across all eight cells",
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
|
|
807
|
+
def _formal_retriever_runtime_receipt(run: _VerifiedPaperRun) -> Mapping[str, Any]:
|
|
808
|
+
expected_arm_ids = {arm.arm_id for arm in run.manifest.arms}
|
|
809
|
+
required_slots = {"backend", "retriever", "downstream_predictor", "seed"}
|
|
810
|
+
by_model: dict[str, dict[str, str]] = {}
|
|
811
|
+
dataset_identity: str | None = None
|
|
812
|
+
for record in run.records:
|
|
813
|
+
cell = record.get("cell")
|
|
814
|
+
result = record.get("result")
|
|
815
|
+
payload = result.get("payload") if isinstance(result, Mapping) else None
|
|
816
|
+
if not isinstance(cell, Mapping) or not isinstance(payload, Mapping):
|
|
817
|
+
raise ArtifactError("formal retriever result lacks cell or payload")
|
|
818
|
+
model_slot = _required_record_text(cell, "model_slot", label="experiment cell")
|
|
819
|
+
observed = payload.get("observed_dependency_hashes")
|
|
820
|
+
if not isinstance(observed, Mapping) or set(observed) != expected_arm_ids:
|
|
821
|
+
raise ArtifactError("formal retriever result lacks exact arm dependency receipts")
|
|
822
|
+
parsed_by_arm: list[dict[str, str]] = []
|
|
823
|
+
for raw in observed.values():
|
|
824
|
+
if not isinstance(raw, Mapping):
|
|
825
|
+
raise ArtifactError("formal retriever arm dependency receipt is invalid")
|
|
826
|
+
parsed = {
|
|
827
|
+
str(slot): str(digest)
|
|
828
|
+
for slot, digest in raw.items()
|
|
829
|
+
if isinstance(slot, str) and isinstance(digest, str)
|
|
830
|
+
}
|
|
831
|
+
if (
|
|
832
|
+
set(parsed) != set(raw)
|
|
833
|
+
or not required_slots <= set(parsed)
|
|
834
|
+
or any(
|
|
835
|
+
len(digest) != 64
|
|
836
|
+
or any(character not in "0123456789abcdef" for character in digest)
|
|
837
|
+
for digest in parsed.values()
|
|
838
|
+
)
|
|
839
|
+
):
|
|
840
|
+
raise ArtifactError("formal retriever dependency fingerprints are invalid")
|
|
841
|
+
parsed_by_arm.append(parsed)
|
|
842
|
+
first = parsed_by_arm[0]
|
|
843
|
+
if any(value != first for value in parsed_by_arm[1:]):
|
|
844
|
+
raise ArtifactError("formal retriever nuisance dependencies differ across arms")
|
|
845
|
+
previous = by_model.setdefault(model_slot, first)
|
|
846
|
+
if previous != first:
|
|
847
|
+
raise ArtifactError("formal retriever dependencies drift across groups")
|
|
848
|
+
current_dataset = payload.get("dataset_identity_sha256")
|
|
849
|
+
if (
|
|
850
|
+
not isinstance(current_dataset, str)
|
|
851
|
+
or len(current_dataset) != 64
|
|
852
|
+
or any(character not in "0123456789abcdef" for character in current_dataset)
|
|
853
|
+
):
|
|
854
|
+
raise ArtifactError("formal retriever result lacks dataset identity SHA-256")
|
|
855
|
+
if dataset_identity is None:
|
|
856
|
+
dataset_identity = current_dataset
|
|
857
|
+
elif dataset_identity != current_dataset:
|
|
858
|
+
raise ArtifactError("formal retriever dataset identity drifts across groups/models")
|
|
859
|
+
if set(by_model) != set(run.manifest.model_slots) or dataset_identity is None:
|
|
860
|
+
raise ArtifactError("formal retriever receipt lacks the exact model grid")
|
|
861
|
+
return {"dependencies_by_model": by_model, "dataset_identity_sha256": dataset_identity}
|
|
862
|
+
|
|
863
|
+
|
|
864
|
+
def _validate_formal_retriever_factorial_balance(
|
|
865
|
+
receipts: Mapping[tuple[str, int], Mapping[str, Any]],
|
|
866
|
+
) -> Mapping[str, Any]:
|
|
867
|
+
dataset_hashes = {str(value["dataset_identity_sha256"]) for value in receipts.values()}
|
|
868
|
+
if len(dataset_hashes) != 1:
|
|
869
|
+
raise ArtifactError("formal retriever cells use different dataset/corpus identities")
|
|
870
|
+
models = set.intersection(
|
|
871
|
+
*(
|
|
872
|
+
set(value["dependencies_by_model"])
|
|
873
|
+
for value in receipts.values()
|
|
874
|
+
if isinstance(value["dependencies_by_model"], Mapping)
|
|
875
|
+
)
|
|
876
|
+
)
|
|
877
|
+
if not models or any(
|
|
878
|
+
set(value["dependencies_by_model"]) != models for value in receipts.values()
|
|
879
|
+
):
|
|
880
|
+
raise ArtifactError("formal retriever cells expose different model slots")
|
|
881
|
+
varied_slots = {"retriever", "downstream_predictor"}
|
|
882
|
+
summaries: dict[str, Any] = {}
|
|
883
|
+
for model_slot in sorted(models):
|
|
884
|
+
dependency_maps = {
|
|
885
|
+
key: dict(value["dependencies_by_model"][model_slot]) for key, value in receipts.items()
|
|
886
|
+
}
|
|
887
|
+
slot_sets = {frozenset(value) for value in dependency_maps.values()}
|
|
888
|
+
if len(slot_sets) != 1:
|
|
889
|
+
raise ArtifactError("formal retriever cells expose different dependency slots")
|
|
890
|
+
slots = next(iter(slot_sets))
|
|
891
|
+
for slot in slots - varied_slots:
|
|
892
|
+
if len({value[slot] for value in dependency_maps.values()}) != 1:
|
|
893
|
+
raise ArtifactError(
|
|
894
|
+
f"formal retriever non-varied dependency {slot!r} differs across cells"
|
|
895
|
+
)
|
|
896
|
+
retriever_hashes = {}
|
|
897
|
+
for retriever in _FORMAL_PAPER_RETRIEVERS:
|
|
898
|
+
values = {dependency_maps[(retriever, top_k)]["retriever"] for top_k in _PAPER_TOP_K}
|
|
899
|
+
if len(values) != 1:
|
|
900
|
+
raise ArtifactError("formal retriever identity changes across top-k")
|
|
901
|
+
retriever_hashes[retriever] = next(iter(values))
|
|
902
|
+
if len(set(retriever_hashes.values())) != len(_FORMAL_PAPER_RETRIEVERS):
|
|
903
|
+
raise ArtifactError("formal retriever identities are not distinct")
|
|
904
|
+
predictor_hashes = {}
|
|
905
|
+
for top_k in _PAPER_TOP_K:
|
|
906
|
+
values = {
|
|
907
|
+
dependency_maps[(retriever, top_k)]["downstream_predictor"]
|
|
908
|
+
for retriever in _FORMAL_PAPER_RETRIEVERS
|
|
909
|
+
}
|
|
910
|
+
if len(values) != 1:
|
|
911
|
+
raise ArtifactError("formal downstream predictor changes across retrievers")
|
|
912
|
+
predictor_hashes[str(top_k)] = next(iter(values))
|
|
913
|
+
if len(set(predictor_hashes.values())) != len(_PAPER_TOP_K):
|
|
914
|
+
raise ArtifactError("formal top-k identities are not distinct")
|
|
915
|
+
summaries[model_slot] = {
|
|
916
|
+
"shared_dependency_slots": sorted(slots - varied_slots),
|
|
917
|
+
"retriever_slot_hashes": retriever_hashes,
|
|
918
|
+
"predictor_slot_hashes_by_top_k": predictor_hashes,
|
|
919
|
+
}
|
|
920
|
+
return {
|
|
921
|
+
"dataset_identity_sha256": next(iter(dataset_hashes)),
|
|
922
|
+
"models": summaries,
|
|
923
|
+
"balance_verified": True,
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
|
|
927
|
+
def validate_paper_selector_analysis_contract(
|
|
928
|
+
campaign: Mapping[str, Any],
|
|
929
|
+
analysis: Mapping[str, Any],
|
|
930
|
+
) -> Mapping[str, Any]:
|
|
931
|
+
"""Require the exact 14-policy, 526-group three-arena Selector analysis."""
|
|
932
|
+
|
|
933
|
+
_validate_paper_selector_campaign(campaign)
|
|
934
|
+
metadata = campaign["metadata"]
|
|
935
|
+
summaries = analysis.get("policy_summaries")
|
|
936
|
+
if (
|
|
937
|
+
analysis.get("analysis") != "selector_multi_policy_campaign"
|
|
938
|
+
or analysis.get("campaign_hash") != campaign.get("campaign_hash")
|
|
939
|
+
or analysis.get("primary_metric") != "valid_success"
|
|
940
|
+
or analysis.get("primary_metric") != campaign.get("primary_metric")
|
|
941
|
+
or analysis.get("evaluation_split") != campaign.get("evaluation_split")
|
|
942
|
+
or analysis.get("independent_groups") != 526
|
|
943
|
+
or analysis.get("common_support_groups") != 526
|
|
944
|
+
or not isinstance(summaries, Mapping)
|
|
945
|
+
or set(summaries) != _PAPER_SELECTOR_POLICIES
|
|
946
|
+
or any(
|
|
947
|
+
not isinstance(summary, Mapping) or summary.get("independent_groups") != 526
|
|
948
|
+
for summary in summaries.values()
|
|
949
|
+
)
|
|
950
|
+
or analysis.get("observed_action_oracle_used_for_training") is not False
|
|
951
|
+
or analysis.get("observed_action_oracle_used_for_inference") is not False
|
|
952
|
+
or analysis.get("paper_claim_authorized") is not False
|
|
953
|
+
):
|
|
954
|
+
raise ValidationError("Selector analysis output differs from the frozen paper protocol")
|
|
955
|
+
return {
|
|
956
|
+
"analysis_protocol": PAPER_SELECTOR_PROTOCOL,
|
|
957
|
+
"independent_groups": metadata["required_independent_groups"],
|
|
958
|
+
"common_support_groups": 526,
|
|
959
|
+
"partial_support_groups": 0,
|
|
960
|
+
"policy_count": 14,
|
|
961
|
+
"policy_rows": 7_364,
|
|
962
|
+
"paper_claim_authorized": False,
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
|
|
966
|
+
def build_paper_selector_claim_gates(
|
|
967
|
+
campaign: Mapping[str, Any],
|
|
968
|
+
*,
|
|
969
|
+
alpha: float,
|
|
970
|
+
valid_success_superiority_margin: float,
|
|
971
|
+
best_fixed_noninferiority_margin: float,
|
|
972
|
+
hard_harm_noninferiority_margin: float,
|
|
973
|
+
safety_comparator_policy_id: str,
|
|
974
|
+
minimum_intervention_coverage: float,
|
|
975
|
+
maximum_intervention_coverage: float,
|
|
976
|
+
maximum_mean_cost: float,
|
|
977
|
+
bootstrap_samples: int,
|
|
978
|
+
permutation_samples: int,
|
|
979
|
+
random_seed: int,
|
|
980
|
+
) -> Mapping[str, Any]:
|
|
981
|
+
"""Freeze scientific claim gates before untouched Selector outcomes exist."""
|
|
982
|
+
|
|
983
|
+
_validate_paper_selector_campaign(campaign)
|
|
984
|
+
campaign_hash = campaign.get("campaign_hash")
|
|
985
|
+
if not isinstance(campaign_hash, str) or len(campaign_hash) != 64:
|
|
986
|
+
raise ValidationError("claim gates require an exact Selector campaign hash")
|
|
987
|
+
base = {
|
|
988
|
+
"schema_version": 1,
|
|
989
|
+
"artifact": _SELECTOR_GATE_ARTIFACT,
|
|
990
|
+
"stage": "frozen_before_test_outcomes",
|
|
991
|
+
"campaign_manifest_hash": campaign_hash,
|
|
992
|
+
"primary_policy_id": "eligibility_aware_selector",
|
|
993
|
+
"no_op_policy_id": "no_op",
|
|
994
|
+
"best_fixed_policy_id": "best_fixed_global",
|
|
995
|
+
"safety_comparator_policy_id": safety_comparator_policy_id,
|
|
996
|
+
"alpha": alpha,
|
|
997
|
+
"valid_success_superiority_margin": valid_success_superiority_margin,
|
|
998
|
+
"best_fixed_noninferiority_margin": best_fixed_noninferiority_margin,
|
|
999
|
+
"hard_harm_noninferiority_margin": hard_harm_noninferiority_margin,
|
|
1000
|
+
"minimum_intervention_coverage": minimum_intervention_coverage,
|
|
1001
|
+
"maximum_intervention_coverage": maximum_intervention_coverage,
|
|
1002
|
+
"maximum_mean_cost": maximum_mean_cost,
|
|
1003
|
+
"bootstrap_samples": bootstrap_samples,
|
|
1004
|
+
"permutation_samples": permutation_samples,
|
|
1005
|
+
"random_seed": random_seed,
|
|
1006
|
+
"experiments_executed": False,
|
|
1007
|
+
}
|
|
1008
|
+
candidate = {**base, "gate_hash": content_hash(base)}
|
|
1009
|
+
return freeze_mapping(validate_paper_selector_claim_gates(campaign, candidate))
|
|
1010
|
+
|
|
1011
|
+
|
|
1012
|
+
def validate_paper_selector_claim_gates(
|
|
1013
|
+
campaign: Mapping[str, Any],
|
|
1014
|
+
gates: Mapping[str, Any],
|
|
1015
|
+
) -> Mapping[str, Any]:
|
|
1016
|
+
"""Validate an exact campaign-bound Selector claim hierarchy."""
|
|
1017
|
+
|
|
1018
|
+
_validate_paper_selector_campaign(campaign)
|
|
1019
|
+
campaign_hash = campaign.get("campaign_hash")
|
|
1020
|
+
if not isinstance(campaign_hash, str) or len(campaign_hash) != 64:
|
|
1021
|
+
raise ValidationError("claim gates require an exact Selector campaign hash")
|
|
1022
|
+
expected_keys = {
|
|
1023
|
+
"schema_version",
|
|
1024
|
+
"artifact",
|
|
1025
|
+
"stage",
|
|
1026
|
+
"campaign_manifest_hash",
|
|
1027
|
+
"primary_policy_id",
|
|
1028
|
+
"no_op_policy_id",
|
|
1029
|
+
"best_fixed_policy_id",
|
|
1030
|
+
"safety_comparator_policy_id",
|
|
1031
|
+
"alpha",
|
|
1032
|
+
"valid_success_superiority_margin",
|
|
1033
|
+
"best_fixed_noninferiority_margin",
|
|
1034
|
+
"hard_harm_noninferiority_margin",
|
|
1035
|
+
"minimum_intervention_coverage",
|
|
1036
|
+
"maximum_intervention_coverage",
|
|
1037
|
+
"maximum_mean_cost",
|
|
1038
|
+
"bootstrap_samples",
|
|
1039
|
+
"permutation_samples",
|
|
1040
|
+
"random_seed",
|
|
1041
|
+
"experiments_executed",
|
|
1042
|
+
"gate_hash",
|
|
1043
|
+
}
|
|
1044
|
+
if set(gates) != expected_keys:
|
|
1045
|
+
raise ValidationError("paper Selector claim-gate schema is invalid")
|
|
1046
|
+
if (
|
|
1047
|
+
gates["schema_version"] != 1
|
|
1048
|
+
or gates["artifact"] != _SELECTOR_GATE_ARTIFACT
|
|
1049
|
+
or gates["stage"] != "frozen_before_test_outcomes"
|
|
1050
|
+
or gates["experiments_executed"] is not False
|
|
1051
|
+
or gates["campaign_manifest_hash"] != campaign_hash
|
|
1052
|
+
or gates["primary_policy_id"] != "eligibility_aware_selector"
|
|
1053
|
+
or gates["no_op_policy_id"] != "no_op"
|
|
1054
|
+
or gates["best_fixed_policy_id"] != "best_fixed_global"
|
|
1055
|
+
or gates["safety_comparator_policy_id"] not in {"no_op", "best_fixed_global"}
|
|
1056
|
+
):
|
|
1057
|
+
raise ValidationError("paper Selector claim gates differ from the frozen campaign")
|
|
1058
|
+
alpha = _bounded_protocol_number(gates["alpha"], lower=0.0, upper=0.5, label="alpha")
|
|
1059
|
+
if not math.isclose(alpha, 0.05, abs_tol=1e-12):
|
|
1060
|
+
raise ValidationError("paper Selector protocol fixes alpha at 0.05")
|
|
1061
|
+
for key in (
|
|
1062
|
+
"valid_success_superiority_margin",
|
|
1063
|
+
"best_fixed_noninferiority_margin",
|
|
1064
|
+
"hard_harm_noninferiority_margin",
|
|
1065
|
+
"minimum_intervention_coverage",
|
|
1066
|
+
"maximum_intervention_coverage",
|
|
1067
|
+
):
|
|
1068
|
+
_bounded_protocol_number(gates[key], lower=0.0, upper=1.0, label=key)
|
|
1069
|
+
minimum_coverage = float(gates["minimum_intervention_coverage"])
|
|
1070
|
+
maximum_coverage = float(gates["maximum_intervention_coverage"])
|
|
1071
|
+
if minimum_coverage > maximum_coverage:
|
|
1072
|
+
raise ValidationError("paper Selector minimum coverage exceeds maximum coverage")
|
|
1073
|
+
_bounded_protocol_number(
|
|
1074
|
+
gates["maximum_mean_cost"],
|
|
1075
|
+
lower=0.0,
|
|
1076
|
+
upper=None,
|
|
1077
|
+
label="maximum_mean_cost",
|
|
1078
|
+
)
|
|
1079
|
+
for key, minimum in (
|
|
1080
|
+
("bootstrap_samples", 100),
|
|
1081
|
+
("permutation_samples", 100),
|
|
1082
|
+
("random_seed", 0),
|
|
1083
|
+
):
|
|
1084
|
+
value = gates[key]
|
|
1085
|
+
if isinstance(value, bool) or not isinstance(value, int) or value < minimum:
|
|
1086
|
+
raise ValidationError(f"paper Selector {key} must be an integer >= {minimum}")
|
|
1087
|
+
gate_hash = gates["gate_hash"]
|
|
1088
|
+
if (
|
|
1089
|
+
not isinstance(gate_hash, str)
|
|
1090
|
+
or len(gate_hash) != 64
|
|
1091
|
+
or content_hash({key: value for key, value in gates.items() if key != "gate_hash"})
|
|
1092
|
+
!= gate_hash
|
|
1093
|
+
):
|
|
1094
|
+
raise ValidationError("paper Selector claim-gate hash differs from its content")
|
|
1095
|
+
return freeze_mapping(gates)
|
|
1096
|
+
|
|
1097
|
+
|
|
1098
|
+
def analyze_paper_selector_claim_hierarchy(
|
|
1099
|
+
campaign: Mapping[str, Any],
|
|
1100
|
+
verified_results: Mapping[str, Any],
|
|
1101
|
+
base_analysis: Mapping[str, Any],
|
|
1102
|
+
claim_gates: Mapping[str, Any],
|
|
1103
|
+
*,
|
|
1104
|
+
bootstrap_samples: int | None = None,
|
|
1105
|
+
) -> Mapping[str, Any]:
|
|
1106
|
+
"""Run the preregistered paired Selector claim hierarchy."""
|
|
1107
|
+
|
|
1108
|
+
protocol_receipt = validate_paper_selector_analysis_contract(campaign, base_analysis)
|
|
1109
|
+
gates = validate_paper_selector_claim_gates(campaign, claim_gates)
|
|
1110
|
+
if (
|
|
1111
|
+
verified_results.get("artifact") != "agentinputlab.selector_campaign_results"
|
|
1112
|
+
or verified_results.get("stage") != "verified"
|
|
1113
|
+
or verified_results.get("data_status") != "VERIFIED_REAL_RESULTS"
|
|
1114
|
+
or verified_results.get("is_synthetic") is not False
|
|
1115
|
+
or verified_results.get("campaign_manifest_hash") != campaign.get("campaign_hash")
|
|
1116
|
+
):
|
|
1117
|
+
raise ValidationError(
|
|
1118
|
+
"paper Selector claim hierarchy requires campaign-bound verified real results"
|
|
1119
|
+
)
|
|
1120
|
+
frozen_bootstrap_samples = int(gates["bootstrap_samples"])
|
|
1121
|
+
if bootstrap_samples is not None and bootstrap_samples != frozen_bootstrap_samples:
|
|
1122
|
+
raise ValidationError(
|
|
1123
|
+
"paper Selector bootstrap_samples differs from the frozen claim gates"
|
|
1124
|
+
)
|
|
1125
|
+
if base_analysis.get("bootstrap_samples") != frozen_bootstrap_samples:
|
|
1126
|
+
raise ValidationError(
|
|
1127
|
+
"Selector campaign analysis bootstrap count differs from the claim gates"
|
|
1128
|
+
)
|
|
1129
|
+
if base_analysis.get("random_seed") != gates["random_seed"]:
|
|
1130
|
+
raise ValidationError("Selector campaign analysis seed differs from the claim gates")
|
|
1131
|
+
_validate_resample_arguments(
|
|
1132
|
+
bootstrap_samples=frozen_bootstrap_samples,
|
|
1133
|
+
permutation_samples=int(gates["permutation_samples"]),
|
|
1134
|
+
random_seed=int(gates["random_seed"]),
|
|
1135
|
+
)
|
|
1136
|
+
records = verified_results.get("records")
|
|
1137
|
+
if not isinstance(records, Sequence) or isinstance(records, str | bytes):
|
|
1138
|
+
raise ValidationError("verified Selector results require records")
|
|
1139
|
+
by_group: dict[str, dict[str, Mapping[str, Any]]] = defaultdict(dict)
|
|
1140
|
+
for row in records:
|
|
1141
|
+
if not isinstance(row, Mapping):
|
|
1142
|
+
raise ValidationError("verified Selector result row must be an object")
|
|
1143
|
+
group_id = row.get("group_id")
|
|
1144
|
+
policy_id = row.get("policy_id")
|
|
1145
|
+
if not isinstance(group_id, str) or not isinstance(policy_id, str):
|
|
1146
|
+
raise ValidationError("verified Selector result row lacks group/policy identity")
|
|
1147
|
+
if policy_id in by_group[group_id]:
|
|
1148
|
+
raise ValidationError("verified Selector results duplicate a group-policy row")
|
|
1149
|
+
by_group[group_id][policy_id] = row
|
|
1150
|
+
if len(by_group) != 526 or any(
|
|
1151
|
+
set(rows) != _PAPER_SELECTOR_POLICIES for rows in by_group.values()
|
|
1152
|
+
):
|
|
1153
|
+
raise ValidationError("paper Selector claim hierarchy requires 526 complete policy blocks")
|
|
1154
|
+
|
|
1155
|
+
primary_id = str(gates["primary_policy_id"])
|
|
1156
|
+
no_op_id = str(gates["no_op_policy_id"])
|
|
1157
|
+
best_id = str(gates["best_fixed_policy_id"])
|
|
1158
|
+
safety_id = str(gates["safety_comparator_policy_id"])
|
|
1159
|
+
superiority = []
|
|
1160
|
+
best_difference = []
|
|
1161
|
+
harm_difference = []
|
|
1162
|
+
primary_costs = []
|
|
1163
|
+
intervened = []
|
|
1164
|
+
for group_id in sorted(by_group):
|
|
1165
|
+
rows = by_group[group_id]
|
|
1166
|
+
primary = rows[primary_id]
|
|
1167
|
+
no_op = rows[no_op_id]
|
|
1168
|
+
best = rows[best_id]
|
|
1169
|
+
safety = rows[safety_id]
|
|
1170
|
+
superiority.append(float(primary["value"]) - float(no_op["value"]))
|
|
1171
|
+
best_difference.append(float(primary["value"]) - float(best["value"]))
|
|
1172
|
+
harm_difference.append(float(bool(primary["hard_harm"])) - float(bool(safety["hard_harm"])))
|
|
1173
|
+
primary_costs.append(float(primary["cost"]))
|
|
1174
|
+
intervened.append(1.0 if primary["intervened"] else 0.0)
|
|
1175
|
+
|
|
1176
|
+
rng = random.Random(int(gates["random_seed"]))
|
|
1177
|
+
alpha = float(gates["alpha"])
|
|
1178
|
+
superiority_margin = float(gates["valid_success_superiority_margin"])
|
|
1179
|
+
best_margin = float(gates["best_fixed_noninferiority_margin"])
|
|
1180
|
+
harm_margin = float(gates["hard_harm_noninferiority_margin"])
|
|
1181
|
+
superiority_p = _one_sided_sign_flip_p_value(
|
|
1182
|
+
[value - superiority_margin for value in superiority],
|
|
1183
|
+
samples=int(gates["permutation_samples"]),
|
|
1184
|
+
rng=rng,
|
|
1185
|
+
)
|
|
1186
|
+
best_p = _one_sided_sign_flip_p_value(
|
|
1187
|
+
[value + best_margin for value in best_difference],
|
|
1188
|
+
samples=int(gates["permutation_samples"]),
|
|
1189
|
+
rng=rng,
|
|
1190
|
+
)
|
|
1191
|
+
harm_p = _one_sided_sign_flip_p_value(
|
|
1192
|
+
[harm_margin - value for value in harm_difference],
|
|
1193
|
+
samples=int(gates["permutation_samples"]),
|
|
1194
|
+
rng=rng,
|
|
1195
|
+
)
|
|
1196
|
+
superiority_interval = _cluster_bootstrap_interval(
|
|
1197
|
+
superiority,
|
|
1198
|
+
samples=frozen_bootstrap_samples,
|
|
1199
|
+
rng=rng,
|
|
1200
|
+
)
|
|
1201
|
+
best_interval = _cluster_bootstrap_interval(
|
|
1202
|
+
best_difference,
|
|
1203
|
+
samples=frozen_bootstrap_samples,
|
|
1204
|
+
rng=rng,
|
|
1205
|
+
)
|
|
1206
|
+
harm_interval = _cluster_bootstrap_interval(
|
|
1207
|
+
harm_difference,
|
|
1208
|
+
samples=frozen_bootstrap_samples,
|
|
1209
|
+
rng=rng,
|
|
1210
|
+
)
|
|
1211
|
+
coverage = fmean(intervened)
|
|
1212
|
+
mean_cost = fmean(primary_costs)
|
|
1213
|
+
raw_passes = (
|
|
1214
|
+
superiority_p <= alpha and superiority_interval[0] > superiority_margin,
|
|
1215
|
+
best_p <= alpha and best_interval[0] > -best_margin,
|
|
1216
|
+
harm_p <= alpha and harm_interval[1] < harm_margin,
|
|
1217
|
+
(
|
|
1218
|
+
float(gates["minimum_intervention_coverage"])
|
|
1219
|
+
<= coverage
|
|
1220
|
+
<= float(gates["maximum_intervention_coverage"])
|
|
1221
|
+
and mean_cost <= float(gates["maximum_mean_cost"])
|
|
1222
|
+
),
|
|
1223
|
+
)
|
|
1224
|
+
reached = True
|
|
1225
|
+
formal_passes: list[bool] = []
|
|
1226
|
+
formally_tested: list[bool] = []
|
|
1227
|
+
for passed in raw_passes:
|
|
1228
|
+
formally_tested.append(reached)
|
|
1229
|
+
formal_passes.append(reached and passed)
|
|
1230
|
+
reached = reached and passed
|
|
1231
|
+
steps = (
|
|
1232
|
+
{
|
|
1233
|
+
"step": 1,
|
|
1234
|
+
"claim": "valid_success_superiority_to_no_op",
|
|
1235
|
+
"estimate": fmean(superiority),
|
|
1236
|
+
"cluster_bootstrap_95ci": list(superiority_interval),
|
|
1237
|
+
"margin": superiority_margin,
|
|
1238
|
+
"one_sided_sign_flip_p_value": superiority_p,
|
|
1239
|
+
},
|
|
1240
|
+
{
|
|
1241
|
+
"step": 2,
|
|
1242
|
+
"claim": "noninferiority_to_calibration_frozen_best_fixed",
|
|
1243
|
+
"estimate": fmean(best_difference),
|
|
1244
|
+
"cluster_bootstrap_95ci": list(best_interval),
|
|
1245
|
+
"margin": best_margin,
|
|
1246
|
+
"one_sided_sign_flip_p_value": best_p,
|
|
1247
|
+
},
|
|
1248
|
+
{
|
|
1249
|
+
"step": 3,
|
|
1250
|
+
"claim": f"hard_harm_noninferiority_to_{safety_id}",
|
|
1251
|
+
"estimate": fmean(harm_difference),
|
|
1252
|
+
"cluster_bootstrap_95ci": list(harm_interval),
|
|
1253
|
+
"margin": harm_margin,
|
|
1254
|
+
"one_sided_sign_flip_p_value": harm_p,
|
|
1255
|
+
},
|
|
1256
|
+
{
|
|
1257
|
+
"step": 4,
|
|
1258
|
+
"claim": "frozen_cost_and_coverage_constraint",
|
|
1259
|
+
"intervention_coverage": coverage,
|
|
1260
|
+
"minimum_intervention_coverage": gates["minimum_intervention_coverage"],
|
|
1261
|
+
"maximum_intervention_coverage": gates["maximum_intervention_coverage"],
|
|
1262
|
+
"mean_cost": mean_cost,
|
|
1263
|
+
"maximum_mean_cost": gates["maximum_mean_cost"],
|
|
1264
|
+
},
|
|
1265
|
+
)
|
|
1266
|
+
return freeze_mapping(
|
|
1267
|
+
{
|
|
1268
|
+
"schema_version": 1,
|
|
1269
|
+
"analysis": "paper_selector_hierarchical_claim_gate",
|
|
1270
|
+
"analysis_protocol": PAPER_SELECTOR_PROTOCOL,
|
|
1271
|
+
"campaign_hash": campaign.get("campaign_hash"),
|
|
1272
|
+
"claim_gate_hash": gates["gate_hash"],
|
|
1273
|
+
"independent_groups": protocol_receipt["independent_groups"],
|
|
1274
|
+
"alpha": alpha,
|
|
1275
|
+
"bootstrap_samples": frozen_bootstrap_samples,
|
|
1276
|
+
"permutation_samples": gates["permutation_samples"],
|
|
1277
|
+
"random_seed": gates["random_seed"],
|
|
1278
|
+
"steps": tuple(
|
|
1279
|
+
{
|
|
1280
|
+
**step,
|
|
1281
|
+
"formally_tested": formally_tested[index],
|
|
1282
|
+
"passed_if_reached": raw_passes[index],
|
|
1283
|
+
"hierarchical_pass": formal_passes[index],
|
|
1284
|
+
}
|
|
1285
|
+
for index, step in enumerate(steps)
|
|
1286
|
+
),
|
|
1287
|
+
"positive_selector_claim": all(formal_passes),
|
|
1288
|
+
"observed_action_oracle_used_for_inference": False,
|
|
1289
|
+
"paper_claim_authorized": False,
|
|
1290
|
+
}
|
|
1291
|
+
)
|
|
1292
|
+
|
|
1293
|
+
|
|
1294
|
+
def _validate_paper_selector_campaign(campaign: Mapping[str, Any]) -> None:
|
|
1295
|
+
if campaign.get("campaign_kind") != "baseline":
|
|
1296
|
+
raise ValidationError("paper Selector protocol requires the baseline campaign")
|
|
1297
|
+
if campaign.get("evaluation_split") != "untouched_test":
|
|
1298
|
+
raise ValidationError("paper Selector protocol requires the untouched test split")
|
|
1299
|
+
policies = campaign.get("policies")
|
|
1300
|
+
if not isinstance(policies, Sequence) or isinstance(policies, str | bytes):
|
|
1301
|
+
raise ValidationError("paper Selector campaign requires a policies array")
|
|
1302
|
+
policy_ids = {item.get("policy_id") for item in policies if isinstance(item, Mapping)}
|
|
1303
|
+
if len(policies) != 14 or policy_ids != _PAPER_SELECTOR_POLICIES:
|
|
1304
|
+
raise ValidationError("paper Selector protocol requires the exact 14-policy roster")
|
|
1305
|
+
metadata = campaign.get("metadata")
|
|
1306
|
+
if (
|
|
1307
|
+
not isinstance(metadata, Mapping)
|
|
1308
|
+
or metadata.get("required_independent_groups") != 526
|
|
1309
|
+
or metadata.get("minimum_common_support_groups") != 526
|
|
1310
|
+
):
|
|
1311
|
+
raise ValidationError(
|
|
1312
|
+
"paper Selector protocol requires exactly 526 three-arena common-support groups"
|
|
1313
|
+
)
|
|
1314
|
+
if campaign.get("campaign_hash") is not None:
|
|
1315
|
+
campaign_hash = campaign["campaign_hash"]
|
|
1316
|
+
if not isinstance(campaign_hash, str) or len(campaign_hash) != 64:
|
|
1317
|
+
raise ValidationError("paper Selector campaign hash must be SHA-256")
|
|
1318
|
+
|
|
1319
|
+
|
|
1320
|
+
def validate_paper_run_contract(
|
|
1321
|
+
run_dir: str | Path,
|
|
1322
|
+
*,
|
|
1323
|
+
protocol: str,
|
|
1324
|
+
) -> Mapping[str, Any]:
|
|
1325
|
+
"""Validate one paper run and return its census receipt without analyzing it."""
|
|
1326
|
+
|
|
1327
|
+
expected = {
|
|
1328
|
+
PAPER_FAMILY_NATIVE_PROTOCOL: _FAMILY_NATIVE_KIND,
|
|
1329
|
+
PAPER_COMMON_SUPPORT_PROTOCOL: _COMMON_SUPPORT_KIND,
|
|
1330
|
+
PAPER_RETRIEVER_FACTORIAL_PROTOCOL: _RETRIEVER_FACTORIAL_KIND,
|
|
1331
|
+
}.get(protocol)
|
|
1332
|
+
if expected is None:
|
|
1333
|
+
raise ValidationError(f"unsupported paper run protocol: {protocol!r}")
|
|
1334
|
+
if expected == _RETRIEVER_FACTORIAL_KIND:
|
|
1335
|
+
manifest = _load_manifest(Path(run_dir).expanduser().resolve())
|
|
1336
|
+
census = manifest.metadata.get("experiment_census")
|
|
1337
|
+
actual_kind = census.get("campaign_kind") if isinstance(census, Mapping) else None
|
|
1338
|
+
if actual_kind in {
|
|
1339
|
+
_RETRIEVER_FACTORIAL_PRIMARY_KIND,
|
|
1340
|
+
_RETRIEVER_FACTORIAL_AUXILIARY_KIND,
|
|
1341
|
+
}:
|
|
1342
|
+
expected = str(actual_kind)
|
|
1343
|
+
run = _load_and_validate_paper_run(run_dir, expected_kind=expected)
|
|
1344
|
+
if expected == _FAMILY_NATIVE_KIND:
|
|
1345
|
+
_family_native_arm_contract(run.manifest)
|
|
1346
|
+
elif expected == _COMMON_SUPPORT_KIND:
|
|
1347
|
+
_common_support_arm_contract(run.manifest)
|
|
1348
|
+
else:
|
|
1349
|
+
_retriever_arm_contract(run.manifest)
|
|
1350
|
+
_retriever_dependency_cell(
|
|
1351
|
+
run.manifest,
|
|
1352
|
+
formal=expected
|
|
1353
|
+
in {_RETRIEVER_FACTORIAL_PRIMARY_KIND, _RETRIEVER_FACTORIAL_AUXILIARY_KIND},
|
|
1354
|
+
)
|
|
1355
|
+
return {
|
|
1356
|
+
"analysis_protocol": protocol,
|
|
1357
|
+
"study_id": run.manifest.study_id,
|
|
1358
|
+
"campaign_kind": run.campaign_kind,
|
|
1359
|
+
"independent_groups": run.independent_groups,
|
|
1360
|
+
"verified_plan_cells": run.expected_plan_cells,
|
|
1361
|
+
"verified_agent_episodes": run.expected_agent_episodes,
|
|
1362
|
+
"paper_claim_authorized": False,
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
|
|
1366
|
+
def _load_and_validate_paper_run(
|
|
1367
|
+
run_dir: str | Path,
|
|
1368
|
+
*,
|
|
1369
|
+
expected_kind: str,
|
|
1370
|
+
) -> _VerifiedPaperRun:
|
|
1371
|
+
root = Path(run_dir).expanduser().resolve()
|
|
1372
|
+
manifest = _load_manifest(root)
|
|
1373
|
+
records = load_verified_cell_results(root)
|
|
1374
|
+
census = manifest.metadata.get("experiment_census")
|
|
1375
|
+
if not isinstance(census, Mapping) or census.get("protocol") != _CENSUS_PROTOCOL:
|
|
1376
|
+
raise ArtifactError("paper run lacks the frozen experiment census protocol")
|
|
1377
|
+
if census.get("campaign_kind") != expected_kind:
|
|
1378
|
+
raise ArtifactError(
|
|
1379
|
+
f"paper run campaign kind is {census.get('campaign_kind')!r}, "
|
|
1380
|
+
f"expected {expected_kind!r}"
|
|
1381
|
+
)
|
|
1382
|
+
expected = _paper_census_expectation(expected_kind)
|
|
1383
|
+
for field, value in expected.items():
|
|
1384
|
+
if census.get(field) != value:
|
|
1385
|
+
raise ArtifactError(
|
|
1386
|
+
f"paper {expected_kind} census field {field} is "
|
|
1387
|
+
f"{census.get(field)!r}, expected {value!r}"
|
|
1388
|
+
)
|
|
1389
|
+
if manifest.stage is not ExperimentStage.CONFIRMATORY:
|
|
1390
|
+
raise ArtifactError("paper primary/factorial analyses require confirmatory-stage runs")
|
|
1391
|
+
retriever_kinds = {
|
|
1392
|
+
_RETRIEVER_FACTORIAL_KIND,
|
|
1393
|
+
_RETRIEVER_FACTORIAL_PRIMARY_KIND,
|
|
1394
|
+
_RETRIEVER_FACTORIAL_AUXILIARY_KIND,
|
|
1395
|
+
}
|
|
1396
|
+
profile = (
|
|
1397
|
+
ProfileKind.FACTORIAL_ADAPTER
|
|
1398
|
+
if expected_kind in retriever_kinds
|
|
1399
|
+
else ProfileKind.CONTROLLED_ADAPTER
|
|
1400
|
+
)
|
|
1401
|
+
if manifest.estimand_profile_kind is not profile:
|
|
1402
|
+
raise ArtifactError(f"paper {expected_kind} run has the wrong estimand profile")
|
|
1403
|
+
expected_arm_ids = census.get("expected_arm_ids")
|
|
1404
|
+
expected_models = census.get("expected_model_slots")
|
|
1405
|
+
expected_seeds = census.get("expected_seeds")
|
|
1406
|
+
if (
|
|
1407
|
+
not isinstance(expected_arm_ids, Sequence)
|
|
1408
|
+
or isinstance(expected_arm_ids, str | bytes)
|
|
1409
|
+
or tuple(expected_arm_ids) != tuple(arm.arm_id for arm in manifest.arms)
|
|
1410
|
+
or not isinstance(expected_models, Sequence)
|
|
1411
|
+
or isinstance(expected_models, str | bytes)
|
|
1412
|
+
or tuple(expected_models) != manifest.model_slots
|
|
1413
|
+
or not isinstance(expected_seeds, Sequence)
|
|
1414
|
+
or isinstance(expected_seeds, str | bytes)
|
|
1415
|
+
or tuple(expected_seeds) != manifest.seeds
|
|
1416
|
+
):
|
|
1417
|
+
raise ArtifactError("paper run census arm/model/seed identities differ from its manifest")
|
|
1418
|
+
|
|
1419
|
+
expected_plan_cells = int(expected["expected_plan_cells"])
|
|
1420
|
+
expected_agent_episodes = int(expected["expected_agent_episodes"])
|
|
1421
|
+
independent_groups = int(expected["independent_group_target"])
|
|
1422
|
+
if len(records) != expected_plan_cells:
|
|
1423
|
+
raise ArtifactError(
|
|
1424
|
+
f"paper run has {len(records)} verified cells, expected {expected_plan_cells}"
|
|
1425
|
+
)
|
|
1426
|
+
clusters: dict[str, set[tuple[str, int]]] = defaultdict(set)
|
|
1427
|
+
episodes = 0
|
|
1428
|
+
for record in records:
|
|
1429
|
+
cell = record.get("cell")
|
|
1430
|
+
if not isinstance(cell, Mapping):
|
|
1431
|
+
raise ArtifactError("paper run record lacks an experiment cell")
|
|
1432
|
+
cluster_id = _cluster_id(cell)
|
|
1433
|
+
model_slot = _required_record_text(cell, "model_slot", label="experiment cell")
|
|
1434
|
+
seed = cell.get("seed")
|
|
1435
|
+
if isinstance(seed, bool) or not isinstance(seed, int):
|
|
1436
|
+
raise ArtifactError("paper experiment cell seed must be an integer")
|
|
1437
|
+
replicate = (model_slot, seed)
|
|
1438
|
+
if replicate in clusters[cluster_id]:
|
|
1439
|
+
raise ArtifactError("paper run duplicates a group/model/seed cell")
|
|
1440
|
+
clusters[cluster_id].add(replicate)
|
|
1441
|
+
arm_ids = cell.get("arm_ids")
|
|
1442
|
+
if not isinstance(arm_ids, Sequence) or isinstance(arm_ids, str | bytes):
|
|
1443
|
+
raise ArtifactError("paper experiment cell lacks an arm inventory")
|
|
1444
|
+
episodes += len(arm_ids)
|
|
1445
|
+
expected_replicates = {
|
|
1446
|
+
(model_slot, seed) for model_slot in manifest.model_slots for seed in manifest.seeds
|
|
1447
|
+
}
|
|
1448
|
+
if len(clusters) != independent_groups:
|
|
1449
|
+
raise ArtifactError(
|
|
1450
|
+
f"paper run has {len(clusters)} independent groups, expected {independent_groups}"
|
|
1451
|
+
)
|
|
1452
|
+
if any(replicates != expected_replicates for replicates in clusters.values()):
|
|
1453
|
+
raise ArtifactError("paper run does not contain the exact model/seed grid per group")
|
|
1454
|
+
if episodes != expected_agent_episodes:
|
|
1455
|
+
raise ArtifactError(
|
|
1456
|
+
f"paper run has {episodes} arm episodes, expected {expected_agent_episodes}"
|
|
1457
|
+
)
|
|
1458
|
+
return _VerifiedPaperRun(
|
|
1459
|
+
root=root,
|
|
1460
|
+
manifest=manifest,
|
|
1461
|
+
records=records,
|
|
1462
|
+
campaign_kind=expected_kind,
|
|
1463
|
+
independent_groups=independent_groups,
|
|
1464
|
+
expected_plan_cells=expected_plan_cells,
|
|
1465
|
+
expected_agent_episodes=expected_agent_episodes,
|
|
1466
|
+
cluster_ids=frozenset(clusters),
|
|
1467
|
+
)
|
|
1468
|
+
|
|
1469
|
+
|
|
1470
|
+
def _paper_census_expectation(kind: str) -> Mapping[str, int]:
|
|
1471
|
+
if kind == _FAMILY_NATIVE_KIND:
|
|
1472
|
+
return {
|
|
1473
|
+
"independent_group_target": 600,
|
|
1474
|
+
"expected_plan_cells": 1_200,
|
|
1475
|
+
"expected_agent_episodes": 3_600,
|
|
1476
|
+
}
|
|
1477
|
+
if kind == _COMMON_SUPPORT_KIND:
|
|
1478
|
+
return {
|
|
1479
|
+
"independent_group_target": 526,
|
|
1480
|
+
"expected_plan_cells": 1_052,
|
|
1481
|
+
"expected_agent_episodes": 6_312,
|
|
1482
|
+
}
|
|
1483
|
+
if kind == _RETRIEVER_FACTORIAL_KIND:
|
|
1484
|
+
return {
|
|
1485
|
+
"independent_group_target": 240,
|
|
1486
|
+
"expected_plan_cells": 240,
|
|
1487
|
+
"expected_agent_episodes": 1_200,
|
|
1488
|
+
}
|
|
1489
|
+
if kind == _RETRIEVER_FACTORIAL_PRIMARY_KIND:
|
|
1490
|
+
return {
|
|
1491
|
+
"independent_group_target": 97,
|
|
1492
|
+
"expected_plan_cells": 194,
|
|
1493
|
+
"expected_agent_episodes": 970,
|
|
1494
|
+
}
|
|
1495
|
+
if kind == _RETRIEVER_FACTORIAL_AUXILIARY_KIND:
|
|
1496
|
+
return {
|
|
1497
|
+
"independent_group_target": 240,
|
|
1498
|
+
"expected_plan_cells": 480,
|
|
1499
|
+
"expected_agent_episodes": 2_400,
|
|
1500
|
+
}
|
|
1501
|
+
raise ValidationError(f"unknown paper census kind: {kind!r}")
|
|
1502
|
+
|
|
1503
|
+
|
|
1504
|
+
def _family_native_arm_contract(manifest: ExperimentManifest) -> SemanticFamily:
|
|
1505
|
+
census = manifest.metadata["experiment_census"]
|
|
1506
|
+
raw_family = census.get("family")
|
|
1507
|
+
try:
|
|
1508
|
+
family = SemanticFamily(raw_family)
|
|
1509
|
+
except (TypeError, ValueError) as exc:
|
|
1510
|
+
raise ArtifactError("family-native census requires one core family") from exc
|
|
1511
|
+
if family not in _primary_families():
|
|
1512
|
+
raise ArtifactError("family-native census family is not one of the five core families")
|
|
1513
|
+
_family_native_arm_ids(manifest, family)
|
|
1514
|
+
if len(manifest.model_slots) != 2 or len(manifest.seeds) != 1:
|
|
1515
|
+
raise ArtifactError("family-native paper run requires two models and one seed")
|
|
1516
|
+
return family
|
|
1517
|
+
|
|
1518
|
+
|
|
1519
|
+
def _family_native_arm_ids(
|
|
1520
|
+
manifest: ExperimentManifest,
|
|
1521
|
+
family: SemanticFamily,
|
|
1522
|
+
) -> tuple[str, tuple[str, str]]:
|
|
1523
|
+
baselines = tuple(arm.arm_id for arm in manifest.arms if arm.family is SemanticFamily.NO_OP)
|
|
1524
|
+
treatments = tuple(arm.arm_id for arm in manifest.arms if arm.family is family)
|
|
1525
|
+
if len(manifest.arms) != 3 or len(baselines) != 1 or len(treatments) != 2:
|
|
1526
|
+
raise ArtifactError(
|
|
1527
|
+
"family-native paper run requires exactly NO_OP plus two same-family arms"
|
|
1528
|
+
)
|
|
1529
|
+
return baselines[0], (treatments[0], treatments[1])
|
|
1530
|
+
|
|
1531
|
+
|
|
1532
|
+
def _common_support_arm_contract(
|
|
1533
|
+
manifest: ExperimentManifest,
|
|
1534
|
+
) -> tuple[str, Mapping[SemanticFamily, str]]:
|
|
1535
|
+
baselines = tuple(arm.arm_id for arm in manifest.arms if arm.family is SemanticFamily.NO_OP)
|
|
1536
|
+
by_family = {
|
|
1537
|
+
family: tuple(arm.arm_id for arm in manifest.arms if arm.family is family)
|
|
1538
|
+
for family in _primary_families()
|
|
1539
|
+
}
|
|
1540
|
+
if (
|
|
1541
|
+
len(manifest.arms) != 6
|
|
1542
|
+
or len(baselines) != 1
|
|
1543
|
+
or any(len(arm_ids) != 1 for arm_ids in by_family.values())
|
|
1544
|
+
or len(manifest.model_slots) != 2
|
|
1545
|
+
or len(manifest.seeds) != 1
|
|
1546
|
+
):
|
|
1547
|
+
raise ArtifactError(
|
|
1548
|
+
"common-support paper run requires NO_OP, one arm per family, two models, and one seed"
|
|
1549
|
+
)
|
|
1550
|
+
return baselines[0], {family: arm_ids[0] for family, arm_ids in by_family.items()}
|
|
1551
|
+
|
|
1552
|
+
|
|
1553
|
+
def _retriever_arm_contract(
|
|
1554
|
+
manifest: ExperimentManifest,
|
|
1555
|
+
) -> tuple[str, tuple[str, str, str, str]]:
|
|
1556
|
+
baselines = tuple(arm.arm_id for arm in manifest.arms if arm.family is SemanticFamily.NO_OP)
|
|
1557
|
+
reformulate = tuple(
|
|
1558
|
+
arm.arm_id for arm in manifest.arms if arm.family is SemanticFamily.REFORMULATE
|
|
1559
|
+
)
|
|
1560
|
+
acquire = tuple(arm.arm_id for arm in manifest.arms if arm.family is SemanticFamily.ACQUIRE)
|
|
1561
|
+
campaign_kind = manifest.metadata.get("experiment_census", {}).get("campaign_kind")
|
|
1562
|
+
expected_model_count = (
|
|
1563
|
+
2
|
|
1564
|
+
if campaign_kind in {_RETRIEVER_FACTORIAL_PRIMARY_KIND, _RETRIEVER_FACTORIAL_AUXILIARY_KIND}
|
|
1565
|
+
else 1
|
|
1566
|
+
)
|
|
1567
|
+
if (
|
|
1568
|
+
len(manifest.arms) != 5
|
|
1569
|
+
or len(baselines) != 1
|
|
1570
|
+
or len(reformulate) != 2
|
|
1571
|
+
or len(acquire) != 2
|
|
1572
|
+
or len(manifest.model_slots) != expected_model_count
|
|
1573
|
+
or len(manifest.seeds) != 1
|
|
1574
|
+
):
|
|
1575
|
+
raise ArtifactError(
|
|
1576
|
+
"retriever-factorial paper cell requires NO_OP, two reformulation arms, "
|
|
1577
|
+
f"two acquisition arms, {expected_model_count} model(s), and one seed"
|
|
1578
|
+
)
|
|
1579
|
+
return baselines[0], (reformulate[0], reformulate[1], acquire[0], acquire[1])
|
|
1580
|
+
|
|
1581
|
+
|
|
1582
|
+
def _retriever_dependency_cell(
|
|
1583
|
+
manifest: ExperimentManifest,
|
|
1584
|
+
*,
|
|
1585
|
+
formal: bool = False,
|
|
1586
|
+
) -> tuple[str, int]:
|
|
1587
|
+
dependency = manifest.metadata.get("dependency_cell")
|
|
1588
|
+
if not isinstance(dependency, Mapping):
|
|
1589
|
+
raise ArtifactError("retriever-factorial run lacks dependency_cell metadata")
|
|
1590
|
+
retriever = dependency.get("retriever_id")
|
|
1591
|
+
top_k = dependency.get("top_k")
|
|
1592
|
+
allowed = _FORMAL_PAPER_RETRIEVERS if formal else _PAPER_RETRIEVERS
|
|
1593
|
+
if retriever not in allowed or top_k not in _PAPER_TOP_K:
|
|
1594
|
+
grid = "4x2" if formal else "2x2"
|
|
1595
|
+
raise ArtifactError(
|
|
1596
|
+
f"retriever-factorial dependency cell is outside the frozen {grid} grid"
|
|
1597
|
+
)
|
|
1598
|
+
slugs = {
|
|
1599
|
+
"bm25": "bm25",
|
|
1600
|
+
"intfloat/e5-large-v2": "e5_large_v2",
|
|
1601
|
+
"BAAI/bge-m3": "bge_m3",
|
|
1602
|
+
"Qwen/Qwen3-Embedding-0.6B": "qwen3_embedding",
|
|
1603
|
+
}
|
|
1604
|
+
expected_binding = slugs[str(retriever)] + f":k{top_k}"
|
|
1605
|
+
if any(arm.parameters.get("dependency_cell") != expected_binding for arm in manifest.arms):
|
|
1606
|
+
raise ArtifactError(
|
|
1607
|
+
"retriever-factorial arm dependency binding differs from its declared cell"
|
|
1608
|
+
)
|
|
1609
|
+
return str(retriever), int(top_k)
|
|
1610
|
+
|
|
1611
|
+
|
|
1612
|
+
def _retriever_runtime_receipt(run: _VerifiedPaperRun) -> Mapping[str, Any]:
|
|
1613
|
+
"""Collapse one cell's observed runtime identities after strict equality checks."""
|
|
1614
|
+
|
|
1615
|
+
expected_arm_ids = {arm.arm_id for arm in run.manifest.arms}
|
|
1616
|
+
reference_dependencies: dict[str, str] | None = None
|
|
1617
|
+
dataset_identity: str | None = None
|
|
1618
|
+
required_slots = {"backend", "retriever", "downstream_predictor", "seed"}
|
|
1619
|
+
for record in run.records:
|
|
1620
|
+
result = record.get("result")
|
|
1621
|
+
payload = result.get("payload") if isinstance(result, Mapping) else None
|
|
1622
|
+
if not isinstance(payload, Mapping):
|
|
1623
|
+
raise ArtifactError("retriever-factorial result lacks a payload")
|
|
1624
|
+
observed = payload.get("observed_dependency_hashes")
|
|
1625
|
+
if not isinstance(observed, Mapping) or set(observed) != expected_arm_ids:
|
|
1626
|
+
raise ArtifactError(
|
|
1627
|
+
"retriever-factorial dependency receipt differs from the arm inventory"
|
|
1628
|
+
)
|
|
1629
|
+
by_arm: dict[str, dict[str, str]] = {}
|
|
1630
|
+
for arm_id, raw_dependencies in observed.items():
|
|
1631
|
+
if not isinstance(arm_id, str) or not isinstance(raw_dependencies, Mapping):
|
|
1632
|
+
raise ArtifactError("retriever-factorial dependency receipt is malformed")
|
|
1633
|
+
parsed: dict[str, str] = {}
|
|
1634
|
+
for slot, digest in raw_dependencies.items():
|
|
1635
|
+
if (
|
|
1636
|
+
not isinstance(slot, str)
|
|
1637
|
+
or not slot
|
|
1638
|
+
or not isinstance(digest, str)
|
|
1639
|
+
or len(digest) != 64
|
|
1640
|
+
or any(character not in "0123456789abcdef" for character in digest)
|
|
1641
|
+
):
|
|
1642
|
+
raise ArtifactError(
|
|
1643
|
+
"retriever-factorial dependency fingerprints must be SHA-256 mappings"
|
|
1644
|
+
)
|
|
1645
|
+
parsed[slot] = digest
|
|
1646
|
+
if not parsed:
|
|
1647
|
+
raise ArtifactError("retriever-factorial dependency receipt is empty")
|
|
1648
|
+
by_arm[arm_id] = parsed
|
|
1649
|
+
first = next(iter(by_arm.values()))
|
|
1650
|
+
if any(dependencies != first for dependencies in by_arm.values()):
|
|
1651
|
+
raise ArtifactError(
|
|
1652
|
+
"retriever-factorial nuisance dependencies differ across arms within a cell"
|
|
1653
|
+
)
|
|
1654
|
+
if not required_slots.issubset(first):
|
|
1655
|
+
raise ArtifactError(
|
|
1656
|
+
"retriever-factorial dependency receipt lacks backend, retriever, "
|
|
1657
|
+
"predictor, or seed identity"
|
|
1658
|
+
)
|
|
1659
|
+
if reference_dependencies is None:
|
|
1660
|
+
reference_dependencies = first
|
|
1661
|
+
elif first != reference_dependencies:
|
|
1662
|
+
raise ArtifactError(
|
|
1663
|
+
"retriever-factorial runtime dependencies drift across groups within a cell"
|
|
1664
|
+
)
|
|
1665
|
+
current_dataset = payload.get("dataset_identity_sha256")
|
|
1666
|
+
if (
|
|
1667
|
+
not isinstance(current_dataset, str)
|
|
1668
|
+
or len(current_dataset) != 64
|
|
1669
|
+
or any(character not in "0123456789abcdef" for character in current_dataset)
|
|
1670
|
+
):
|
|
1671
|
+
raise ArtifactError("retriever-factorial result lacks dataset identity SHA-256")
|
|
1672
|
+
if dataset_identity is None:
|
|
1673
|
+
dataset_identity = current_dataset
|
|
1674
|
+
elif current_dataset != dataset_identity:
|
|
1675
|
+
raise ArtifactError(
|
|
1676
|
+
"retriever-factorial dataset identity drifts across groups within a cell"
|
|
1677
|
+
)
|
|
1678
|
+
if reference_dependencies is None or dataset_identity is None: # pragma: no cover
|
|
1679
|
+
raise ArtifactError("retriever-factorial dependency receipt has no records")
|
|
1680
|
+
return {
|
|
1681
|
+
"dependencies": reference_dependencies,
|
|
1682
|
+
"dataset_identity_sha256": dataset_identity,
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
|
|
1686
|
+
def _validate_retriever_factorial_balance(
|
|
1687
|
+
receipts: Mapping[tuple[str, int], Mapping[str, Any]],
|
|
1688
|
+
) -> Mapping[str, Any]:
|
|
1689
|
+
"""Verify all non-factorial runtime identities across the four dependency cells."""
|
|
1690
|
+
|
|
1691
|
+
dependency_maps = {key: dict(receipt["dependencies"]) for key, receipt in receipts.items()}
|
|
1692
|
+
key_sets = {frozenset(dependencies) for dependencies in dependency_maps.values()}
|
|
1693
|
+
if len(key_sets) != 1:
|
|
1694
|
+
raise ArtifactError("retriever-factorial cells expose different dependency slots")
|
|
1695
|
+
slots = next(iter(key_sets))
|
|
1696
|
+
dataset_hashes = {str(receipt["dataset_identity_sha256"]) for receipt in receipts.values()}
|
|
1697
|
+
if len(dataset_hashes) != 1:
|
|
1698
|
+
raise ArtifactError("retriever-factorial cells use different dataset/corpus identities")
|
|
1699
|
+
varied_slots = {"retriever", "downstream_predictor"}
|
|
1700
|
+
for slot in slots - varied_slots:
|
|
1701
|
+
if len({dependencies[slot] for dependencies in dependency_maps.values()}) != 1:
|
|
1702
|
+
raise ArtifactError(
|
|
1703
|
+
f"retriever-factorial non-varied dependency {slot!r} differs across cells"
|
|
1704
|
+
)
|
|
1705
|
+
retriever_hashes: dict[str, str] = {}
|
|
1706
|
+
for retriever in _PAPER_RETRIEVERS:
|
|
1707
|
+
values = {dependency_maps[(retriever, top_k)]["retriever"] for top_k in _PAPER_TOP_K}
|
|
1708
|
+
if len(values) != 1:
|
|
1709
|
+
raise ArtifactError(
|
|
1710
|
+
"retriever identity changes across top-k cells for the same retriever"
|
|
1711
|
+
)
|
|
1712
|
+
retriever_hashes[retriever] = next(iter(values))
|
|
1713
|
+
if len(set(retriever_hashes.values())) != len(_PAPER_RETRIEVERS):
|
|
1714
|
+
raise ArtifactError("the two declared retrievers resolve to the same runtime identity")
|
|
1715
|
+
predictor_hashes: dict[int, str] = {}
|
|
1716
|
+
for top_k in _PAPER_TOP_K:
|
|
1717
|
+
values = {
|
|
1718
|
+
dependency_maps[(retriever, top_k)]["downstream_predictor"]
|
|
1719
|
+
for retriever in _PAPER_RETRIEVERS
|
|
1720
|
+
}
|
|
1721
|
+
if len(values) != 1:
|
|
1722
|
+
raise ArtifactError(
|
|
1723
|
+
"downstream predictor identity changes across retrievers at fixed top-k"
|
|
1724
|
+
)
|
|
1725
|
+
predictor_hashes[top_k] = next(iter(values))
|
|
1726
|
+
if len(set(predictor_hashes.values())) != len(_PAPER_TOP_K):
|
|
1727
|
+
raise ArtifactError("the two declared top-k cells resolve to the same predictor identity")
|
|
1728
|
+
return {
|
|
1729
|
+
"dataset_identity_sha256": next(iter(dataset_hashes)),
|
|
1730
|
+
"shared_dependency_slots": sorted(slots - varied_slots),
|
|
1731
|
+
"retriever_slot_hashes": retriever_hashes,
|
|
1732
|
+
"predictor_slot_hashes_by_top_k": {
|
|
1733
|
+
str(top_k): digest for top_k, digest in predictor_hashes.items()
|
|
1734
|
+
},
|
|
1735
|
+
"balance_verified": True,
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
|
|
1739
|
+
def _effect_summary(
|
|
1740
|
+
values: Sequence[float],
|
|
1741
|
+
*,
|
|
1742
|
+
bootstrap_samples: int,
|
|
1743
|
+
rng: random.Random,
|
|
1744
|
+
) -> Mapping[str, Any]:
|
|
1745
|
+
return {
|
|
1746
|
+
"cluster_count": len(values),
|
|
1747
|
+
"mean_difference": fmean(values),
|
|
1748
|
+
"cluster_bootstrap_95ci": list(
|
|
1749
|
+
_cluster_bootstrap_interval(values, samples=bootstrap_samples, rng=rng)
|
|
1750
|
+
),
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
|
|
1754
|
+
def _one_sided_sign_flip_p_value(
|
|
1755
|
+
shifted_effects: Sequence[float],
|
|
1756
|
+
*,
|
|
1757
|
+
samples: int,
|
|
1758
|
+
rng: random.Random,
|
|
1759
|
+
) -> float:
|
|
1760
|
+
if len(shifted_effects) < 2:
|
|
1761
|
+
raise ArtifactError("paired sign-flip test requires at least two groups")
|
|
1762
|
+
observed = fmean(shifted_effects)
|
|
1763
|
+
exceedances = 0
|
|
1764
|
+
for _ in range(samples):
|
|
1765
|
+
permuted = fmean(value if rng.random() < 0.5 else -value for value in shifted_effects)
|
|
1766
|
+
if permuted >= observed - 1e-15:
|
|
1767
|
+
exceedances += 1
|
|
1768
|
+
return (exceedances + 1.0) / (samples + 1.0)
|
|
1769
|
+
|
|
1770
|
+
|
|
1771
|
+
def _bounded_protocol_number(
|
|
1772
|
+
value: Any,
|
|
1773
|
+
*,
|
|
1774
|
+
lower: float,
|
|
1775
|
+
upper: float | None,
|
|
1776
|
+
label: str,
|
|
1777
|
+
) -> float:
|
|
1778
|
+
if (
|
|
1779
|
+
isinstance(value, bool)
|
|
1780
|
+
or not isinstance(value, int | float)
|
|
1781
|
+
or not math.isfinite(value)
|
|
1782
|
+
or value < lower
|
|
1783
|
+
or (upper is not None and value > upper)
|
|
1784
|
+
):
|
|
1785
|
+
suffix = f" and <= {upper}" if upper is not None else ""
|
|
1786
|
+
raise ValidationError(f"paper Selector {label} must be finite, >= {lower}{suffix}")
|
|
1787
|
+
return float(value)
|
|
1788
|
+
|
|
1789
|
+
|
|
1790
|
+
def _run_roots(run_dirs: Sequence[str | Path]) -> tuple[Path, ...]:
|
|
1791
|
+
if isinstance(run_dirs, str | bytes):
|
|
1792
|
+
raise ValidationError("run_dirs must be a sequence of paths")
|
|
1793
|
+
roots = tuple(Path(path).expanduser().resolve() for path in run_dirs)
|
|
1794
|
+
if not roots:
|
|
1795
|
+
raise ValidationError("at least one run directory is required")
|
|
1796
|
+
if len(roots) != len(set(roots)):
|
|
1797
|
+
raise ValidationError("run directories must be unique")
|
|
1798
|
+
return roots
|
|
1799
|
+
|
|
1800
|
+
|
|
1801
|
+
__all__ = [
|
|
1802
|
+
"LEGACY_CONFIRMATORY_PROTOCOL",
|
|
1803
|
+
"PAPER_COMMON_SUPPORT_PROTOCOL",
|
|
1804
|
+
"PAPER_FAMILY_NATIVE_PROTOCOL",
|
|
1805
|
+
"PAPER_RETRIEVER_FACTORIAL_PROTOCOL",
|
|
1806
|
+
"PAPER_SELECTOR_PROTOCOL",
|
|
1807
|
+
"analyze_confirmatory_protocol",
|
|
1808
|
+
"analyze_paper_common_support",
|
|
1809
|
+
"analyze_paper_dual_arena_retriever_factorial",
|
|
1810
|
+
"analyze_paper_family_confirmation",
|
|
1811
|
+
"analyze_paper_retriever_factorial",
|
|
1812
|
+
"analyze_paper_selector_claim_hierarchy",
|
|
1813
|
+
"build_paper_selector_claim_gates",
|
|
1814
|
+
"infer_paper_run_protocol",
|
|
1815
|
+
"validate_paper_run_contract",
|
|
1816
|
+
"validate_paper_selector_analysis_contract",
|
|
1817
|
+
"validate_paper_selector_claim_gates",
|
|
1818
|
+
]
|