simjecture 0.1.0__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.
- conjecture_solver/__init__.py +10 -0
- conjecture_solver/__main__.py +4 -0
- conjecture_solver/action_handlers.py +459 -0
- conjecture_solver/adapters/__init__.py +7 -0
- conjecture_solver/adapters/base.py +96 -0
- conjecture_solver/adapters/fake.py +155 -0
- conjecture_solver/adapters/pic.py +196 -0
- conjecture_solver/adapters/warpx.py +1375 -0
- conjecture_solver/autonomous_research.py +1071 -0
- conjecture_solver/benchmarks/__init__.py +6 -0
- conjecture_solver/benchmarks/electrostatic_pic.py +547 -0
- conjecture_solver/benchmarks/kinetic_sufficiency.py +318 -0
- conjecture_solver/builtin_skills/warpx/SKILL.md +151 -0
- conjecture_solver/builtin_skills/warpx/agents/openai.yaml +4 -0
- conjecture_solver/builtin_skills/warpx/examples/implicit_em_smoke.py +74 -0
- conjecture_solver/builtin_skills/warpx/examples/minimal_smoke.py +54 -0
- conjecture_solver/builtin_skills/warpx/examples/openpmd_field_smoke.py +87 -0
- conjecture_solver/builtin_skills/warpx/manifest.json +11 -0
- conjecture_solver/builtin_skills/warpx/references/2d-xz-commissioning.md +145 -0
- conjecture_solver/builtin_skills/warpx/references/cpu-launch-tuning.md +54 -0
- conjecture_solver/builtin_skills/warpx/references/diagnostics.md +139 -0
- conjecture_solver/builtin_skills/warpx/references/gpu-launch-tuning.md +79 -0
- conjecture_solver/builtin_skills/warpx/references/local-cuda-deployment.md +87 -0
- conjecture_solver/builtin_skills/warpx/references/numerical-risks.md +32 -0
- conjecture_solver/builtin_skills/warpx/references/picmi-interface.md +118 -0
- conjecture_solver/builtin_skills/warpx/references/resource-scaling.md +24 -0
- conjecture_solver/builtin_skills/warpx/references/time-integration.md +58 -0
- conjecture_solver/builtin_skills/warpx/scripts/benchmark_cpu_threads.py +207 -0
- conjecture_solver/builtin_skills/warpx/scripts/benchmark_gpu.py +242 -0
- conjecture_solver/builtin_skills/warpx/scripts/bootstrap_local_cuda.sh +82 -0
- conjecture_solver/builtin_skills/warpx/scripts/probe_local_cuda.py +110 -0
- conjecture_solver/builtin_skills/warpx/scripts/reduced_energy_budget.py +195 -0
- conjecture_solver/builtin_skills/warpx/scripts/run_local_cuda.sh +39 -0
- conjecture_solver/campaign.py +406 -0
- conjecture_solver/cli.py +963 -0
- conjecture_solver/confirmation.py +287 -0
- conjecture_solver/control.py +234 -0
- conjecture_solver/deployment.py +874 -0
- conjecture_solver/discovery.py +68 -0
- conjecture_solver/domains/__init__.py +40 -0
- conjecture_solver/domains/base.py +157 -0
- conjecture_solver/domains/kinetic_sufficiency.py +521 -0
- conjecture_solver/ledger.py +238 -0
- conjecture_solver/lifecycle.py +131 -0
- conjecture_solver/literature.py +494 -0
- conjecture_solver/llm.py +228 -0
- conjecture_solver/models.py +279 -0
- conjecture_solver/mvp_agent.py +3212 -0
- conjecture_solver/mvp_claims.py +1279 -0
- conjecture_solver/mvp_control.py +322 -0
- conjecture_solver/mvp_guidance.py +172 -0
- conjecture_solver/mvp_launch.py +1296 -0
- conjecture_solver/mvp_monitor.py +1487 -0
- conjecture_solver/mvp_skills.py +538 -0
- conjecture_solver/orchestration.py +578 -0
- conjecture_solver/outbox.py +526 -0
- conjecture_solver/parameters.py +394 -0
- conjecture_solver/proposals.py +427 -0
- conjecture_solver/research_tools.py +278 -0
- conjecture_solver/schema_export.py +246 -0
- conjecture_solver/search.py +1047 -0
- conjecture_solver/semantics.py +146 -0
- conjecture_solver/tui/__init__.py +7 -0
- conjecture_solver/tui/app.py +202 -0
- conjecture_solver/tui/claim_views.py +213 -0
- conjecture_solver/tui/screens.py +1014 -0
- conjecture_solver/warpx_analysis.py +184 -0
- conjecture_solver/warpx_campaign.py +148 -0
- conjecture_solver/warpx_confirmation.py +617 -0
- simjecture-0.1.0.dist-info/METADATA +400 -0
- simjecture-0.1.0.dist-info/RECORD +75 -0
- simjecture-0.1.0.dist-info/WHEEL +4 -0
- simjecture-0.1.0.dist-info/entry_points.txt +4 -0
- simjecture-0.1.0.dist-info/licenses/LICENSE +202 -0
- simjecture-0.1.0.dist-info/licenses/NOTICE +6 -0
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
"""Crash-recoverable runner for the first end-to-end scientific campaign."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from datetime import UTC, datetime
|
|
8
|
+
from enum import StrEnum
|
|
9
|
+
|
|
10
|
+
from .adapters.base import (
|
|
11
|
+
JobReference,
|
|
12
|
+
JobState,
|
|
13
|
+
NormalizedResult,
|
|
14
|
+
RunPackage,
|
|
15
|
+
SimulatorAdapter,
|
|
16
|
+
)
|
|
17
|
+
from .benchmarks.kinetic_sufficiency import build_problem
|
|
18
|
+
from .control import CampaignControl
|
|
19
|
+
from .discovery import DiscoveryPackage
|
|
20
|
+
from .ledger import SQLiteEventLedger, StoredEvent
|
|
21
|
+
from .models import (
|
|
22
|
+
AttemptOutcome,
|
|
23
|
+
AttemptRecord,
|
|
24
|
+
Claim,
|
|
25
|
+
ClaimDisposition,
|
|
26
|
+
EvidenceRole,
|
|
27
|
+
ExperimentSpec,
|
|
28
|
+
HypothesisNode,
|
|
29
|
+
RunEvidence,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class CrashPoint(StrEnum):
|
|
34
|
+
AFTER_CAMPAIGN_CREATED = "after_campaign_created"
|
|
35
|
+
AFTER_ACTION_PLANNED = "after_action_planned"
|
|
36
|
+
AFTER_ATTEMPT_RECORDED = "after_attempt_recorded"
|
|
37
|
+
AFTER_SUBMIT_BEFORE_COMMIT = "after_submit_before_commit"
|
|
38
|
+
AFTER_JOB_COMMITTED = "after_job_committed"
|
|
39
|
+
AFTER_RETRIEVE_BEFORE_COMMIT = "after_retrieve_before_commit"
|
|
40
|
+
AFTER_RESULT_COMMITTED = "after_result_committed"
|
|
41
|
+
AFTER_ATTEMPT_COMPLETED = "after_attempt_completed"
|
|
42
|
+
AFTER_EVIDENCE_COMMIT = "after_evidence_commit"
|
|
43
|
+
AFTER_CLAIM_COMMIT = "after_claim_commit"
|
|
44
|
+
AFTER_CAMPAIGN_COMPLETED = "after_campaign_completed"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class InjectedCrash(RuntimeError):
|
|
48
|
+
def __init__(self, point: CrashPoint) -> None:
|
|
49
|
+
super().__init__(f"injected crash at {point.value}")
|
|
50
|
+
self.point = point
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass
|
|
54
|
+
class CampaignProjection:
|
|
55
|
+
hypothesis: HypothesisNode | None = None
|
|
56
|
+
experiment: ExperimentSpec | None = None
|
|
57
|
+
run_package: RunPackage | None = None
|
|
58
|
+
attempt: AttemptRecord | None = None
|
|
59
|
+
job: JobReference | None = None
|
|
60
|
+
normalized_result: NormalizedResult | None = None
|
|
61
|
+
evidence: RunEvidence | None = None
|
|
62
|
+
claim: Claim | None = None
|
|
63
|
+
completed_event: StoredEvent | None = None
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
def replay(cls, events: tuple[StoredEvent, ...]) -> CampaignProjection:
|
|
67
|
+
state = cls()
|
|
68
|
+
for event in events:
|
|
69
|
+
payload = event.payload
|
|
70
|
+
if event.event_type == "campaign_created":
|
|
71
|
+
state.hypothesis = HypothesisNode.model_validate(payload["hypothesis"])
|
|
72
|
+
state.experiment = ExperimentSpec.model_validate(payload["experiment"])
|
|
73
|
+
elif event.event_type == "action_planned":
|
|
74
|
+
state.run_package = RunPackage.model_validate(payload["run_package"])
|
|
75
|
+
elif event.event_type in {"attempt_recorded", "attempt_completed"}:
|
|
76
|
+
state.attempt = AttemptRecord.model_validate(payload["attempt"])
|
|
77
|
+
elif event.event_type == "job_submitted":
|
|
78
|
+
state.job = JobReference.model_validate(payload["job"])
|
|
79
|
+
elif event.event_type == "result_retrieved":
|
|
80
|
+
state.normalized_result = NormalizedResult.model_validate(
|
|
81
|
+
payload["normalized_result"]
|
|
82
|
+
)
|
|
83
|
+
elif event.event_type == "evidence_ingested":
|
|
84
|
+
state.evidence = RunEvidence.model_validate(payload["evidence"])
|
|
85
|
+
elif event.event_type == "claim_evaluated":
|
|
86
|
+
state.claim = Claim.model_validate(payload["claim"])
|
|
87
|
+
elif event.event_type == "campaign_completed":
|
|
88
|
+
state.completed_event = event
|
|
89
|
+
return state
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def planted_experiment() -> ExperimentSpec:
|
|
93
|
+
return ExperimentSpec(
|
|
94
|
+
id="experiment_kinetic_sufficiency_v1",
|
|
95
|
+
hypothesis_ids=("hypothesis_low_moments_sufficient_for_stability",),
|
|
96
|
+
action_type="kinetic_sufficiency",
|
|
97
|
+
physical_parameters={"wavenumber": 0.5},
|
|
98
|
+
required_diagnostics=("dominant_linear_mode", "distribution_moments"),
|
|
99
|
+
predictions={
|
|
100
|
+
"sufficiency": "matched distributions have equal growth rate",
|
|
101
|
+
"counterexample": "matched distributions differ in growth rate",
|
|
102
|
+
},
|
|
103
|
+
falsification_condition="a matched pair differs by more than 0.02",
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def planted_campaign_problem() -> tuple[HypothesisNode, ExperimentSpec]:
|
|
108
|
+
hypothesis, _ = build_problem()
|
|
109
|
+
return hypothesis, planted_experiment()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class CampaignRunner:
|
|
113
|
+
"""Resume one campaign by replaying its ledger before each side effect."""
|
|
114
|
+
|
|
115
|
+
def __init__(
|
|
116
|
+
self,
|
|
117
|
+
*,
|
|
118
|
+
campaign_id: str,
|
|
119
|
+
ledger: SQLiteEventLedger,
|
|
120
|
+
adapter: SimulatorAdapter,
|
|
121
|
+
hypothesis: HypothesisNode,
|
|
122
|
+
experiment: ExperimentSpec,
|
|
123
|
+
crash_at: CrashPoint | None = None,
|
|
124
|
+
control: CampaignControl | None = None,
|
|
125
|
+
) -> None:
|
|
126
|
+
self.campaign_id = campaign_id
|
|
127
|
+
self.ledger = ledger
|
|
128
|
+
self.adapter = adapter
|
|
129
|
+
self.hypothesis = hypothesis
|
|
130
|
+
self.experiment = experiment
|
|
131
|
+
self.crash_at = crash_at
|
|
132
|
+
if control is not None and control.campaign_id != campaign_id:
|
|
133
|
+
raise ValueError("control plane targets a different campaign")
|
|
134
|
+
self.control = control
|
|
135
|
+
|
|
136
|
+
def _crash(self, point: CrashPoint) -> None:
|
|
137
|
+
if self.crash_at is point:
|
|
138
|
+
raise InjectedCrash(point)
|
|
139
|
+
|
|
140
|
+
def _events(self) -> tuple[StoredEvent, ...]:
|
|
141
|
+
return self.ledger.load(self.campaign_id)
|
|
142
|
+
|
|
143
|
+
def _state(self) -> CampaignProjection:
|
|
144
|
+
return CampaignProjection.replay(self._events())
|
|
145
|
+
|
|
146
|
+
def _append(
|
|
147
|
+
self,
|
|
148
|
+
event_type: str,
|
|
149
|
+
aggregate_type: str,
|
|
150
|
+
aggregate_id: str,
|
|
151
|
+
payload: dict[str, object],
|
|
152
|
+
) -> StoredEvent:
|
|
153
|
+
result = self.ledger.append(
|
|
154
|
+
campaign_id=self.campaign_id,
|
|
155
|
+
event_type=event_type,
|
|
156
|
+
aggregate_type=aggregate_type,
|
|
157
|
+
aggregate_id=aggregate_id,
|
|
158
|
+
payload=payload,
|
|
159
|
+
idempotency_key=f"{self.campaign_id}:{event_type}",
|
|
160
|
+
)
|
|
161
|
+
return result.event
|
|
162
|
+
|
|
163
|
+
def _validate_identity(self, state: CampaignProjection) -> None:
|
|
164
|
+
if state.experiment is not None and state.experiment != self.experiment:
|
|
165
|
+
raise ValueError("campaign already exists with a different experiment")
|
|
166
|
+
if state.hypothesis is not None and state.hypothesis != self.hypothesis:
|
|
167
|
+
raise ValueError("campaign already exists with a different hypothesis")
|
|
168
|
+
|
|
169
|
+
def _package(self, state: CampaignProjection) -> DiscoveryPackage:
|
|
170
|
+
if not all(
|
|
171
|
+
(
|
|
172
|
+
state.hypothesis,
|
|
173
|
+
state.experiment,
|
|
174
|
+
state.attempt,
|
|
175
|
+
state.normalized_result,
|
|
176
|
+
state.evidence,
|
|
177
|
+
state.claim,
|
|
178
|
+
state.completed_event,
|
|
179
|
+
)
|
|
180
|
+
):
|
|
181
|
+
raise RuntimeError("cannot package an incomplete campaign")
|
|
182
|
+
assert state.hypothesis is not None
|
|
183
|
+
assert state.experiment is not None
|
|
184
|
+
assert state.attempt is not None
|
|
185
|
+
assert state.normalized_result is not None
|
|
186
|
+
assert state.evidence is not None
|
|
187
|
+
assert state.claim is not None
|
|
188
|
+
assert state.completed_event is not None
|
|
189
|
+
return DiscoveryPackage.create(
|
|
190
|
+
campaign_id=self.campaign_id,
|
|
191
|
+
hypothesis=state.hypothesis,
|
|
192
|
+
experiment=state.experiment,
|
|
193
|
+
attempt=state.attempt,
|
|
194
|
+
evidence=state.evidence,
|
|
195
|
+
claim=state.claim,
|
|
196
|
+
normalized_result=state.normalized_result,
|
|
197
|
+
provenance_event_hashes=tuple(event.event_hash for event in self._events()),
|
|
198
|
+
generated_at=datetime.fromisoformat(state.completed_event.created_at),
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
def run(self) -> DiscoveryPackage:
|
|
202
|
+
state = self._state()
|
|
203
|
+
self._validate_identity(state)
|
|
204
|
+
if state.completed_event is not None:
|
|
205
|
+
return self._package(state)
|
|
206
|
+
|
|
207
|
+
if state.experiment is None:
|
|
208
|
+
self._append(
|
|
209
|
+
"campaign_created",
|
|
210
|
+
"campaign",
|
|
211
|
+
self.campaign_id,
|
|
212
|
+
{
|
|
213
|
+
"hypothesis": self.hypothesis.model_dump(mode="json"),
|
|
214
|
+
"experiment": self.experiment.model_dump(mode="json"),
|
|
215
|
+
},
|
|
216
|
+
)
|
|
217
|
+
self._crash(CrashPoint.AFTER_CAMPAIGN_CREATED)
|
|
218
|
+
state = self._state()
|
|
219
|
+
|
|
220
|
+
if state.run_package is None:
|
|
221
|
+
validation = self.adapter.validate(self.experiment)
|
|
222
|
+
if not validation.valid:
|
|
223
|
+
raise ValueError("; ".join(validation.errors))
|
|
224
|
+
run_package = self.adapter.compile_input(self.experiment)
|
|
225
|
+
self._append(
|
|
226
|
+
"action_planned",
|
|
227
|
+
"experiment",
|
|
228
|
+
self.experiment.id,
|
|
229
|
+
{
|
|
230
|
+
"validation": validation.model_dump(mode="json"),
|
|
231
|
+
"run_package": run_package.model_dump(mode="json"),
|
|
232
|
+
},
|
|
233
|
+
)
|
|
234
|
+
self._crash(CrashPoint.AFTER_ACTION_PLANNED)
|
|
235
|
+
state = self._state()
|
|
236
|
+
assert state.run_package is not None
|
|
237
|
+
|
|
238
|
+
if self.control is not None and state.job is None:
|
|
239
|
+
self.control.require_action_authority(self.experiment.id)
|
|
240
|
+
|
|
241
|
+
attempt_id = f"attempt_{hashlib.sha256(self.campaign_id.encode()).hexdigest()[:16]}"
|
|
242
|
+
submission_key = f"{self.campaign_id}:submit:{state.run_package.package_hash}"
|
|
243
|
+
if state.attempt is None:
|
|
244
|
+
attempt = AttemptRecord(
|
|
245
|
+
id=attempt_id,
|
|
246
|
+
experiment_id=self.experiment.id,
|
|
247
|
+
idempotency_key=submission_key,
|
|
248
|
+
)
|
|
249
|
+
self._append(
|
|
250
|
+
"attempt_recorded",
|
|
251
|
+
"attempt",
|
|
252
|
+
attempt.id,
|
|
253
|
+
{"attempt": attempt.model_dump(mode="json")},
|
|
254
|
+
)
|
|
255
|
+
self._crash(CrashPoint.AFTER_ATTEMPT_RECORDED)
|
|
256
|
+
state = self._state()
|
|
257
|
+
assert state.attempt is not None
|
|
258
|
+
|
|
259
|
+
# Re-submission is intentional: a real scheduler must treat this as an
|
|
260
|
+
# idempotent attach, including after the process died before commit.
|
|
261
|
+
attached_job = self.adapter.submit(
|
|
262
|
+
state.run_package,
|
|
263
|
+
idempotency_key=state.attempt.idempotency_key,
|
|
264
|
+
)
|
|
265
|
+
if state.job is not None and attached_job != state.job:
|
|
266
|
+
raise RuntimeError("adapter violated the idempotent submission contract")
|
|
267
|
+
if state.job is None:
|
|
268
|
+
self._crash(CrashPoint.AFTER_SUBMIT_BEFORE_COMMIT)
|
|
269
|
+
self._append(
|
|
270
|
+
"job_submitted",
|
|
271
|
+
"attempt",
|
|
272
|
+
state.attempt.id,
|
|
273
|
+
{"job": attached_job.model_dump(mode="json")},
|
|
274
|
+
)
|
|
275
|
+
self._crash(CrashPoint.AFTER_JOB_COMMITTED)
|
|
276
|
+
state = self._state()
|
|
277
|
+
assert state.job is not None
|
|
278
|
+
|
|
279
|
+
if state.normalized_result is None:
|
|
280
|
+
status = self.adapter.monitor(state.job)
|
|
281
|
+
if status.state is not JobState.COMPLETED:
|
|
282
|
+
raise RuntimeError(f"job is not complete: {status.state.value}")
|
|
283
|
+
raw_result = self.adapter.retrieve(state.job)
|
|
284
|
+
normalized = self.adapter.normalize(raw_result)
|
|
285
|
+
self._crash(CrashPoint.AFTER_RETRIEVE_BEFORE_COMMIT)
|
|
286
|
+
self._append(
|
|
287
|
+
"result_retrieved",
|
|
288
|
+
"attempt",
|
|
289
|
+
state.attempt.id,
|
|
290
|
+
{
|
|
291
|
+
"raw_artifact_hashes": list(raw_result.artifact_hashes),
|
|
292
|
+
"normalized_result": normalized.model_dump(mode="json"),
|
|
293
|
+
},
|
|
294
|
+
)
|
|
295
|
+
self._crash(CrashPoint.AFTER_RESULT_COMMITTED)
|
|
296
|
+
state = self._state()
|
|
297
|
+
assert state.normalized_result is not None
|
|
298
|
+
|
|
299
|
+
if state.attempt.outcome is None:
|
|
300
|
+
completed = AttemptRecord.model_validate(
|
|
301
|
+
{
|
|
302
|
+
**state.attempt.model_dump(mode="json"),
|
|
303
|
+
"outcome": AttemptOutcome.SUCCESS.value,
|
|
304
|
+
"completed_at": datetime.now(UTC).isoformat(),
|
|
305
|
+
"external_job_id": state.job.job_id,
|
|
306
|
+
}
|
|
307
|
+
)
|
|
308
|
+
self._append(
|
|
309
|
+
"attempt_completed",
|
|
310
|
+
"attempt",
|
|
311
|
+
completed.id,
|
|
312
|
+
{"attempt": completed.model_dump(mode="json")},
|
|
313
|
+
)
|
|
314
|
+
self._crash(CrashPoint.AFTER_ATTEMPT_COMPLETED)
|
|
315
|
+
state = self._state()
|
|
316
|
+
assert state.attempt is not None
|
|
317
|
+
|
|
318
|
+
if self.control is not None:
|
|
319
|
+
self.control.require_processing_authority()
|
|
320
|
+
|
|
321
|
+
if state.evidence is None:
|
|
322
|
+
observables = state.normalized_result.observables
|
|
323
|
+
scientific_eligibility = bool(
|
|
324
|
+
state.normalized_result.diagnostics.get(
|
|
325
|
+
"scientific_evidence_eligible",
|
|
326
|
+
True,
|
|
327
|
+
)
|
|
328
|
+
)
|
|
329
|
+
evidence = RunEvidence(
|
|
330
|
+
id=f"evidence_{attempt_id.removeprefix('attempt_')}",
|
|
331
|
+
source_attempt_id=attempt_id,
|
|
332
|
+
role=EvidenceRole.DISCOVERY,
|
|
333
|
+
eligible=scientific_eligibility,
|
|
334
|
+
eligibility_reason=(
|
|
335
|
+
"successful run passed adapter and result validity gates"
|
|
336
|
+
if scientific_eligibility
|
|
337
|
+
else "adapter marked the execution profile or numerical result ineligible"
|
|
338
|
+
),
|
|
339
|
+
observable_values={
|
|
340
|
+
"maxwellian_growth_rate": float(observables["maxwellian_growth_rate"]),
|
|
341
|
+
"two_stream_growth_rate": float(observables["two_stream_growth_rate"]),
|
|
342
|
+
"outcome_separation": abs(
|
|
343
|
+
float(observables["two_stream_growth_rate"])
|
|
344
|
+
- float(observables["maxwellian_growth_rate"])
|
|
345
|
+
),
|
|
346
|
+
},
|
|
347
|
+
independence_group=(
|
|
348
|
+
f"{state.run_package.adapter_name}:{state.run_package.package_hash}"
|
|
349
|
+
),
|
|
350
|
+
artifact_hashes=state.normalized_result.artifact_hashes,
|
|
351
|
+
)
|
|
352
|
+
self._append(
|
|
353
|
+
"evidence_ingested",
|
|
354
|
+
"evidence",
|
|
355
|
+
evidence.id,
|
|
356
|
+
{"evidence": evidence.model_dump(mode="json")},
|
|
357
|
+
)
|
|
358
|
+
self._crash(CrashPoint.AFTER_EVIDENCE_COMMIT)
|
|
359
|
+
state = self._state()
|
|
360
|
+
assert state.evidence is not None
|
|
361
|
+
|
|
362
|
+
if state.claim is None:
|
|
363
|
+
falsified = state.evidence.eligible and bool(
|
|
364
|
+
state.normalized_result.observables["hypothesis_falsified"]
|
|
365
|
+
)
|
|
366
|
+
disposition = (
|
|
367
|
+
ClaimDisposition.REFUTED_WITHIN_MODEL
|
|
368
|
+
if falsified
|
|
369
|
+
else ClaimDisposition.UNRESOLVED
|
|
370
|
+
)
|
|
371
|
+
claim = Claim(
|
|
372
|
+
id=f"claim_{hashlib.sha256(self.campaign_id.encode()).hexdigest()[:16]}",
|
|
373
|
+
hypothesis_id=self.hypothesis.id,
|
|
374
|
+
statement=(
|
|
375
|
+
"The low-order-moment predictive-sufficiency hypothesis is "
|
|
376
|
+
"refuted within the declared kinetic model."
|
|
377
|
+
if falsified
|
|
378
|
+
else "The declared test did not refute the hypothesis."
|
|
379
|
+
),
|
|
380
|
+
disposition=disposition,
|
|
381
|
+
scope=self.hypothesis.domain.description,
|
|
382
|
+
evidence_ids=(state.evidence.id,),
|
|
383
|
+
limitations=(
|
|
384
|
+
"discovery evidence; independent confirmation has not been run",
|
|
385
|
+
"scope is restricted to the preregistered model family and k=0.5",
|
|
386
|
+
),
|
|
387
|
+
)
|
|
388
|
+
self._append(
|
|
389
|
+
"claim_evaluated",
|
|
390
|
+
"claim",
|
|
391
|
+
claim.id,
|
|
392
|
+
{"claim": claim.model_dump(mode="json")},
|
|
393
|
+
)
|
|
394
|
+
self._crash(CrashPoint.AFTER_CLAIM_COMMIT)
|
|
395
|
+
|
|
396
|
+
state = self._state()
|
|
397
|
+
assert state.claim is not None
|
|
398
|
+
self._append(
|
|
399
|
+
"campaign_completed",
|
|
400
|
+
"campaign",
|
|
401
|
+
self.campaign_id,
|
|
402
|
+
{"claim_id": state.claim.id},
|
|
403
|
+
)
|
|
404
|
+
self._crash(CrashPoint.AFTER_CAMPAIGN_COMPLETED)
|
|
405
|
+
final_state = self._state()
|
|
406
|
+
return self._package(final_state)
|