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.
Files changed (75) hide show
  1. conjecture_solver/__init__.py +10 -0
  2. conjecture_solver/__main__.py +4 -0
  3. conjecture_solver/action_handlers.py +459 -0
  4. conjecture_solver/adapters/__init__.py +7 -0
  5. conjecture_solver/adapters/base.py +96 -0
  6. conjecture_solver/adapters/fake.py +155 -0
  7. conjecture_solver/adapters/pic.py +196 -0
  8. conjecture_solver/adapters/warpx.py +1375 -0
  9. conjecture_solver/autonomous_research.py +1071 -0
  10. conjecture_solver/benchmarks/__init__.py +6 -0
  11. conjecture_solver/benchmarks/electrostatic_pic.py +547 -0
  12. conjecture_solver/benchmarks/kinetic_sufficiency.py +318 -0
  13. conjecture_solver/builtin_skills/warpx/SKILL.md +151 -0
  14. conjecture_solver/builtin_skills/warpx/agents/openai.yaml +4 -0
  15. conjecture_solver/builtin_skills/warpx/examples/implicit_em_smoke.py +74 -0
  16. conjecture_solver/builtin_skills/warpx/examples/minimal_smoke.py +54 -0
  17. conjecture_solver/builtin_skills/warpx/examples/openpmd_field_smoke.py +87 -0
  18. conjecture_solver/builtin_skills/warpx/manifest.json +11 -0
  19. conjecture_solver/builtin_skills/warpx/references/2d-xz-commissioning.md +145 -0
  20. conjecture_solver/builtin_skills/warpx/references/cpu-launch-tuning.md +54 -0
  21. conjecture_solver/builtin_skills/warpx/references/diagnostics.md +139 -0
  22. conjecture_solver/builtin_skills/warpx/references/gpu-launch-tuning.md +79 -0
  23. conjecture_solver/builtin_skills/warpx/references/local-cuda-deployment.md +87 -0
  24. conjecture_solver/builtin_skills/warpx/references/numerical-risks.md +32 -0
  25. conjecture_solver/builtin_skills/warpx/references/picmi-interface.md +118 -0
  26. conjecture_solver/builtin_skills/warpx/references/resource-scaling.md +24 -0
  27. conjecture_solver/builtin_skills/warpx/references/time-integration.md +58 -0
  28. conjecture_solver/builtin_skills/warpx/scripts/benchmark_cpu_threads.py +207 -0
  29. conjecture_solver/builtin_skills/warpx/scripts/benchmark_gpu.py +242 -0
  30. conjecture_solver/builtin_skills/warpx/scripts/bootstrap_local_cuda.sh +82 -0
  31. conjecture_solver/builtin_skills/warpx/scripts/probe_local_cuda.py +110 -0
  32. conjecture_solver/builtin_skills/warpx/scripts/reduced_energy_budget.py +195 -0
  33. conjecture_solver/builtin_skills/warpx/scripts/run_local_cuda.sh +39 -0
  34. conjecture_solver/campaign.py +406 -0
  35. conjecture_solver/cli.py +963 -0
  36. conjecture_solver/confirmation.py +287 -0
  37. conjecture_solver/control.py +234 -0
  38. conjecture_solver/deployment.py +874 -0
  39. conjecture_solver/discovery.py +68 -0
  40. conjecture_solver/domains/__init__.py +40 -0
  41. conjecture_solver/domains/base.py +157 -0
  42. conjecture_solver/domains/kinetic_sufficiency.py +521 -0
  43. conjecture_solver/ledger.py +238 -0
  44. conjecture_solver/lifecycle.py +131 -0
  45. conjecture_solver/literature.py +494 -0
  46. conjecture_solver/llm.py +228 -0
  47. conjecture_solver/models.py +279 -0
  48. conjecture_solver/mvp_agent.py +3212 -0
  49. conjecture_solver/mvp_claims.py +1279 -0
  50. conjecture_solver/mvp_control.py +322 -0
  51. conjecture_solver/mvp_guidance.py +172 -0
  52. conjecture_solver/mvp_launch.py +1296 -0
  53. conjecture_solver/mvp_monitor.py +1487 -0
  54. conjecture_solver/mvp_skills.py +538 -0
  55. conjecture_solver/orchestration.py +578 -0
  56. conjecture_solver/outbox.py +526 -0
  57. conjecture_solver/parameters.py +394 -0
  58. conjecture_solver/proposals.py +427 -0
  59. conjecture_solver/research_tools.py +278 -0
  60. conjecture_solver/schema_export.py +246 -0
  61. conjecture_solver/search.py +1047 -0
  62. conjecture_solver/semantics.py +146 -0
  63. conjecture_solver/tui/__init__.py +7 -0
  64. conjecture_solver/tui/app.py +202 -0
  65. conjecture_solver/tui/claim_views.py +213 -0
  66. conjecture_solver/tui/screens.py +1014 -0
  67. conjecture_solver/warpx_analysis.py +184 -0
  68. conjecture_solver/warpx_campaign.py +148 -0
  69. conjecture_solver/warpx_confirmation.py +617 -0
  70. simjecture-0.1.0.dist-info/METADATA +400 -0
  71. simjecture-0.1.0.dist-info/RECORD +75 -0
  72. simjecture-0.1.0.dist-info/WHEEL +4 -0
  73. simjecture-0.1.0.dist-info/entry_points.txt +4 -0
  74. simjecture-0.1.0.dist-info/licenses/LICENSE +202 -0
  75. simjecture-0.1.0.dist-info/licenses/NOTICE +6 -0
