Skip to content

Commit 8374a76

Browse files
committed
Add relational modal known-truth report runner
1 parent c125252 commit 8374a76

1 file changed

Lines changed: 158 additions & 0 deletions

File tree

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
"""Durable known-truth report for relational shear-Weyl modal diagnostics."""
2+
3+
from __future__ import annotations
4+
5+
import argparse
6+
import json
7+
import pathlib
8+
import sys
9+
10+
import numpy as np
11+
12+
HERE = pathlib.Path(__file__).resolve().parent
13+
if str(HERE) not in sys.path:
14+
sys.path.insert(0, str(HERE))
15+
16+
from modal_relational import analyze_tensor_relation, relation_under_common_rotation
17+
from modal_tensor import rotate_tensor, rotation_matrix
18+
19+
20+
def build_report() -> dict:
21+
cases = []
22+
23+
e = np.diag([3.0, 0.4, -2.1])
24+
s = np.diag([1.7, 0.2, -1.0])
25+
aligned = analyze_tensor_relation(e, s)
26+
cases.append({
27+
"case_id": "MR-01",
28+
"purpose": "aligned distinct spectra yield zero commutator",
29+
"status": "PASS",
30+
"result": aligned.as_jsonable(),
31+
})
32+
33+
rel = rotation_matrix(np.array([0.0, 0.0, 1.0]), 0.43)
34+
s_rot = rotate_tensor(s, rel)
35+
rotated = analyze_tensor_relation(e, s_rot)
36+
cases.append({
37+
"case_id": "MR-02",
38+
"purpose": "same separate spectra/invariants but different relative eigenframe",
39+
"status": "PASS_RELATIONAL_INFORMATION_NOT_REDUCED_TO_SEPARATE_SPECTRA",
40+
"aligned_normalized_commutator": aligned.normalized_commutator_frobenius,
41+
"rotated_normalized_commutator": rotated.normalized_commutator_frobenius,
42+
"rotated_alignment_matrix_abs": rotated.alignment_matrix_abs.tolist(),
43+
"same_second_eigenvalues": bool(np.allclose(
44+
aligned.second.eigenvalues, rotated.second.eigenvalues, atol=1e-12, rtol=1e-12
45+
)),
46+
})
47+
48+
global_rotation = rotation_matrix(np.array([1.0, -2.0, 0.3]), 0.79)
49+
common = relation_under_common_rotation(e, s_rot, global_rotation)
50+
cases.append({
51+
"case_id": "MR-03",
52+
"purpose": "common coordinate rotation covariance",
53+
"status": "PASS",
54+
"alignment_max_abs_error": float(np.max(np.abs(
55+
common.alignment_matrix_abs - rotated.alignment_matrix_abs
56+
))),
57+
"commutator_norm_abs_error": abs(
58+
common.commutator_frobenius_norm - rotated.commutator_frobenius_norm
59+
),
60+
"normalized_commutator_abs_error": abs(
61+
float(common.normalized_commutator_frobenius)
62+
- float(rotated.normalized_commutator_frobenius)
63+
),
64+
"axial_rotation_max_abs_error": float(np.max(np.abs(
65+
common.commutator_axial_vector
66+
- global_rotation @ rotated.commutator_axial_vector
67+
))),
68+
})
69+
70+
e_deg = np.diag([1.0, 1.0, -2.0])
71+
s_deg = rotate_tensor(
72+
np.diag([2.0, 2.0, -4.0]),
73+
rotation_matrix(np.array([0.0, 0.0, 1.0]), 0.71),
74+
)
75+
degenerate = analyze_tensor_relation(e_deg, s_deg)
76+
cases.append({
77+
"case_id": "MR-04",
78+
"purpose": "exact degenerate subspace refuses unique-axis interpretation",
79+
"status": "REFUSED_UNIQUE_AXIS_RELATION_BUT_TENSOR_RELATION_RETAINED",
80+
"result": degenerate.as_jsonable(),
81+
})
82+
83+
zero = analyze_tensor_relation(np.zeros((3, 3)), np.diag([1.0, 0.0, -1.0]))
84+
cases.append({
85+
"case_id": "MR-05",
86+
"purpose": "zero-norm tensor refuses normalized commutator",
87+
"status": "REFUSED_NORMALIZED_SCALAR_SUMMARY",
88+
"result": zero.as_jsonable(),
89+
})
90+
91+
generic = analyze_tensor_relation(
92+
np.diag([2.0, 0.5, -1.5]),
93+
rotate_tensor(
94+
np.diag([1.2, 0.1, -0.8]),
95+
rotation_matrix(np.array([1.0, 1.0, 0.0]), 0.37),
96+
),
97+
)
98+
cases.append({
99+
"case_id": "MR-06",
100+
"purpose": "symmetric-tensor commutator is antisymmetric",
101+
"status": "PASS",
102+
"antisymmetry_residual": float(np.linalg.norm(
103+
generic.commutator + generic.commutator.T, ord="fro"
104+
)),
105+
})
106+
107+
base = np.diag([1.4, 0.1, -0.9])
108+
sweep = []
109+
for angle in (0.0, 0.1, 0.2, 0.3):
110+
relation = analyze_tensor_relation(
111+
np.diag([3.0, 0.5, -2.0]),
112+
rotate_tensor(
113+
base, rotation_matrix(np.array([0.0, 0.0, 1.0]), angle)
114+
),
115+
)
116+
sweep.append({
117+
"angle_radians": angle,
118+
"commutator_frobenius_norm": relation.commutator_frobenius_norm,
119+
"normalized_commutator_frobenius": relation.normalized_commutator_frobenius,
120+
})
121+
cases.append({
122+
"case_id": "MR-07",
123+
"purpose": "relative rotation produces continuous relational response",
124+
"status": "MEASURED_NO_SCIENTIFIC_THRESHOLD_FROZEN",
125+
"rotation_sweep": sweep,
126+
})
127+
128+
return {
129+
"protocol": "MODAL_RELATIONAL_KNOWN_TRUTH_SUITE",
130+
"stage": "P0-Q",
131+
"scientific_claim_tested": False,
132+
"scientific_thresholds_frozen": False,
133+
"primary_object": "full relation between two symmetric modal tensors",
134+
"relational_objects": [
135+
"absolute eigenframe overlap matrix",
136+
"tensor commutator",
137+
"commutator axial vector",
138+
"commutator norm summaries",
139+
],
140+
"case_count": len(cases),
141+
"cases": cases,
142+
"next_gate": "APQ-3 residual/adjudication before any late-time outcome exposure",
143+
}
144+
145+
146+
def main() -> None:
147+
parser = argparse.ArgumentParser()
148+
parser.add_argument("--output", required=True)
149+
args = parser.parse_args()
150+
report = build_report()
151+
output = pathlib.Path(args.output)
152+
output.parent.mkdir(parents=True, exist_ok=True)
153+
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n")
154+
print(json.dumps({"case_count": report["case_count"], "output": str(output)}))
155+
156+
157+
if __name__ == "__main__":
158+
main()

0 commit comments

Comments
 (0)