qig-bench 0.1.0__tar.gz
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.
qig_bench-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: qig-bench
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Validation harness for QIG compute backends — benchmark against frozen physics results
|
|
5
|
+
Project-URL: Homepage, https://github.com/GaryOcean428/qig-bench
|
|
6
|
+
Project-URL: Repository, https://github.com/GaryOcean428/qig-bench
|
|
7
|
+
Author-email: Braden Lang <braden@garyocean.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: benchmarking,physics,qig,quantum,validation
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Requires-Dist: numpy>=1.24
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
19
|
+
Provides-Extra: full
|
|
20
|
+
Requires-Dist: qig-warp>=0.4.0; extra == 'full'
|
|
21
|
+
Requires-Dist: scipy>=1.10; extra == 'full'
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# qig-bench
|
|
25
|
+
|
|
26
|
+
Validation harness for QIG compute backends — benchmark against frozen physics results.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install qig-bench
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from qig_bench import run_suite
|
|
38
|
+
from qig_bench.compare import compare
|
|
39
|
+
|
|
40
|
+
results = run_suite(backend="my-backend", verification_root="path/to/qig-verification")
|
|
41
|
+
table = compare({"my-backend": results})
|
|
42
|
+
print(table)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Core 5 Benchmarks
|
|
46
|
+
|
|
47
|
+
| # | Benchmark | Frozen Value | Tolerance |
|
|
48
|
+
|---|---|---|---|
|
|
49
|
+
| 1 | Constitutive κ at L=4 | 63.32 | ±5% |
|
|
50
|
+
| 2 | Screening ξ_G at L=5 | 0.6182 | ±2% |
|
|
51
|
+
| 3 | Anderson α | 0.089/site | ±5% |
|
|
52
|
+
| 4 | Bridge exponent | 0.86 | ±10% |
|
|
53
|
+
| 5 | Regime h_t | 0.1055 | ±5% |
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# qig-bench
|
|
2
|
+
|
|
3
|
+
Validation harness for QIG compute backends — benchmark against frozen physics results.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install qig-bench
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from qig_bench import run_suite
|
|
15
|
+
from qig_bench.compare import compare
|
|
16
|
+
|
|
17
|
+
results = run_suite(backend="my-backend", verification_root="path/to/qig-verification")
|
|
18
|
+
table = compare({"my-backend": results})
|
|
19
|
+
print(table)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Core 5 Benchmarks
|
|
23
|
+
|
|
24
|
+
| # | Benchmark | Frozen Value | Tolerance |
|
|
25
|
+
|---|---|---|---|
|
|
26
|
+
| 1 | Constitutive κ at L=4 | 63.32 | ±5% |
|
|
27
|
+
| 2 | Screening ξ_G at L=5 | 0.6182 | ±2% |
|
|
28
|
+
| 3 | Anderson α | 0.089/site | ±5% |
|
|
29
|
+
| 4 | Bridge exponent | 0.86 | ±10% |
|
|
30
|
+
| 5 | Regime h_t | 0.1055 | ±5% |
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "qig-bench"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Validation harness for QIG compute backends — benchmark against frozen physics results"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Braden Lang", email = "braden@garyocean.com"},
|
|
14
|
+
]
|
|
15
|
+
keywords = ["qig", "benchmarking", "validation", "physics", "quantum"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Science/Research",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Topic :: Scientific/Engineering :: Physics",
|
|
22
|
+
]
|
|
23
|
+
dependencies = [
|
|
24
|
+
"numpy>=1.24",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
full = ["scipy>=1.10", "qig-warp>=0.4.0"]
|
|
29
|
+
dev = ["pytest>=7.0"]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://github.com/GaryOcean428/qig-bench"
|
|
33
|
+
Repository = "https://github.com/GaryOcean428/qig-bench"
|
|
34
|
+
|
|
35
|
+
[tool.hatch.build.targets.wheel]
|
|
36
|
+
packages = ["src/qig_bench"]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""
|
|
2
|
+
qig-bench v0.1.0 — Validation Harness for QIG Compute Backends
|
|
3
|
+
|
|
4
|
+
Benchmarks compute backends against frozen physics results.
|
|
5
|
+
Any upgrade to qig-compute, qig-warp, or qigv must pass these
|
|
6
|
+
benchmarks before promotion from alpha to stable.
|
|
7
|
+
|
|
8
|
+
Core 5 Benchmark Suite:
|
|
9
|
+
1. Constitutive law κ at L=4 (EXP-000.004)
|
|
10
|
+
2. Screening length ξ at L=5 (EXP-066)
|
|
11
|
+
3. Anderson orthogonality α (EXP-041)
|
|
12
|
+
4. Bridge exponent at L=4 h=3.0 (EXP-042)
|
|
13
|
+
5. Regime sweep h_t at L=5 (EXP-004b)
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
from qig_bench import run_suite, compare
|
|
17
|
+
|
|
18
|
+
# Run against a compute backend
|
|
19
|
+
results = run_suite(backend="qigv")
|
|
20
|
+
|
|
21
|
+
# Compare multiple backends
|
|
22
|
+
table = compare(["pre-warp", "warp-0.3", "warp-0.4.1", "qig-compute"])
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
__version__ = "0.1.0"
|
|
26
|
+
|
|
27
|
+
from qig_bench.suite import (
|
|
28
|
+
BENCHMARKS,
|
|
29
|
+
Benchmark,
|
|
30
|
+
BenchmarkResult,
|
|
31
|
+
run_suite,
|
|
32
|
+
)
|
|
33
|
+
from qig_bench.compare import compare, format_table
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Comparison table generator — format benchmark results across backends.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from qig_bench.suite import BenchmarkResult, BENCHMARKS
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def compare(suite_results: dict[str, list[BenchmarkResult]]) -> str:
|
|
11
|
+
"""Generate comparison table from multiple backend runs.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
suite_results: {backend_name: [BenchmarkResult, ...]}
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
Formatted comparison table string
|
|
18
|
+
"""
|
|
19
|
+
backends = list(suite_results.keys())
|
|
20
|
+
benchmark_ids = list(BENCHMARKS.keys())
|
|
21
|
+
|
|
22
|
+
# Build lookup: backend -> benchmark_id -> result
|
|
23
|
+
lookup = {}
|
|
24
|
+
for backend, results in suite_results.items():
|
|
25
|
+
lookup[backend] = {r.benchmark_id: r for r in results}
|
|
26
|
+
|
|
27
|
+
return format_table(backends, benchmark_ids, lookup)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def format_table(backends: list[str], benchmark_ids: list[str],
|
|
31
|
+
lookup: dict[str, dict[str, BenchmarkResult]]) -> str:
|
|
32
|
+
"""Format the comparison table."""
|
|
33
|
+
# Column headers
|
|
34
|
+
col_headers = {
|
|
35
|
+
"kappa_L4": "κ",
|
|
36
|
+
"xi_L5": "ξ",
|
|
37
|
+
"anderson_alpha": "α",
|
|
38
|
+
"bridge_exponent": "τ-exp",
|
|
39
|
+
"regime_h_t": "h_t",
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
frozen_vals = {bid: BENCHMARKS[bid].frozen_value for bid in benchmark_ids}
|
|
43
|
+
|
|
44
|
+
lines = []
|
|
45
|
+
lines.append("╔" + "═" * 72 + "╗")
|
|
46
|
+
lines.append("║ qig-bench v0.1.0 — Compute Backend Comparison" + " " * 25 + "║")
|
|
47
|
+
lines.append("╠" + "═" * 72 + "╣")
|
|
48
|
+
|
|
49
|
+
# Header row
|
|
50
|
+
header = f"║ {'Backend':>20s}"
|
|
51
|
+
for bid in benchmark_ids:
|
|
52
|
+
header += f" {col_headers.get(bid, bid):>8s}"
|
|
53
|
+
header += f" {'Time':>8s} {'Pass':>5s} ║"
|
|
54
|
+
lines.append(header)
|
|
55
|
+
|
|
56
|
+
# Frozen reference row
|
|
57
|
+
frozen_row = f"║ {'[frozen]':>20s}"
|
|
58
|
+
for bid in benchmark_ids:
|
|
59
|
+
frozen_row += f" {frozen_vals[bid]:>8.4f}"
|
|
60
|
+
frozen_row += f" {'—':>8s} {'—':>5s} ║"
|
|
61
|
+
lines.append(frozen_row)
|
|
62
|
+
|
|
63
|
+
lines.append("║" + "─" * 72 + "║")
|
|
64
|
+
|
|
65
|
+
# Backend rows
|
|
66
|
+
for backend in backends:
|
|
67
|
+
row = f"║ {backend:>20s}"
|
|
68
|
+
total_time = 0.0
|
|
69
|
+
all_passed = True
|
|
70
|
+
|
|
71
|
+
for bid in benchmark_ids:
|
|
72
|
+
result = lookup.get(backend, {}).get(bid)
|
|
73
|
+
if result and result.measured_value is not None:
|
|
74
|
+
row += f" {result.measured_value:>8.4f}"
|
|
75
|
+
total_time += result.runtime_s
|
|
76
|
+
if not result.passed:
|
|
77
|
+
all_passed = False
|
|
78
|
+
elif result and result.error:
|
|
79
|
+
row += f" {'ERR':>8s}"
|
|
80
|
+
all_passed = False
|
|
81
|
+
else:
|
|
82
|
+
row += f" {'—':>8s}"
|
|
83
|
+
all_passed = False
|
|
84
|
+
|
|
85
|
+
time_str = f"{total_time:.1f}s" if total_time > 0 else "—"
|
|
86
|
+
pass_str = "✓" if all_passed else "✗"
|
|
87
|
+
row += f" {time_str:>8s} {pass_str:>5s} ║"
|
|
88
|
+
lines.append(row)
|
|
89
|
+
|
|
90
|
+
lines.append("╚" + "═" * 72 + "╝")
|
|
91
|
+
return "\n".join(lines)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def format_detailed(results: list[BenchmarkResult]) -> str:
|
|
95
|
+
"""Format detailed results for a single backend."""
|
|
96
|
+
lines = []
|
|
97
|
+
lines.append(f"Backend: {results[0].backend if results else 'unknown'}")
|
|
98
|
+
lines.append("-" * 60)
|
|
99
|
+
|
|
100
|
+
for r in results:
|
|
101
|
+
bm = BENCHMARKS.get(r.benchmark_id)
|
|
102
|
+
name = bm.name if bm else r.benchmark_id
|
|
103
|
+
status = "PASS" if r.passed else "FAIL"
|
|
104
|
+
if r.error:
|
|
105
|
+
status = f"ERROR: {r.error}"
|
|
106
|
+
|
|
107
|
+
val_str = f"{r.measured_value:.6f}" if r.measured_value is not None else "N/A"
|
|
108
|
+
dev_str = f"{r.deviation_pct:.2f}%" if r.deviation_pct is not None else "N/A"
|
|
109
|
+
|
|
110
|
+
lines.append(f" {name:30s} {val_str:>12s} (frozen: {r.frozen_value:.4f}, "
|
|
111
|
+
f"dev: {dev_str:>8s}, {r.runtime_s:.2f}s) [{status}]")
|
|
112
|
+
|
|
113
|
+
passed = sum(1 for r in results if r.passed)
|
|
114
|
+
total = len(results)
|
|
115
|
+
lines.append(f"\n {passed}/{total} benchmarks passed")
|
|
116
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Core 5 Benchmark Suite — frozen physics results as validation gates.
|
|
3
|
+
|
|
4
|
+
Each benchmark defines:
|
|
5
|
+
- What to measure (observable, experiment source)
|
|
6
|
+
- The frozen value (from qig-verification results)
|
|
7
|
+
- Tolerance (how close the backend must get)
|
|
8
|
+
- A runner function that takes a compute backend and returns the result
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import time
|
|
15
|
+
from dataclasses import dataclass, field
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Callable
|
|
18
|
+
|
|
19
|
+
import numpy as np
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class Benchmark:
|
|
24
|
+
"""Definition of a single benchmark test."""
|
|
25
|
+
id: str
|
|
26
|
+
name: str
|
|
27
|
+
frozen_value: float
|
|
28
|
+
tolerance_pct: float
|
|
29
|
+
unit: str
|
|
30
|
+
source_experiment: str
|
|
31
|
+
description: str
|
|
32
|
+
runner: Callable | None = None # Set after definition
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class BenchmarkResult:
|
|
37
|
+
"""Result of running one benchmark."""
|
|
38
|
+
benchmark_id: str
|
|
39
|
+
measured_value: float | None
|
|
40
|
+
frozen_value: float
|
|
41
|
+
deviation_pct: float | None
|
|
42
|
+
passed: bool
|
|
43
|
+
runtime_s: float
|
|
44
|
+
backend: str
|
|
45
|
+
error: str | None = None
|
|
46
|
+
metadata: dict = field(default_factory=dict)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# ═══════════════════════════════════════════════════════════════
|
|
50
|
+
# CORE 5 BENCHMARKS
|
|
51
|
+
# ═══════════════════════════════════════════════════════════════
|
|
52
|
+
|
|
53
|
+
BENCHMARKS = {
|
|
54
|
+
"kappa_L4": Benchmark(
|
|
55
|
+
id="kappa_L4",
|
|
56
|
+
name="Constitutive κ at L=4",
|
|
57
|
+
frozen_value=63.32,
|
|
58
|
+
tolerance_pct=5.0,
|
|
59
|
+
unit="dimensionless",
|
|
60
|
+
source_experiment="EXP-000.004",
|
|
61
|
+
description="G = κT constitutive law. Matrix-trace extraction on L=4 PBC torus.",
|
|
62
|
+
),
|
|
63
|
+
"xi_L5": Benchmark(
|
|
64
|
+
id="xi_L5",
|
|
65
|
+
name="Screening ξ_G at L=5",
|
|
66
|
+
frozen_value=0.6182,
|
|
67
|
+
tolerance_pct=2.0,
|
|
68
|
+
unit="lattice spacings",
|
|
69
|
+
source_experiment="EXP-066",
|
|
70
|
+
description="Yukawa screening length from single-defect curvature response.",
|
|
71
|
+
),
|
|
72
|
+
"anderson_alpha": Benchmark(
|
|
73
|
+
id="anderson_alpha",
|
|
74
|
+
name="Anderson α",
|
|
75
|
+
frozen_value=0.089,
|
|
76
|
+
tolerance_pct=5.0,
|
|
77
|
+
unit="per site",
|
|
78
|
+
source_experiment="EXP-041",
|
|
79
|
+
description="Orthogonality catastrophe decay rate from wavefunction overlap.",
|
|
80
|
+
),
|
|
81
|
+
"bridge_exponent": Benchmark(
|
|
82
|
+
id="bridge_exponent",
|
|
83
|
+
name="Bridge exponent at h=3.0",
|
|
84
|
+
frozen_value=0.86,
|
|
85
|
+
tolerance_pct=10.0,
|
|
86
|
+
unit="dimensionless",
|
|
87
|
+
source_experiment="EXP-042",
|
|
88
|
+
description="τ ∝ J^α bridge law from sign-flip N-updates counting.",
|
|
89
|
+
),
|
|
90
|
+
"regime_h_t": Benchmark(
|
|
91
|
+
id="regime_h_t",
|
|
92
|
+
name="Regime transition h_t",
|
|
93
|
+
frozen_value=0.10554,
|
|
94
|
+
tolerance_pct=5.0,
|
|
95
|
+
unit="field strength",
|
|
96
|
+
source_experiment="EXP-004b",
|
|
97
|
+
description="First field value where R² crosses 0.5 in κ(h) sweep.",
|
|
98
|
+
),
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _check_result(measured: float, frozen: float, tolerance_pct: float) -> tuple[float, bool]:
|
|
103
|
+
"""Check if measured value is within tolerance of frozen value."""
|
|
104
|
+
if frozen == 0:
|
|
105
|
+
deviation = abs(measured) * 100
|
|
106
|
+
else:
|
|
107
|
+
deviation = abs(measured - frozen) / abs(frozen) * 100
|
|
108
|
+
passed = deviation <= tolerance_pct
|
|
109
|
+
return round(deviation, 4), passed
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def run_single(benchmark: Benchmark, backend: str = "qigv",
|
|
113
|
+
verification_root: Path | None = None) -> BenchmarkResult:
|
|
114
|
+
"""Run a single benchmark against a compute backend.
|
|
115
|
+
|
|
116
|
+
For now, reads frozen results from qig-verification files.
|
|
117
|
+
Future: actually recompute using the specified backend.
|
|
118
|
+
"""
|
|
119
|
+
t0 = time.time()
|
|
120
|
+
|
|
121
|
+
if verification_root is None:
|
|
122
|
+
# Try to find qig-verification relative to this package
|
|
123
|
+
here = Path(__file__).resolve()
|
|
124
|
+
for parent in here.parents:
|
|
125
|
+
candidate = parent / "qig-verification" / "results"
|
|
126
|
+
if candidate.exists():
|
|
127
|
+
verification_root = parent / "qig-verification"
|
|
128
|
+
break
|
|
129
|
+
if verification_root is None:
|
|
130
|
+
return BenchmarkResult(
|
|
131
|
+
benchmark_id=benchmark.id,
|
|
132
|
+
measured_value=None,
|
|
133
|
+
frozen_value=benchmark.frozen_value,
|
|
134
|
+
deviation_pct=None,
|
|
135
|
+
passed=False,
|
|
136
|
+
runtime_s=0,
|
|
137
|
+
backend=backend,
|
|
138
|
+
error="Cannot find qig-verification/results directory",
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
try:
|
|
142
|
+
measured = _read_frozen_result(benchmark.id, verification_root)
|
|
143
|
+
deviation, passed = _check_result(measured, benchmark.frozen_value, benchmark.tolerance_pct)
|
|
144
|
+
|
|
145
|
+
return BenchmarkResult(
|
|
146
|
+
benchmark_id=benchmark.id,
|
|
147
|
+
measured_value=round(measured, 6),
|
|
148
|
+
frozen_value=benchmark.frozen_value,
|
|
149
|
+
deviation_pct=deviation,
|
|
150
|
+
passed=passed,
|
|
151
|
+
runtime_s=round(time.time() - t0, 4),
|
|
152
|
+
backend=backend,
|
|
153
|
+
)
|
|
154
|
+
except Exception as e:
|
|
155
|
+
return BenchmarkResult(
|
|
156
|
+
benchmark_id=benchmark.id,
|
|
157
|
+
measured_value=None,
|
|
158
|
+
frozen_value=benchmark.frozen_value,
|
|
159
|
+
deviation_pct=None,
|
|
160
|
+
passed=False,
|
|
161
|
+
runtime_s=round(time.time() - t0, 4),
|
|
162
|
+
backend=backend,
|
|
163
|
+
error=str(e),
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _read_frozen_result(benchmark_id: str, verification_root: Path) -> float:
|
|
168
|
+
"""Read a frozen result value from qig-verification."""
|
|
169
|
+
results = verification_root / "results"
|
|
170
|
+
|
|
171
|
+
if benchmark_id == "kappa_L4":
|
|
172
|
+
registry = results / "validated" / "kappa_registry.json"
|
|
173
|
+
if registry.exists():
|
|
174
|
+
with open(registry) as f:
|
|
175
|
+
data = json.load(f)
|
|
176
|
+
# Structure: {"validated": {"L4_geometric": {"kappa": ...}}}
|
|
177
|
+
validated = data.get("validated", {})
|
|
178
|
+
for key, entry in validated.items():
|
|
179
|
+
if isinstance(entry, dict) and entry.get("L") == 4:
|
|
180
|
+
return entry["kappa"]
|
|
181
|
+
raise FileNotFoundError("No kappa value found for L=4 in registry")
|
|
182
|
+
|
|
183
|
+
elif benchmark_id == "xi_L5":
|
|
184
|
+
# Read from pruning_validation.json which has the frozen ξ fits
|
|
185
|
+
pv = results / "exp066" / "pruning_validation.json"
|
|
186
|
+
if pv.exists():
|
|
187
|
+
with open(pv) as f:
|
|
188
|
+
data = json.load(f)
|
|
189
|
+
for entry in data.get("results", []):
|
|
190
|
+
if entry.get("L") == 5:
|
|
191
|
+
return entry["full"]["xi"]
|
|
192
|
+
raise FileNotFoundError("No ξ found in EXP-066 pruning_validation.json")
|
|
193
|
+
|
|
194
|
+
elif benchmark_id == "anderson_alpha":
|
|
195
|
+
for fn in sorted((results / "exp041").glob("*.json")):
|
|
196
|
+
with open(fn) as f:
|
|
197
|
+
data = json.load(f)
|
|
198
|
+
ps = data.get("primary_scaling", {})
|
|
199
|
+
if isinstance(ps, dict) and "alpha_per_site" in ps:
|
|
200
|
+
return ps["alpha_per_site"]
|
|
201
|
+
raise FileNotFoundError("No Anderson alpha found in EXP-041 results")
|
|
202
|
+
|
|
203
|
+
elif benchmark_id == "bridge_exponent":
|
|
204
|
+
# Find L=4 or L=5 at h=3.0 and fit τ vs J
|
|
205
|
+
for fn in sorted((results / "exp042").glob("*L5*.json")):
|
|
206
|
+
with open(fn) as f:
|
|
207
|
+
data = json.load(f)
|
|
208
|
+
per_J = data.get("per_J", [])
|
|
209
|
+
if not per_J:
|
|
210
|
+
continue
|
|
211
|
+
# Extract τ = N_updates / omega for J >= 1.5
|
|
212
|
+
Js, taus = [], []
|
|
213
|
+
for p in per_J:
|
|
214
|
+
J = p.get("J", 0)
|
|
215
|
+
N = p.get("N_updates", 0)
|
|
216
|
+
omega = p.get("omega", 0)
|
|
217
|
+
if J >= 1.5 and omega > 0 and N > 0:
|
|
218
|
+
Js.append(J)
|
|
219
|
+
taus.append(N / omega)
|
|
220
|
+
if len(Js) >= 3:
|
|
221
|
+
log_J = np.log(np.array(Js))
|
|
222
|
+
log_tau = np.log(np.array(taus))
|
|
223
|
+
coeffs = np.polyfit(log_J, log_tau, 1)
|
|
224
|
+
return coeffs[0] # bridge exponent
|
|
225
|
+
# Fallback to L=4
|
|
226
|
+
for fn in sorted((results / "exp042").glob("*L4*.json")):
|
|
227
|
+
with open(fn) as f:
|
|
228
|
+
data = json.load(f)
|
|
229
|
+
per_J = data.get("per_J", [])
|
|
230
|
+
if not per_J:
|
|
231
|
+
continue
|
|
232
|
+
Js, taus = [], []
|
|
233
|
+
for p in per_J:
|
|
234
|
+
J = p.get("J", 0)
|
|
235
|
+
N = p.get("N_updates", 0)
|
|
236
|
+
omega = p.get("omega", 0)
|
|
237
|
+
if J >= 1.5 and omega > 0 and N > 0:
|
|
238
|
+
Js.append(J)
|
|
239
|
+
taus.append(N / omega)
|
|
240
|
+
if len(Js) >= 3:
|
|
241
|
+
log_J = np.log(np.array(Js))
|
|
242
|
+
log_tau = np.log(np.array(taus))
|
|
243
|
+
coeffs = np.polyfit(log_J, log_tau, 1)
|
|
244
|
+
return coeffs[0]
|
|
245
|
+
raise FileNotFoundError("No bridge exponent found in EXP-042 results")
|
|
246
|
+
|
|
247
|
+
elif benchmark_id == "regime_h_t":
|
|
248
|
+
for fn in sorted((results / "exp004b").glob("*L5*.json")):
|
|
249
|
+
with open(fn) as f:
|
|
250
|
+
data = json.load(f)
|
|
251
|
+
if "h_t" in data:
|
|
252
|
+
return data["h_t"]
|
|
253
|
+
if "transition_midpoint" in data:
|
|
254
|
+
return data["transition_midpoint"]
|
|
255
|
+
if "analysis" in data:
|
|
256
|
+
a = data["analysis"]
|
|
257
|
+
if isinstance(a, dict) and "h_t" in a:
|
|
258
|
+
return a["h_t"]
|
|
259
|
+
raise FileNotFoundError("No h_t found in EXP-004b results")
|
|
260
|
+
|
|
261
|
+
raise ValueError(f"Unknown benchmark: {benchmark_id}")
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def run_suite(backend: str = "qigv",
|
|
265
|
+
verification_root: Path | None = None) -> list[BenchmarkResult]:
|
|
266
|
+
"""Run all Core 5 benchmarks."""
|
|
267
|
+
results = []
|
|
268
|
+
for bm in BENCHMARKS.values():
|
|
269
|
+
result = run_single(bm, backend=backend, verification_root=verification_root)
|
|
270
|
+
results.append(result)
|
|
271
|
+
return results
|