@@ -0,0 +1,155 @@
1
+ """Deterministic in-process adapter for the first physics benchmark."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+
8
+ from ..benchmarks.kinetic_sufficiency import run_kinetic_sufficiency_benchmark
9
+ from ..models import ExperimentSpec
10
+ from .base import (
11
+ CapabilityManifest,
12
+ CostEstimate,
13
+ JobReference,
14
+ JobState,
15
+ JobStatus,
16
+ NormalizedResult,
17
+ RawResult,
18
+ RunPackage,
19
+ ValidationReport,
20
+ )
21
+
22
+
23
+ class DeterministicKineticScheduler:
24
+ """External-system stand-in whose state can outlive an adapter process."""
25
+
26
+ def __init__(self) -> None:
27
+ self.jobs: dict[str, RawResult] = {}
28
+ self.idempotency: dict[str, str] = {}
29
+
30
+
31
+ class DeterministicKineticAdapter:
32
+ def __init__(self, scheduler: DeterministicKineticScheduler | None = None) -> None:
33
+ self._scheduler = scheduler or DeterministicKineticScheduler()
34
+
35
+ @property
36
+ def submitted_job_count(self) -> int:
37
+ """Count distinct external jobs, excluding idempotent reattachments."""
38
+
39
+ return len(self._scheduler.jobs)
40
+
41
+ def capabilities(self) -> CapabilityManifest:
42
+ return CapabilityManifest(
43
+ adapter_name="deterministic_kinetic",
44
+ adapter_version="0.1.0",
45
+ supported_actions=("kinetic_sufficiency",),
46
+ supported_models=("linearized_1d_electrostatic_vlasov_poisson",),
47
+ supported_diagnostics=("dominant_linear_mode", "distribution_moments"),
48
+ supported_coordinates=("density", "mean_velocity", "variance"),
49
+ supported_observable_kinds=("dominant_linear_growth_rate",),
50
+ supports_checkpoint=False,
51
+ deterministic=True,
52
+ )
53
+
54
+ def validate(self, experiment: ExperimentSpec) -> ValidationReport:
55
+ errors: list[str] = []
56
+ if experiment.action_type != "kinetic_sufficiency":
57
+ errors.append("unsupported action_type")
58
+ if experiment.physical_parameters.get("wavenumber") != 0.5:
59
+ errors.append("the planted benchmark is preregistered at wavenumber 0.5")
60
+ required = {"dominant_linear_mode", "distribution_moments"}
61
+ missing = required - set(experiment.required_diagnostics)
62
+ if missing:
63
+ errors.append(f"missing diagnostics: {sorted(missing)}")
64
+ return ValidationReport(valid=not errors, errors=tuple(errors))
65
+
66
+ def estimate_cost(self, experiment: ExperimentSpec) -> CostEstimate:
67
+ report = self.validate(experiment)
68
+ if not report.valid:
69
+ raise ValueError("; ".join(report.errors))
70
+ return CostEstimate(compute_units=0.001, wall_seconds=1.0, storage_bytes=20_000)
71
+
72
+ def compile_input(self, experiment: ExperimentSpec) -> RunPackage:
73
+ report = self.validate(experiment)
74
+ if not report.valid:
75
+ raise ValueError("; ".join(report.errors))
76
+ payload = {
77
+ "benchmark": "kinetic_sufficiency",
78
+ "wavenumber": experiment.physical_parameters["wavenumber"],
79
+ }
80
+ canonical = json.dumps(payload, sort_keys=True, separators=(",", ":"))
81
+ return RunPackage(
82
+ experiment_id=experiment.id,
83
+ adapter_name="deterministic_kinetic",
84
+ payload=payload,
85
+ package_hash=hashlib.sha256(canonical.encode()).hexdigest(),
86
+ )
87
+
88
+ def submit(self, run: RunPackage, *, idempotency_key: str) -> JobReference:
89
+ if idempotency_key in self._scheduler.idempotency:
90
+ return JobReference(
91
+ job_id=self._scheduler.idempotency[idempotency_key],
92
+ experiment_id=run.experiment_id,
93
+ idempotency_key=idempotency_key,
94
+ )
95
+ job_identity = hashlib.sha256(
96
+ f"{run.package_hash}:{idempotency_key}".encode()
97
+ ).hexdigest()
98
+ job_id = f"fake-{job_identity[:16]}"
99
+ benchmark = run_kinetic_sufficiency_benchmark(
100
+ wavenumber=float(run.payload["wavenumber"])
101
+ )
102
+ result_payload = {
103
+ "experiment_id": run.experiment_id,
104
+ "benchmark": benchmark.model_dump(mode="json"),
105
+ }
106
+ canonical_result = json.dumps(result_payload, sort_keys=True, separators=(",", ":"))
107
+ self._scheduler.jobs[job_id] = RawResult(
108
+ job_id=job_id,
109
+ payload=result_payload,
110
+ artifact_hashes=(hashlib.sha256(canonical_result.encode()).hexdigest(),),
111
+ )
112
+ self._scheduler.idempotency[idempotency_key] = job_id
113
+ return JobReference(
114
+ job_id=job_id,
115
+ experiment_id=run.experiment_id,
116
+ idempotency_key=idempotency_key,
117
+ )
118
+
119
+ def monitor(self, job: JobReference) -> JobStatus:
120
+ state = JobState.COMPLETED if job.job_id in self._scheduler.jobs else JobState.UNKNOWN
121
+ return JobStatus(job_id=job.job_id, state=state)
122
+
123
+ def retrieve(self, job: JobReference) -> RawResult:
124
+ try:
125
+ return self._scheduler.jobs[job.job_id]
126
+ except KeyError as error:
127
+ raise LookupError(f"unknown fake job {job.job_id}") from error
128
+
129
+ def normalize(self, result: RawResult) -> NormalizedResult:
130
+ payload = result.payload
131
+ benchmark = payload["benchmark"]
132
+ return NormalizedResult(
133
+ experiment_id=str(payload["experiment_id"]),
134
+ observables={
135
+ "maxwellian_growth_rate": float(
136
+ benchmark["maxwellian"]["mode"]["growth_rate"]
137
+ ),
138
+ "two_stream_growth_rate": float(
139
+ benchmark["two_stream"]["mode"]["growth_rate"]
140
+ ),
141
+ "moments_match": bool(benchmark["moments_match"]),
142
+ "hypothesis_falsified": bool(benchmark["witness"]["falsifies"]),
143
+ },
144
+ diagnostics=benchmark,
145
+ artifact_hashes=result.artifact_hashes,
146
+ )
147
+
148
+ def cancel(self, job: JobReference) -> JobStatus:
149
+ if job.job_id in self._scheduler.jobs:
150
+ return JobStatus(
151
+ job_id=job.job_id,
152
+ state=JobState.COMPLETED,
153
+ detail="deterministic in-process jobs complete atomically",
154
+ )
155
+ return JobStatus(job_id=job.job_id, state=JobState.UNKNOWN)
@@ -0,0 +1,196 @@
1
+ """In-process adapter for the independent electrostatic PIC benchmark."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+
8
+ from ..benchmarks.electrostatic_pic import (
9
+ PICConfig,
10
+ PICSufficiencyResult,
11
+ run_pic_sufficiency_benchmark,
12
+ )
13
+ from ..models import ExperimentSpec
14
+ from .base import (
15
+ CapabilityManifest,
16
+ CostEstimate,
17
+ JobReference,
18
+ JobState,
19
+ JobStatus,
20
+ NormalizedResult,
21
+ RawResult,
22
+ RunPackage,
23
+ ValidationReport,
24
+ )
25
+
26
+
27
+ class DeterministicPICScheduler:
28
+ """External-scheduler stand-in shared across restarted adapter instances."""
29
+
30
+ def __init__(self) -> None:
31
+ self.jobs: dict[str, RawResult] = {}
32
+ self.idempotency: dict[str, str] = {}
33
+
34
+
35
+ class ElectrostaticPICAdapter:
36
+ _NUMERICAL_KEYS = {
37
+ "grid_cells",
38
+ "velocity_beams",
39
+ "particles_per_beam",
40
+ "time_step",
41
+ "final_time",
42
+ "diagnostic_interval",
43
+ "seed",
44
+ }
45
+ _PHYSICAL_KEYS = {"wavenumber", "perturbation_amplitude", "stream_drift"}
46
+
47
+ def __init__(self, scheduler: DeterministicPICScheduler | None = None) -> None:
48
+ self._scheduler = scheduler or DeterministicPICScheduler()
49
+
50
+ @property
51
+ def submitted_job_count(self) -> int:
52
+ return len(self._scheduler.jobs)
53
+
54
+ def capabilities(self) -> CapabilityManifest:
55
+ return CapabilityManifest(
56
+ adapter_name="electrostatic_pic",
57
+ adapter_version="0.1.0",
58
+ supported_actions=("kinetic_sufficiency",),
59
+ supported_models=("electrostatic_1d_pic_vlasov_poisson",),
60
+ supported_diagnostics=(
61
+ "dominant_linear_mode",
62
+ "distribution_moments",
63
+ "energy_conservation",
64
+ "gauss_residual",
65
+ ),
66
+ supported_coordinates=("density", "mean_velocity", "variance"),
67
+ supported_observable_kinds=("effective_fundamental_growth_rate",),
68
+ supports_checkpoint=False,
69
+ deterministic=True,
70
+ )
71
+
72
+ def _config(self, experiment: ExperimentSpec) -> PICConfig:
73
+ values = {
74
+ **experiment.physical_parameters,
75
+ **experiment.numerical_parameters,
76
+ }
77
+ return PICConfig.model_validate(values)
78
+
79
+ def validate(self, experiment: ExperimentSpec) -> ValidationReport:
80
+ errors: list[str] = []
81
+ warnings: list[str] = []
82
+ if experiment.action_type != "kinetic_sufficiency":
83
+ errors.append("unsupported action_type")
84
+ unknown_physical = set(experiment.physical_parameters) - self._PHYSICAL_KEYS
85
+ unknown_numerical = set(experiment.numerical_parameters) - self._NUMERICAL_KEYS
86
+ if unknown_physical:
87
+ errors.append(f"unknown physical parameters: {sorted(unknown_physical)}")
88
+ if unknown_numerical:
89
+ errors.append(f"unknown numerical parameters: {sorted(unknown_numerical)}")
90
+ missing_diagnostics = set(experiment.required_diagnostics) - set(
91
+ self.capabilities().supported_diagnostics
92
+ )
93
+ if missing_diagnostics:
94
+ errors.append(f"unsupported diagnostics: {sorted(missing_diagnostics)}")
95
+ if not errors:
96
+ try:
97
+ config = self._config(experiment)
98
+ except ValueError as error:
99
+ errors.append(f"invalid PIC configuration: {error}")
100
+ else:
101
+ particle_count = config.velocity_beams * config.particles_per_beam
102
+ if particle_count > 1_000_000:
103
+ warnings.append("PIC request contains more than one million particles")
104
+ return ValidationReport(valid=not errors, errors=tuple(errors), warnings=tuple(warnings))
105
+
106
+ def estimate_cost(self, experiment: ExperimentSpec) -> CostEstimate:
107
+ report = self.validate(experiment)
108
+ if not report.valid:
109
+ raise ValueError("; ".join(report.errors))
110
+ config = self._config(experiment)
111
+ particle_count = config.velocity_beams * config.particles_per_beam
112
+ step_count = round(config.final_time / config.time_step)
113
+ work = 2 * particle_count * step_count
114
+ return CostEstimate(
115
+ compute_units=work / 100_000_000,
116
+ wall_seconds=work / 20_000_000,
117
+ storage_bytes=(round(config.final_time / config.diagnostic_interval) + 1) * 128,
118
+ )
119
+
120
+ def compile_input(self, experiment: ExperimentSpec) -> RunPackage:
121
+ report = self.validate(experiment)
122
+ if not report.valid:
123
+ raise ValueError("; ".join(report.errors))
124
+ payload = {
125
+ "benchmark": "electrostatic_pic_sufficiency",
126
+ "config": self._config(experiment).model_dump(mode="json"),
127
+ }
128
+ canonical = json.dumps(payload, sort_keys=True, separators=(",", ":"))
129
+ return RunPackage(
130
+ experiment_id=experiment.id,
131
+ adapter_name="electrostatic_pic",
132
+ payload=payload,
133
+ package_hash=hashlib.sha256(canonical.encode()).hexdigest(),
134
+ )
135
+
136
+ def submit(self, run: RunPackage, *, idempotency_key: str) -> JobReference:
137
+ if idempotency_key in self._scheduler.idempotency:
138
+ return JobReference(
139
+ job_id=self._scheduler.idempotency[idempotency_key],
140
+ experiment_id=run.experiment_id,
141
+ idempotency_key=idempotency_key,
142
+ )
143
+ identity = hashlib.sha256(f"{run.package_hash}:{idempotency_key}".encode()).hexdigest()
144
+ job_id = f"pic-{identity[:16]}"
145
+ result = run_pic_sufficiency_benchmark(PICConfig.model_validate(run.payload["config"]))
146
+ result_payload = {
147
+ "experiment_id": run.experiment_id,
148
+ "benchmark": result.model_dump(mode="json"),
149
+ }
150
+ canonical = json.dumps(result_payload, sort_keys=True, separators=(",", ":"))
151
+ self._scheduler.jobs[job_id] = RawResult(
152
+ job_id=job_id,
153
+ payload=result_payload,
154
+ artifact_hashes=(hashlib.sha256(canonical.encode()).hexdigest(),),
155
+ )
156
+ self._scheduler.idempotency[idempotency_key] = job_id
157
+ return JobReference(
158
+ job_id=job_id,
159
+ experiment_id=run.experiment_id,
160
+ idempotency_key=idempotency_key,
161
+ )
162
+
163
+ def monitor(self, job: JobReference) -> JobStatus:
164
+ state = JobState.COMPLETED if job.job_id in self._scheduler.jobs else JobState.UNKNOWN
165
+ return JobStatus(job_id=job.job_id, state=state)
166
+
167
+ def retrieve(self, job: JobReference) -> RawResult:
168
+ try:
169
+ return self._scheduler.jobs[job.job_id]
170
+ except KeyError as error:
171
+ raise LookupError(f"unknown PIC job {job.job_id}") from error
172
+
173
+ def normalize(self, result: RawResult) -> NormalizedResult:
174
+ benchmark = PICSufficiencyResult.model_validate(result.payload["benchmark"])
175
+ if not benchmark.maxwellian.validity_passed or not benchmark.two_stream.validity_passed:
176
+ raise ValueError("PIC result failed numerical validity gates")
177
+ return NormalizedResult(
178
+ experiment_id=str(result.payload["experiment_id"]),
179
+ observables={
180
+ "maxwellian_growth_rate": benchmark.maxwellian.effective_growth_rate,
181
+ "two_stream_growth_rate": benchmark.two_stream.effective_growth_rate,
182
+ "moments_match": benchmark.moments_match,
183
+ "hypothesis_falsified": benchmark.hypothesis_falsified,
184
+ },
185
+ diagnostics=benchmark.model_dump(mode="json"),
186
+ artifact_hashes=result.artifact_hashes,
187
+ )
188
+
189
+ def cancel(self, job: JobReference) -> JobStatus:
190
+ if job.job_id in self._scheduler.jobs:
191
+ return JobStatus(
192
+ job_id=job.job_id,
193
+ state=JobState.COMPLETED,
194
+ detail="in-process PIC jobs complete synchronously",
195
+ )
196
+ return JobStatus(job_id=job.job_id, state=JobState.UNKNOWN)