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,459 @@
|
|
|
1
|
+
"""Registered handlers for the first multi-action scientific campaign."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
|
|
9
|
+
from pydantic import ValidationError
|
|
10
|
+
|
|
11
|
+
from .adapters.base import SimulatorAdapter
|
|
12
|
+
from .benchmarks.electrostatic_pic import PICNumericalConfig
|
|
13
|
+
from .benchmarks.kinetic_sufficiency import build_problem
|
|
14
|
+
from .confirmation import (
|
|
15
|
+
PICConfirmationDesign,
|
|
16
|
+
PICConfirmationRunner,
|
|
17
|
+
confirmation_design_from_search,
|
|
18
|
+
)
|
|
19
|
+
from .models import Claim, ClaimDisposition, EvidenceRole, RunEvidence
|
|
20
|
+
from .orchestration import (
|
|
21
|
+
ActionContext,
|
|
22
|
+
ActionExecution,
|
|
23
|
+
ActionExecutionError,
|
|
24
|
+
ActionFailureKind,
|
|
25
|
+
ActionHandler,
|
|
26
|
+
ActionOrigin,
|
|
27
|
+
CampaignAction,
|
|
28
|
+
CampaignActionGraph,
|
|
29
|
+
CampaignBudget,
|
|
30
|
+
)
|
|
31
|
+
from .outbox import OutboxCrashPoint
|
|
32
|
+
from .search import (
|
|
33
|
+
BlindedSearchReport,
|
|
34
|
+
BlindedSearchRequest,
|
|
35
|
+
BlindedSearchRunner,
|
|
36
|
+
SearchStrategy,
|
|
37
|
+
baseline_strategies,
|
|
38
|
+
)
|
|
39
|
+
from .warpx_confirmation import (
|
|
40
|
+
InjectedWarpXConfirmationCrash,
|
|
41
|
+
QualifiedWarpXInstrument,
|
|
42
|
+
WarpXConfirmationCrashPoint,
|
|
43
|
+
WarpXConfirmationDesign,
|
|
44
|
+
WarpXConfirmationDisposition,
|
|
45
|
+
WarpXConfirmationReport,
|
|
46
|
+
WarpXConfirmationRunner,
|
|
47
|
+
default_warpx_confirmation_design,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
BLINDED_SEARCH_ACTION = "blinded_analytic_search"
|
|
51
|
+
PIC_CONFIRMATION_ACTION = "frozen_pic_confirmation"
|
|
52
|
+
QUALIFIED_WARPX_CONFIRMATION_ACTION = "qualified_warpx_confirmation"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _hash_output(output: dict[str, object]) -> str:
|
|
56
|
+
canonical = json.dumps(output, sort_keys=True, separators=(",", ":"))
|
|
57
|
+
return hashlib.sha256(canonical.encode()).hexdigest()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class BlindedSearchActionHandler:
|
|
61
|
+
def execute(
|
|
62
|
+
self,
|
|
63
|
+
context: ActionContext,
|
|
64
|
+
action: CampaignAction,
|
|
65
|
+
dependencies: dict[str, ActionExecution],
|
|
66
|
+
) -> ActionExecution:
|
|
67
|
+
if dependencies:
|
|
68
|
+
raise ActionExecutionError(
|
|
69
|
+
ActionFailureKind.SPECIFICATION,
|
|
70
|
+
"blinded search action must not receive dependency outputs",
|
|
71
|
+
)
|
|
72
|
+
try:
|
|
73
|
+
request = BlindedSearchRequest.model_validate(action.payload["request"])
|
|
74
|
+
strategies = tuple(
|
|
75
|
+
SearchStrategy.model_validate(strategy) for strategy in action.payload["strategies"]
|
|
76
|
+
)
|
|
77
|
+
except (KeyError, TypeError, ValidationError) as error:
|
|
78
|
+
raise ActionExecutionError(
|
|
79
|
+
ActionFailureKind.SPECIFICATION,
|
|
80
|
+
f"invalid blinded-search payload: {error}",
|
|
81
|
+
) from error
|
|
82
|
+
expected_units = request.evaluations_per_method * len(request.comparison_methods)
|
|
83
|
+
if abs(action.budget_units - expected_units) > 1e-12:
|
|
84
|
+
raise ActionExecutionError(
|
|
85
|
+
ActionFailureKind.SPECIFICATION,
|
|
86
|
+
"search budget units must equal the number of candidate evaluations",
|
|
87
|
+
)
|
|
88
|
+
try:
|
|
89
|
+
report = BlindedSearchRunner(
|
|
90
|
+
campaign_id=context.campaign_id,
|
|
91
|
+
ledger=context.ledger,
|
|
92
|
+
request=request,
|
|
93
|
+
strategies=strategies,
|
|
94
|
+
).run()
|
|
95
|
+
except ValueError as error:
|
|
96
|
+
raise ActionExecutionError(
|
|
97
|
+
ActionFailureKind.SPECIFICATION,
|
|
98
|
+
f"blinded search was rejected: {error}",
|
|
99
|
+
) from error
|
|
100
|
+
output: dict[str, object] = {"search_report": report.model_dump(mode="json")}
|
|
101
|
+
return ActionExecution(
|
|
102
|
+
action_id=action.id,
|
|
103
|
+
evidence_eligible=report.confirmation_candidate_id is not None,
|
|
104
|
+
output=output,
|
|
105
|
+
output_hash=_hash_output(output),
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class PICConfirmationActionHandler:
|
|
110
|
+
def execute(
|
|
111
|
+
self,
|
|
112
|
+
context: ActionContext,
|
|
113
|
+
action: CampaignAction,
|
|
114
|
+
dependencies: dict[str, ActionExecution],
|
|
115
|
+
) -> ActionExecution:
|
|
116
|
+
source_action_id = action.payload.get("source_action_id")
|
|
117
|
+
if not isinstance(source_action_id, str) or source_action_id not in dependencies:
|
|
118
|
+
raise ActionExecutionError(
|
|
119
|
+
ActionFailureKind.SPECIFICATION,
|
|
120
|
+
"confirmation source_action_id must name a completed dependency",
|
|
121
|
+
)
|
|
122
|
+
try:
|
|
123
|
+
search_report = BlindedSearchReport.model_validate(
|
|
124
|
+
dependencies[source_action_id].output["search_report"]
|
|
125
|
+
)
|
|
126
|
+
design = confirmation_design_from_search(search_report)
|
|
127
|
+
design = PICConfirmationDesign.model_validate(
|
|
128
|
+
{
|
|
129
|
+
**design.model_dump(mode="json"),
|
|
130
|
+
"seeds": action.payload["seeds"],
|
|
131
|
+
"velocity_beams": action.payload["velocity_beams"],
|
|
132
|
+
"base_config": action.payload["base_config"],
|
|
133
|
+
}
|
|
134
|
+
)
|
|
135
|
+
except (KeyError, TypeError, ValueError, ValidationError) as error:
|
|
136
|
+
raise ActionExecutionError(
|
|
137
|
+
ActionFailureKind.SPECIFICATION,
|
|
138
|
+
f"invalid PIC confirmation payload: {error}",
|
|
139
|
+
) from error
|
|
140
|
+
expected_units = 2 * len(design.configurations())
|
|
141
|
+
if abs(action.budget_units - expected_units) > 1e-12:
|
|
142
|
+
raise ActionExecutionError(
|
|
143
|
+
ActionFailureKind.SPECIFICATION,
|
|
144
|
+
"confirmation budget units must equal the number of PIC case executions",
|
|
145
|
+
)
|
|
146
|
+
report = PICConfirmationRunner(
|
|
147
|
+
campaign_id=context.campaign_id,
|
|
148
|
+
ledger=context.ledger,
|
|
149
|
+
design=design,
|
|
150
|
+
).run()
|
|
151
|
+
output: dict[str, object] = {"confirmation_report": report.model_dump(mode="json")}
|
|
152
|
+
return ActionExecution(
|
|
153
|
+
action_id=action.id,
|
|
154
|
+
evidence_eligible=all(attempt.eligible for attempt in report.attempts),
|
|
155
|
+
output=output,
|
|
156
|
+
output_hash=_hash_output(output),
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _warpx_evidence_and_claim(
|
|
161
|
+
context: ActionContext,
|
|
162
|
+
report: WarpXConfirmationReport,
|
|
163
|
+
) -> tuple[tuple[RunEvidence, ...], Claim]:
|
|
164
|
+
hypothesis, _ = build_problem()
|
|
165
|
+
evidence: list[RunEvidence] = []
|
|
166
|
+
group = f"warpx_picmi_qualified_{report.design.qualification_hash[:16]}"
|
|
167
|
+
for attempt in report.attempts:
|
|
168
|
+
observables = attempt.normalized_result.observables
|
|
169
|
+
identity = hashlib.sha256(
|
|
170
|
+
f"{context.campaign_id}:{attempt.resolution_id}:{attempt.seed}".encode()
|
|
171
|
+
).hexdigest()[:20]
|
|
172
|
+
evidence.append(
|
|
173
|
+
RunEvidence(
|
|
174
|
+
id=f"evidence_{identity}",
|
|
175
|
+
source_attempt_id=(
|
|
176
|
+
f"attempt_warpx_confirmation_{attempt.resolution_id}_seed_{attempt.seed}_v1"
|
|
177
|
+
),
|
|
178
|
+
role=EvidenceRole.CONFIRMATION,
|
|
179
|
+
eligible=attempt.confirmed,
|
|
180
|
+
eligibility_reason=(
|
|
181
|
+
"fresh qualified WarpX pair passed every preregistered confirmation gate"
|
|
182
|
+
if attempt.confirmed
|
|
183
|
+
else "WarpX pair failed one or more preregistered confirmation gates"
|
|
184
|
+
),
|
|
185
|
+
observable_values={
|
|
186
|
+
"maxwellian_growth_rate": float(observables["maxwellian_growth_rate"]),
|
|
187
|
+
"two_stream_growth_rate": float(observables["two_stream_growth_rate"]),
|
|
188
|
+
"outcome_separation": float(observables["outcome_separation"]),
|
|
189
|
+
},
|
|
190
|
+
independence_group=group,
|
|
191
|
+
artifact_hashes=attempt.normalized_result.artifact_hashes,
|
|
192
|
+
)
|
|
193
|
+
)
|
|
194
|
+
for failure in report.failures:
|
|
195
|
+
identity = hashlib.sha256(
|
|
196
|
+
f"{context.campaign_id}:{failure.resolution_id}:{failure.seed}".encode()
|
|
197
|
+
).hexdigest()[:20]
|
|
198
|
+
evidence.append(
|
|
199
|
+
RunEvidence(
|
|
200
|
+
id=f"evidence_{identity}",
|
|
201
|
+
source_attempt_id=f"attempt_{failure.experiment_id.removeprefix('experiment_')}",
|
|
202
|
+
role=EvidenceRole.CONFIRMATION,
|
|
203
|
+
eligible=False,
|
|
204
|
+
eligibility_reason=(
|
|
205
|
+
f"{failure.kind.value} execution failure is not physical evidence: "
|
|
206
|
+
f"{failure.detail}"
|
|
207
|
+
),
|
|
208
|
+
observable_values={},
|
|
209
|
+
independence_group=group,
|
|
210
|
+
)
|
|
211
|
+
)
|
|
212
|
+
completed_event = next(
|
|
213
|
+
event
|
|
214
|
+
for event in reversed(context.ledger.load(context.campaign_id))
|
|
215
|
+
if event.event_type == "qualified_warpx_confirmation_completed"
|
|
216
|
+
)
|
|
217
|
+
refuted = report.disposition is WarpXConfirmationDisposition.CONFIRMED
|
|
218
|
+
evidence_ids = tuple(item.id for item in evidence)
|
|
219
|
+
claim = Claim(
|
|
220
|
+
id=f"claim_{hashlib.sha256(context.campaign_id.encode()).hexdigest()[:20]}",
|
|
221
|
+
hypothesis_id=hypothesis.id,
|
|
222
|
+
statement=(
|
|
223
|
+
"The low-order-moment predictive-sufficiency hypothesis is independently "
|
|
224
|
+
"refuted by the qualified WarpX confirmation matrix."
|
|
225
|
+
if refuted
|
|
226
|
+
else "The qualified WarpX confirmation matrix did not resolve the hypothesis."
|
|
227
|
+
),
|
|
228
|
+
disposition=(
|
|
229
|
+
ClaimDisposition.REFUTED_WITHIN_MODEL if refuted else ClaimDisposition.UNRESOLVED
|
|
230
|
+
),
|
|
231
|
+
scope=(
|
|
232
|
+
"the registered one-dimensional electrostatic WarpX/PICMI model at "
|
|
233
|
+
"k lambda_D = 0.5 and the frozen qualified parameter envelope"
|
|
234
|
+
),
|
|
235
|
+
evidence_ids=evidence_ids,
|
|
236
|
+
limitations=(
|
|
237
|
+
"all confirmation attempts share one WarpX implementation and estimator family",
|
|
238
|
+
"the result does not establish sufficiency or insufficiency outside the "
|
|
239
|
+
"qualified scope",
|
|
240
|
+
"finite seeds and two resolutions bound, but do not eliminate, sampling uncertainty",
|
|
241
|
+
),
|
|
242
|
+
created_at=datetime.fromisoformat(completed_event.created_at),
|
|
243
|
+
)
|
|
244
|
+
return tuple(evidence), claim
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
class QualifiedWarpXConfirmationActionHandler:
|
|
248
|
+
def __init__(
|
|
249
|
+
self,
|
|
250
|
+
*,
|
|
251
|
+
instrument: QualifiedWarpXInstrument,
|
|
252
|
+
adapter: SimulatorAdapter,
|
|
253
|
+
crash_at: WarpXConfirmationCrashPoint | None = None,
|
|
254
|
+
crash_ordinal: int | None = None,
|
|
255
|
+
outbox_crash_at: OutboxCrashPoint | None = None,
|
|
256
|
+
) -> None:
|
|
257
|
+
self.instrument = instrument
|
|
258
|
+
self.adapter = adapter
|
|
259
|
+
self.crash_at = crash_at
|
|
260
|
+
self.crash_ordinal = crash_ordinal
|
|
261
|
+
self.outbox_crash_at = outbox_crash_at
|
|
262
|
+
|
|
263
|
+
def execute(
|
|
264
|
+
self,
|
|
265
|
+
context: ActionContext,
|
|
266
|
+
action: CampaignAction,
|
|
267
|
+
dependencies: dict[str, ActionExecution],
|
|
268
|
+
) -> ActionExecution:
|
|
269
|
+
source_action_id = action.payload.get("source_action_id")
|
|
270
|
+
if not isinstance(source_action_id, str) or source_action_id not in dependencies:
|
|
271
|
+
raise ActionExecutionError(
|
|
272
|
+
ActionFailureKind.SPECIFICATION,
|
|
273
|
+
"WarpX confirmation source_action_id must name a completed dependency",
|
|
274
|
+
)
|
|
275
|
+
try:
|
|
276
|
+
if action.payload["instrument_id"] != self.instrument.id:
|
|
277
|
+
raise ValueError("action names a different registered instrument")
|
|
278
|
+
if action.payload["qualification_hash"] != self.instrument.qualification_hash:
|
|
279
|
+
raise ValueError("action qualification hash differs from the instrument")
|
|
280
|
+
search_report = BlindedSearchReport.model_validate(
|
|
281
|
+
dependencies[source_action_id].output["search_report"]
|
|
282
|
+
)
|
|
283
|
+
design = WarpXConfirmationDesign.model_validate(action.payload["design"])
|
|
284
|
+
if search_report.confirmation_candidate is None:
|
|
285
|
+
raise ValueError("analytic discovery did not freeze a confirmation candidate")
|
|
286
|
+
if search_report.confirmation_candidate != design.physical.candidate:
|
|
287
|
+
raise ValueError(
|
|
288
|
+
"frozen analytic candidate is outside the exact qualified WarpX scope"
|
|
289
|
+
)
|
|
290
|
+
expected_units = 2 * len(design.seeds) * len(design.resolutions)
|
|
291
|
+
if abs(action.budget_units - expected_units) > 1e-12:
|
|
292
|
+
raise ValueError(
|
|
293
|
+
"WarpX budget units must equal the paired case executions in the matrix"
|
|
294
|
+
)
|
|
295
|
+
report = WarpXConfirmationRunner(
|
|
296
|
+
campaign_id=context.campaign_id,
|
|
297
|
+
ledger=context.ledger,
|
|
298
|
+
instrument=self.instrument,
|
|
299
|
+
adapter=self.adapter,
|
|
300
|
+
design=design,
|
|
301
|
+
control=context.control,
|
|
302
|
+
crash_at=self.crash_at,
|
|
303
|
+
crash_ordinal=self.crash_ordinal,
|
|
304
|
+
outbox_crash_at=self.outbox_crash_at,
|
|
305
|
+
).run()
|
|
306
|
+
except (KeyError, TypeError, ValueError, ValidationError) as error:
|
|
307
|
+
raise ActionExecutionError(
|
|
308
|
+
ActionFailureKind.SPECIFICATION,
|
|
309
|
+
f"invalid qualified WarpX confirmation action: {error}",
|
|
310
|
+
) from error
|
|
311
|
+
except InjectedWarpXConfirmationCrash:
|
|
312
|
+
raise
|
|
313
|
+
evidence, claim = _warpx_evidence_and_claim(context, report)
|
|
314
|
+
output: dict[str, object] = {
|
|
315
|
+
"instrument_id": self.instrument.id,
|
|
316
|
+
"qualification_hash": self.instrument.qualification_hash,
|
|
317
|
+
"confirmation_report": report.model_dump(mode="json"),
|
|
318
|
+
"evidence": [item.model_dump(mode="json") for item in evidence],
|
|
319
|
+
"claim": claim.model_dump(mode="json"),
|
|
320
|
+
}
|
|
321
|
+
return ActionExecution(
|
|
322
|
+
action_id=action.id,
|
|
323
|
+
evidence_eligible=(
|
|
324
|
+
report.disposition is WarpXConfirmationDisposition.CONFIRMED
|
|
325
|
+
and bool(evidence)
|
|
326
|
+
and all(item.eligible for item in evidence)
|
|
327
|
+
),
|
|
328
|
+
output=output,
|
|
329
|
+
output_hash=_hash_output(output),
|
|
330
|
+
artifact_hashes=tuple(
|
|
331
|
+
artifact for item in evidence for artifact in item.artifact_hashes
|
|
332
|
+
),
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def blinded_campaign_handlers() -> dict[str, ActionHandler]:
|
|
337
|
+
return {
|
|
338
|
+
BLINDED_SEARCH_ACTION: BlindedSearchActionHandler(),
|
|
339
|
+
PIC_CONFIRMATION_ACTION: PICConfirmationActionHandler(),
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def qualified_warpx_campaign_handlers(
|
|
344
|
+
*,
|
|
345
|
+
instrument: QualifiedWarpXInstrument,
|
|
346
|
+
adapter: SimulatorAdapter,
|
|
347
|
+
crash_at: WarpXConfirmationCrashPoint | None = None,
|
|
348
|
+
crash_ordinal: int | None = None,
|
|
349
|
+
outbox_crash_at: OutboxCrashPoint | None = None,
|
|
350
|
+
) -> dict[str, ActionHandler]:
|
|
351
|
+
return {
|
|
352
|
+
BLINDED_SEARCH_ACTION: BlindedSearchActionHandler(),
|
|
353
|
+
QUALIFIED_WARPX_CONFIRMATION_ACTION: QualifiedWarpXConfirmationActionHandler(
|
|
354
|
+
instrument=instrument,
|
|
355
|
+
adapter=adapter,
|
|
356
|
+
crash_at=crash_at,
|
|
357
|
+
crash_ordinal=crash_ordinal,
|
|
358
|
+
outbox_crash_at=outbox_crash_at,
|
|
359
|
+
),
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def build_qualified_warpx_campaign_graph(
|
|
364
|
+
request: BlindedSearchRequest,
|
|
365
|
+
ai_strategy: SearchStrategy,
|
|
366
|
+
instrument: QualifiedWarpXInstrument,
|
|
367
|
+
) -> CampaignActionGraph:
|
|
368
|
+
strategies = (ai_strategy, *baseline_strategies(request))
|
|
369
|
+
analytic_evaluations = request.evaluations_per_method * len(request.comparison_methods)
|
|
370
|
+
design = default_warpx_confirmation_design(instrument.qualification)
|
|
371
|
+
warpx_case_executions = 2 * len(design.seeds) * len(design.resolutions)
|
|
372
|
+
discovery = CampaignAction(
|
|
373
|
+
id="action_blinded_analytic_discovery_v1",
|
|
374
|
+
action_type=BLINDED_SEARCH_ACTION,
|
|
375
|
+
purpose="select and analytically evaluate a blinded matched-moment candidate batch",
|
|
376
|
+
evidence_role=EvidenceRole.DISCOVERY,
|
|
377
|
+
independence_group="analytic_gaussian_mixture_dispersion_solver_v1",
|
|
378
|
+
origin=ActionOrigin.MIXED,
|
|
379
|
+
budget_units=float(analytic_evaluations),
|
|
380
|
+
payload={
|
|
381
|
+
"request": request.model_dump(mode="json"),
|
|
382
|
+
"strategies": [strategy.model_dump(mode="json") for strategy in strategies],
|
|
383
|
+
},
|
|
384
|
+
)
|
|
385
|
+
confirmation = CampaignAction(
|
|
386
|
+
id="action_qualified_warpx_confirmation_v1",
|
|
387
|
+
action_type=QUALIFIED_WARPX_CONFIRMATION_ACTION,
|
|
388
|
+
purpose=(
|
|
389
|
+
"test the frozen analytic witness using the registered qualified WarpX "
|
|
390
|
+
"instrument on fresh seeds and two resolutions"
|
|
391
|
+
),
|
|
392
|
+
dependencies=(discovery.id,),
|
|
393
|
+
evidence_role=EvidenceRole.CONFIRMATION,
|
|
394
|
+
independence_group=f"qualified_warpx_{instrument.qualification_hash[:16]}",
|
|
395
|
+
origin=ActionOrigin.DETERMINISTIC,
|
|
396
|
+
budget_units=float(warpx_case_executions),
|
|
397
|
+
payload={
|
|
398
|
+
"source_action_id": discovery.id,
|
|
399
|
+
"instrument_id": instrument.id,
|
|
400
|
+
"qualification_hash": instrument.qualification_hash,
|
|
401
|
+
"design": design.model_dump(mode="json"),
|
|
402
|
+
},
|
|
403
|
+
)
|
|
404
|
+
return CampaignActionGraph(
|
|
405
|
+
id="action_graph_blinded_qualified_warpx_confirmation_v1",
|
|
406
|
+
actions=(discovery, confirmation),
|
|
407
|
+
budget=CampaignBudget(
|
|
408
|
+
total_units=float(analytic_evaluations + warpx_case_executions),
|
|
409
|
+
unit_name="physics_case_evaluation",
|
|
410
|
+
),
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def build_blinded_multi_action_graph(
|
|
415
|
+
request: BlindedSearchRequest,
|
|
416
|
+
ai_strategy: SearchStrategy,
|
|
417
|
+
) -> CampaignActionGraph:
|
|
418
|
+
strategies = (ai_strategy, *baseline_strategies(request))
|
|
419
|
+
analytic_evaluations = request.evaluations_per_method * len(request.comparison_methods)
|
|
420
|
+
seeds = (1, 7, 19)
|
|
421
|
+
velocity_beams = (192, 384)
|
|
422
|
+
pic_case_executions = 2 * len(seeds) * len(velocity_beams)
|
|
423
|
+
discovery = CampaignAction(
|
|
424
|
+
id="action_blinded_analytic_discovery_v1",
|
|
425
|
+
action_type=BLINDED_SEARCH_ACTION,
|
|
426
|
+
purpose="select and analytically evaluate a blinded matched-moment candidate batch",
|
|
427
|
+
evidence_role=EvidenceRole.DISCOVERY,
|
|
428
|
+
independence_group="analytic_gaussian_mixture_dispersion_solver_v1",
|
|
429
|
+
origin=ActionOrigin.MIXED,
|
|
430
|
+
budget_units=float(analytic_evaluations),
|
|
431
|
+
payload={
|
|
432
|
+
"request": request.model_dump(mode="json"),
|
|
433
|
+
"strategies": [strategy.model_dump(mode="json") for strategy in strategies],
|
|
434
|
+
},
|
|
435
|
+
)
|
|
436
|
+
confirmation = CampaignAction(
|
|
437
|
+
id="action_frozen_pic_confirmation_v1",
|
|
438
|
+
action_type=PIC_CONFIRMATION_ACTION,
|
|
439
|
+
purpose="confirm the frozen AI witness with fresh independent PIC attempts",
|
|
440
|
+
dependencies=(discovery.id,),
|
|
441
|
+
evidence_role=EvidenceRole.CONFIRMATION,
|
|
442
|
+
independence_group="electrostatic_pic_inverse_cdf_quiet_start_v1",
|
|
443
|
+
origin=ActionOrigin.DETERMINISTIC,
|
|
444
|
+
budget_units=float(pic_case_executions),
|
|
445
|
+
payload={
|
|
446
|
+
"source_action_id": discovery.id,
|
|
447
|
+
"seeds": list(seeds),
|
|
448
|
+
"velocity_beams": list(velocity_beams),
|
|
449
|
+
"base_config": PICNumericalConfig().model_dump(mode="json"),
|
|
450
|
+
},
|
|
451
|
+
)
|
|
452
|
+
return CampaignActionGraph(
|
|
453
|
+
id="action_graph_blinded_discovery_confirmation_v1",
|
|
454
|
+
actions=(discovery, confirmation),
|
|
455
|
+
budget=CampaignBudget(
|
|
456
|
+
total_units=float(analytic_evaluations + pic_case_executions),
|
|
457
|
+
unit_name="physics_case_evaluation",
|
|
458
|
+
),
|
|
459
|
+
)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""Simulator-neutral execution contract."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from enum import StrEnum
|
|
6
|
+
from typing import Any, Protocol, runtime_checkable
|
|
7
|
+
|
|
8
|
+
from pydantic import Field
|
|
9
|
+
|
|
10
|
+
from ..models import ExperimentSpec, StrictModel
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class JobState(StrEnum):
|
|
14
|
+
QUEUED = "queued"
|
|
15
|
+
RUNNING = "running"
|
|
16
|
+
COMPLETED = "completed"
|
|
17
|
+
FAILED = "failed"
|
|
18
|
+
CANCELLED = "cancelled"
|
|
19
|
+
UNKNOWN = "unknown"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class CapabilityManifest(StrictModel):
|
|
23
|
+
adapter_name: str
|
|
24
|
+
adapter_version: str
|
|
25
|
+
supported_actions: tuple[str, ...]
|
|
26
|
+
supported_models: tuple[str, ...]
|
|
27
|
+
supported_diagnostics: tuple[str, ...]
|
|
28
|
+
supported_coordinates: tuple[str, ...]
|
|
29
|
+
supported_observable_kinds: tuple[str, ...]
|
|
30
|
+
supports_checkpoint: bool
|
|
31
|
+
deterministic: bool
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ValidationReport(StrictModel):
|
|
35
|
+
valid: bool
|
|
36
|
+
errors: tuple[str, ...] = ()
|
|
37
|
+
warnings: tuple[str, ...] = ()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class CostEstimate(StrictModel):
|
|
41
|
+
compute_units: float = Field(ge=0)
|
|
42
|
+
wall_seconds: float = Field(ge=0)
|
|
43
|
+
storage_bytes: int = Field(ge=0)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class RunPackage(StrictModel):
|
|
47
|
+
experiment_id: str
|
|
48
|
+
adapter_name: str
|
|
49
|
+
payload: dict[str, Any]
|
|
50
|
+
package_hash: str
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class JobReference(StrictModel):
|
|
54
|
+
job_id: str
|
|
55
|
+
experiment_id: str
|
|
56
|
+
idempotency_key: str
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class JobStatus(StrictModel):
|
|
60
|
+
job_id: str
|
|
61
|
+
state: JobState
|
|
62
|
+
detail: str = ""
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class RawResult(StrictModel):
|
|
66
|
+
job_id: str
|
|
67
|
+
payload: dict[str, Any]
|
|
68
|
+
artifact_hashes: tuple[str, ...] = ()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class NormalizedResult(StrictModel):
|
|
72
|
+
experiment_id: str
|
|
73
|
+
observables: dict[str, float | bool | str]
|
|
74
|
+
diagnostics: dict[str, Any]
|
|
75
|
+
artifact_hashes: tuple[str, ...] = ()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@runtime_checkable
|
|
79
|
+
class SimulatorAdapter(Protocol):
|
|
80
|
+
def capabilities(self) -> CapabilityManifest: ...
|
|
81
|
+
|
|
82
|
+
def validate(self, experiment: ExperimentSpec) -> ValidationReport: ...
|
|
83
|
+
|
|
84
|
+
def estimate_cost(self, experiment: ExperimentSpec) -> CostEstimate: ...
|
|
85
|
+
|
|
86
|
+
def compile_input(self, experiment: ExperimentSpec) -> RunPackage: ...
|
|
87
|
+
|
|
88
|
+
def submit(self, run: RunPackage, *, idempotency_key: str) -> JobReference: ...
|
|
89
|
+
|
|
90
|
+
def monitor(self, job: JobReference) -> JobStatus: ...
|
|
91
|
+
|
|
92
|
+
def retrieve(self, job: JobReference) -> RawResult: ...
|
|
93
|
+
|
|
94
|
+
def normalize(self, result: RawResult) -> NormalizedResult: ...
|
|
95
|
+
|
|
96
|
+
def cancel(self, job: JobReference) -> JobStatus: ...
|