FRCM Pi D R6 reciprocal-loop candidate diagnostic.py
#!/usr/bin/env python3
"""FRCM Pi D R6 reciprocal-loop candidate diagnostic.
Runs ten simultaneous transitions at 60 decimal digits using fixed B_G.
This is a candidate iteration, not a lag test, physical-time model, or lock test.
It writes a timestamped JSON result and SHA-256 sidecar.
"""
from datetime import datetime, timezone
import hashlib
import json
from pathlib import Path
import mpmath as mp
# Runtime and output location
mp.mp.dps = 60
TRANSITIONS = 10
CHECK_TOL = mp.mpf("1e-45")
# __file__ exists when run as a .py file; Colab/Jupyter cells usually lack it.
if "__file__" in globals():
OUT_DIR = Path(__file__).resolve().parent
else:
OUT_DIR = Path.cwd()
RUN_ID = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
JSON_PATH = OUT_DIR / f"R6_RECIPROCAL_LOOP_CANDIDATE_{RUN_ID}.json"
HASH_PATH = OUT_DIR / f"R6_RECIPROCAL_LOOP_CANDIDATE_{RUN_ID}.json.sha256"
def mat(rows):
"""Build an mpmath matrix from decimal strings."""
return mp.matrix([[mp.mpf(str(value)) for value in row] for row in rows])
def trace(matrix):
if matrix.rows != matrix.cols:
raise ValueError("trace() requires a square matrix")
return sum(matrix[i, i] for i in range(matrix.rows))
def frobenius_norm(matrix):
return mp.sqrt(
sum(
abs(matrix[i, j]) ** 2
for i in range(matrix.rows)
for j in range(matrix.cols)
)
)
def max_abs(matrix):
return max(
abs(matrix[i, j])
for i in range(matrix.rows)
for j in range(matrix.cols)
)
def matrix_to_json(matrix):
return [
[mp.nstr(matrix[i, j], mp.mp.dps) for j in range(matrix.cols)]
for i in range(matrix.rows)
]
def scalar_to_json(value):
return mp.nstr(value, mp.mp.dps)
def is_finite(value):
return not mp.isnan(value) and not mp.isinf(value)
def require_close(name, actual, expected, tolerance):
residual = abs(actual - expected)
if not is_finite(residual) or residual > tolerance:
raise RuntimeError(
f"Fail-closed check '{name}' failed: "
f"residual={mp.nstr(residual, 12)}, "
f"tolerance={mp.nstr(tolerance, 6)}"
)
return residual
def geometric_sum(ratio, n):
"""Return sum(ratio**k for k=0..n-1), including ratio == 1."""
if n == 0:
return mp.mpf("0")
if ratio == 1:
return mp.mpf(n)
return (ratio ** n - 1) / (ratio - 1)
# Fixed candidate inputs
G = mat([["2.0", "0.5"], ["0.5", "1.5"]])
# Legacy mapping from the recorded v0.8 example:
# b_initial is the old v0.8 P0; P_initial is the old v0.8 P1.
b_initial = mat([["1.5", "0.4"], ["-0.3", "0.8"]])
P_initial = mat([["2.0", "0.5"], ["-0.5", "1.0"]])
lam = mp.mpf("0.5")
mu = mp.mpf("0.5")
if G.rows != 2 or G.cols != 2 or b_initial.rows != 2 or P_initial.rows != 2:
raise ValueError("This candidate is specified for 2x2 matrices.")
if G.cols != G.rows or b_initial.cols != b_initial.rows or P_initial.cols != P_initial.rows:
raise ValueError("G, b_initial, and P_initial must be square.")
if lam < 0 or mu < 0:
raise ValueError("This recorded candidate uses nonnegative lambda and mu.")
# Calculate the G-adjoint skew direction once, then freeze it.
B_G = P_initial - mp.inverse(G) * P_initial.T * G
q = trace(B_G * B_G)
a_initial = trace((b_initial - P_initial) * B_G)
amplification_factor = 1 - (lam + mu) * q
# Independent exact-rational reference values for these fixed inputs.
q_expected = -mp.mpf(25) / 22
a_initial_expected = mp.mpf(7) / 44
amplification_expected = mp.mpf(47) / 22
q_residual = require_close("q", q, q_expected, CHECK_TOL)
a_initial_residual = require_close(
"initial mismatch", a_initial, a_initial_expected, CHECK_TOL
)
amplification_residual = require_close(
"amplification factor",
amplification_factor,
amplification_expected,
CHECK_TOL,
)
if q == 0:
raise RuntimeError("B_G direction is degenerate (q=0); candidate recurrence undefined.")
if not all(is_finite(x) for x in [q, a_initial, amplification_factor]):
raise RuntimeError("Non-finite scalar detected; refusing to write a result.")
# Ten simultaneous/Jacobi transitions and analytic cross-check
b = b_initial.copy()
P = P_initial.copy()
snapshots = []
summary_rows = []
max_scalar_residual = mp.mpf("0")
max_b_matrix_residual = mp.mpf("0")
max_P_matrix_residual = mp.mpf("0")
for n in range(TRANSITIONS + 1):
# Record the computed state at index n before any next transition.
mismatch = trace((b - P) * B_G)
state_difference_norm = frobenius_norm(b - P)
# Closed form from the fixed-operator recurrence.
r_power = amplification_factor ** n
sum_r = geometric_sum(amplification_factor, n)
mismatch_prediction = a_initial_expected * r_power
b_coefficient = lam * a_initial_expected * sum_r
P_coefficient = mu * a_initial_expected * sum_r
b_prediction = b_initial - b_coefficient * B_G
P_prediction = P_initial + P_coefficient * B_G
scalar_residual = mismatch - mismatch_prediction
b_matrix_residual = b - b_prediction
P_matrix_residual = P - P_prediction
max_scalar_residual = max(max_scalar_residual, abs(scalar_residual))
max_b_matrix_residual = max(max_b_matrix_residual, max_abs(b_matrix_residual))
max_P_matrix_residual = max(max_P_matrix_residual, max_abs(P_matrix_residual))
snapshots.append({
"n": n,
"b_n": matrix_to_json(b),
"P_n": matrix_to_json(P),
"a_n_trace_mismatch": scalar_to_json(mismatch),
"E_n_frobenius_norm_b_minus_P": scalar_to_json(state_difference_norm),
"analytic_comparison": {
"a_n_predicted": scalar_to_json(mismatch_prediction),
"a_n_residual": scalar_to_json(scalar_residual),
"b_n_residual_max_abs": scalar_to_json(max_abs(b_matrix_residual)),
"P_n_residual_max_abs": scalar_to_json(max_abs(P_matrix_residual)),
},
})
summary_rows.append((n, mismatch, state_difference_norm))
# Do exactly ten transitions. At n=10, record only; do not update again.
if n < TRANSITIONS:
# Both use the same pre-update mismatch and frozen B_G.
b_next = b - lam * mismatch * B_G
P_next = P + mu * mismatch * B_G
b, P = b_next, P_next
# Require the computed trajectory to agree with the independent closed form.
for name, residual in [
("trajectory scalar residual", max_scalar_residual),
("b trajectory matrix residual", max_b_matrix_residual),
("P trajectory matrix residual", max_P_matrix_residual),
]:
if not is_finite(residual) or residual > CHECK_TOL:
raise RuntimeError(
f"Fail-closed trajectory check '{name}' failed: "
f"residual={mp.nstr(residual, 12)}, "
f"tolerance={mp.nstr(CHECK_TOL, 6)}"
)
# Result record: explicit about what this candidate did not test.
record = {
"record_type": "FRCM_PI_D_R6_RECIPROCAL_LOOP_CANDIDATE",
"status": "COMPUTED_CANDIDATE_ITERATION_NOT_LAG_TEST",
"generated_utc": datetime.now(timezone.utc).isoformat(),
"precision_decimal_digits": mp.mp.dps,
"transition_count": TRANSITIONS,
"sequence_index_semantics": "ordinal mathematical iteration index; not physical time",
"test_scope": {
"reciprocal_candidate_iteration": "computed",
"lag_or_delay": "NOT_TESTED: no delayed-state rule or input-switch event is defined",
"P_equals_2I_zero_coupling_lock": "NOT_TESTED: B_G is frozen to the v0.8 P1 reference",
"physical_interpretation": "not assigned",
},
"candidate_update": {
"mismatch": "a_n = trace((b_n - P_n) * B_G)",
"b_update": "b_(n+1) = b_n - lambda * a_n * B_G",
"P_update": "P_(n+1) = P_n + mu * a_n * B_G",
"state_timing": "simultaneous/Jacobi-style; both updates use the same pre-update pair",
"B_G_timing": "computed from P_initial once and frozen for all transitions",
"candidate_status": "proposed update law; not recovered as an established project law",
},
"parameters": {
"G": matrix_to_json(G),
"b_initial": matrix_to_json(b_initial),
"P_initial": matrix_to_json(P_initial),
"lambda": scalar_to_json(lam),
"mu": scalar_to_json(mu),
"B_G_frozen": matrix_to_json(B_G),
"B_G_construction": "P_initial - inverse(G) * transpose(P_initial) * G",
},
"provenance": {
"b_initial_legacy_mapping": "v0.8 P0",
"P_initial_legacy_mapping": "v0.8 P1",
"mu_status": "researcher-selected candidate value; set to 0.5 for this run",
"source_json": "not read by this standalone script; inputs are stated explicitly above",
},
"derived_checks": {
"q_trace_B_G_squared": scalar_to_json(q),
"a_initial": scalar_to_json(a_initial),
"amplification_factor": scalar_to_json(amplification_factor),
"expected_q": scalar_to_json(q_expected),
"expected_a_initial": scalar_to_json(a_initial_expected),
"expected_amplification_factor": scalar_to_json(amplification_expected),
"q_residual": scalar_to_json(q_residual),
"a_initial_residual": scalar_to_json(a_initial_residual),
"amplification_factor_residual": scalar_to_json(amplification_residual),
"max_trajectory_scalar_residual": scalar_to_json(max_scalar_residual),
"max_b_matrix_residual": scalar_to_json(max_b_matrix_residual),
"max_P_matrix_residual": scalar_to_json(max_P_matrix_residual),
"check_tolerance": scalar_to_json(CHECK_TOL),
"analytic_recurrence": "a_(n+1) = [1 - (lambda + mu) q] * a_n",
"analytic_closed_form": (
"a_n = a_0*r^n; "
"b_n = b_0 - lambda*a_0*sum(r^k,k=0..n-1)*B_G; "
"P_n = P_0 + mu*a_0*sum(r^k,k=0..n-1)*B_G"
),
},
"snapshots": snapshots,
"final_states": {
"b_10": matrix_to_json(b),
"P_10": matrix_to_json(P),
"a_10": snapshots[-1]["a_n_trace_mismatch"],
"E_10_frobenius_norm_b_minus_P": snapshots[-1]["E_n_frobenius_norm_b_minus_P"],
},
}
# Preserve arbitrary precision as decimal strings in JSON.
json_bytes = (
json.dumps(record, indent=2, ensure_ascii=False) + chr(10)
).encode("utf-8")
JSON_PATH.write_bytes(json_bytes)
# Hash the exact bytes written to disk.
sha256 = hashlib.sha256(JSON_PATH.read_bytes()).hexdigest()
HASH_PATH.write_text(
f"{sha256} {JSON_PATH.name}" + chr(10),
encoding="ascii",
)
# Best-effort source hash when run from a .py file.
source_hash = None
if "__file__" in globals():
try:
source_hash = hashlib.sha256(
Path(__file__).resolve().read_bytes()
).hexdigest()
except OSError:
source_hash = None
# Human-readable report.
print("FRCM Pi D — R6 RECIPROCAL LOOP CANDIDATE")
print(f"mpmath precision: {mp.mp.dps} decimal digits")
print(f"Transitions executed: {TRANSITIONS}")
print(f"q = {mp.nstr(q, 30)}")
print(f"a_0 = {mp.nstr(a_initial, 30)}")
print(f"r = {mp.nstr(amplification_factor, 30)}")
print(
"At this r, scalar mismatch "
f"{'amplifies' if abs(r) > 1 else 'decays' if abs(r) < 1 else 'is neutral'}."
)
print("Lag/delay: NOT TESTED (no delayed-state rule or input-switch event).")
print("P=2I lock: NOT TESTED (B_G was frozen to the v0.8 P1 reference).")
print()
print(" n | a_n | E_n = ||b_n - P_n||_F")
print("---+--------------------------------+--------------------------------")
for n, mismatch, norm in summary_rows:
print(f"{n:2d} | {mp.nstr(mismatch, 20):>30} | {mp.nstr(norm, 20):>30}")
print()
print("Maximum computed-versus-analytic residuals:")
print("scalar:", mp.nstr(max_scalar_residual, 12))
print("b matrix max_abs:", mp.nstr(max_b_matrix_residual, 12))
print("P matrix max_abs:", mp.nstr(max_P_matrix_residual, 12))
print(f"JSON: {JSON_PATH}")
print(f"SHA-256 sidecar: {HASH_PATH}")
print(f"JSON SHA-256: {sha256}")
if source_hash is None:
print("Source SHA-256: unavailable (run from a saved .py file to record it)")
else:
print(f"Source SHA-256: {source_hash}")
# In Colab, download both saved artifacts to the user's computer.
try:
from google.colab import files
except ImportError:
files = None
if files is not None:
files.download(str(JSON_PATH))
files.download(str(HASH_PATH))