mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
This squashes all local history (768 commits) onto the scrubbed PR #90 baseline. Individual commits were lost during filter-repo corruption; the working tree content is preserved intact. Build: N/A (working tree state only)
336 lines
12 KiB
Python
336 lines
12 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
braid_shock_16d.py - 16D BraidShock PrimeFold Simulator with Dimensional Shock Trim (DST)
|
|
Implements Q16_16 integer-only arithmetic to model soliton charged front propagation,
|
|
folded-prime impedance, reaction drainage, and underverse payment ledger logging.
|
|
"""
|
|
|
|
import sys
|
|
import os
|
|
import json
|
|
import argparse
|
|
import hashlib
|
|
|
|
# Canonical Q16_16 Fixed-Point Arithmetic in Python
|
|
class Q16_16:
|
|
SCALE = 65536
|
|
MIN_VAL = -2147483648
|
|
MAX_VAL = 2147483647
|
|
|
|
@staticmethod
|
|
def from_float(f: float) -> int:
|
|
return int(max(min(f * Q16_16.SCALE, Q16_16.MAX_VAL), Q16_16.MIN_VAL))
|
|
|
|
@staticmethod
|
|
def to_float(val: int) -> float:
|
|
return val / Q16_16.SCALE
|
|
|
|
@staticmethod
|
|
def clamp(val: int) -> int:
|
|
if val > Q16_16.MAX_VAL:
|
|
return Q16_16.MAX_VAL
|
|
if val < Q16_16.MIN_VAL:
|
|
return Q16_16.MIN_VAL
|
|
return val
|
|
|
|
@staticmethod
|
|
def add(a: int, b: int) -> int:
|
|
return Q16_16.clamp(a + b)
|
|
|
|
@staticmethod
|
|
def sub(a: int, b: int) -> int:
|
|
return Q16_16.clamp(a - b)
|
|
|
|
@staticmethod
|
|
def mul(a: int, b: int) -> int:
|
|
return Q16_16.clamp((a * b) // Q16_16.SCALE)
|
|
|
|
@staticmethod
|
|
def div(a: int, b: int) -> int:
|
|
if b == 0:
|
|
return Q16_16.MAX_VAL
|
|
return Q16_16.clamp((a * Q16_16.SCALE) // b)
|
|
|
|
@staticmethod
|
|
def neg(val: int) -> int:
|
|
return Q16_16.clamp(-val)
|
|
|
|
@staticmethod
|
|
def abs(val: int) -> int:
|
|
return abs(val) if val != Q16_16.MIN_VAL else Q16_16.MAX_VAL
|
|
|
|
# Simple 1D array operations for Q16_16 without importing numpy to ensure portability/compatibility
|
|
class Q16Array:
|
|
@staticmethod
|
|
def zeros(size: int):
|
|
return [0] * size
|
|
|
|
@staticmethod
|
|
def add_arrays(a: list, b: list) -> list:
|
|
return [Q16_16.add(x, y) for x, y in zip(a, b)]
|
|
|
|
@staticmethod
|
|
def sub_arrays(a: list, b: list) -> list:
|
|
return [Q16_16.sub(x, y) for x, y in zip(a, b)]
|
|
|
|
@staticmethod
|
|
def mul_scalar(a: list, s: int) -> list:
|
|
return [Q16_16.mul(x, s) for x in a]
|
|
|
|
@staticmethod
|
|
def grad(a: list) -> list:
|
|
# Central difference with periodic boundary conditions
|
|
size = len(a)
|
|
res = [0] * size
|
|
for i in range(size):
|
|
prev_val = a[i - 1]
|
|
next_val = a[(i + 1) % size]
|
|
# (next - prev) / 2
|
|
res[i] = Q16_16.div(Q16_16.sub(next_val, prev_val), 131072) # 2.0 in Q16_16 is 131072
|
|
return res
|
|
|
|
@staticmethod
|
|
def laplacian(a: list) -> list:
|
|
# Second derivative with periodic boundary conditions
|
|
size = len(a)
|
|
res = [0] * size
|
|
for i in range(size):
|
|
prev_val = a[i - 1]
|
|
curr_val = a[i]
|
|
next_val = a[(i + 1) % size]
|
|
# next - 2*curr + prev
|
|
sum_neighbors = Q16_16.add(next_val, prev_val)
|
|
two_curr = Q16_16.mul(curr_val, 131072)
|
|
res[i] = Q16_16.sub(sum_neighbors, two_curr)
|
|
return res
|
|
|
|
class Cramer4D:
|
|
@staticmethod
|
|
def det3x3(m00, m01, m02, m10, m11, m12, m20, m21, m22) -> int:
|
|
term1 = Q16_16.sub(Q16_16.mul(m11, m22), Q16_16.mul(m12, m21))
|
|
term2 = Q16_16.sub(Q16_16.mul(m10, m22), Q16_16.mul(m12, m20))
|
|
term3 = Q16_16.sub(Q16_16.mul(m10, m21), Q16_16.mul(m11, m20))
|
|
|
|
part1 = Q16_16.mul(m00, term1)
|
|
part2 = Q16_16.mul(m01, term2)
|
|
part3 = Q16_16.mul(m02, term3)
|
|
|
|
res = Q16_16.sub(part1, part2)
|
|
res = Q16_16.add(res, part3)
|
|
return res
|
|
|
|
@staticmethod
|
|
def det4x4(m) -> int:
|
|
c00 = Cramer4D.det3x3(m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], m[3][3])
|
|
c01 = Cramer4D.det3x3(m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], m[3][3])
|
|
c02 = Cramer4D.det3x3(m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], m[3][3])
|
|
c03 = Cramer4D.det3x3(m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2])
|
|
|
|
part0 = Q16_16.mul(m[0][0], c00)
|
|
part1 = Q16_16.mul(m[0][1], c01)
|
|
part2 = Q16_16.mul(m[0][2], c02)
|
|
part3 = Q16_16.mul(m[0][3], c03)
|
|
|
|
res = Q16_16.sub(part0, part1)
|
|
res = Q16_16.add(res, part2)
|
|
res = Q16_16.sub(res, part3)
|
|
return res
|
|
|
|
@staticmethod
|
|
def solve(a, b) -> list:
|
|
detA = Cramer4D.det4x4(a)
|
|
if detA == 0:
|
|
return [0, 0, 0, 0]
|
|
|
|
x = [0, 0, 0, 0]
|
|
for col in range(4):
|
|
a_sub = [row[:] for row in a]
|
|
for row in range(4):
|
|
a_sub[row][col] = b[row]
|
|
detA_sub = Cramer4D.det4x4(a_sub)
|
|
x[col] = Q16_16.div(detA_sub, detA)
|
|
return x
|
|
|
|
def run_simulation(grid_size: int, steps: int):
|
|
# Primes to construct folded prime potential lattice
|
|
primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
|
|
|
|
# 1. Precompute Folded-Prime Potential lattice on grid
|
|
phi_p = Q16Array.zeros(grid_size)
|
|
for i in range(grid_size):
|
|
val = 0
|
|
for p in primes:
|
|
# Distance to nearest multiple of prime
|
|
dist = abs((i % p) - p / 2)
|
|
# Add to potential (fixed-point math)
|
|
val = Q16_16.add(val, Q16_16.from_float(1.0 / (dist + 1.0)))
|
|
phi_p[i] = val
|
|
|
|
# Gradient of prime potential
|
|
grad_phi_p = Q16Array.grad(phi_p)
|
|
|
|
# Initialize variables
|
|
u = Q16Array.zeros(grid_size) # Velocity
|
|
q = Q16Array.zeros(grid_size) # Front Charge / Admissibility
|
|
r = Q16Array.zeros(grid_size) # Reaction tail
|
|
b = Q16Array.zeros(grid_size) # Underverse bleed ledger
|
|
|
|
# Initialize a localized initial pressure (Gaussian-like packet in Q16_16)
|
|
for i in range(grid_size):
|
|
dist = min(abs(i - grid_size // 4), grid_size - abs(i - grid_size // 4))
|
|
u[i] = Q16_16.from_float(2.0 / (1.0 + 0.1 * dist * dist))
|
|
q[i] = Q16_16.from_float(1.5 / (1.0 + 0.1 * dist * dist))
|
|
|
|
# Constant coefficients in Q16_16
|
|
nu = Q16_16.from_float(0.05) # Viscosity
|
|
beta = Q16_16.from_float(0.01) # Soliton dispersion
|
|
kappa = Q16_16.from_float(0.15) # Charge self-reinforcement
|
|
lam = Q16_16.from_float(0.1) # Reaction bleed coefficient
|
|
dt = Q16_16.from_float(0.1) # Time step
|
|
gamma = Q16_16.from_float(0.08) # Drainage rate
|
|
|
|
underverse_total_payment = 0
|
|
entropy_total = 0
|
|
discarded_pressure_total = 0
|
|
|
|
# 2. Time Evolve
|
|
for step in range(steps):
|
|
# Calculate gradients
|
|
grad_u = Q16Array.grad(u)
|
|
lap_u = Q16Array.laplacian(u)
|
|
grad_lap_u = Q16Array.grad(lap_u)
|
|
|
|
grad_q = Q16Array.grad(q)
|
|
q_grad_q = [Q16_16.mul(qi, gqi) for qi, gqi in zip(q, grad_q)]
|
|
|
|
grad_r = Q16Array.grad(r)
|
|
|
|
# Burgers momentum update:
|
|
# du/dt = -u * grad(u) + nu * lap(u) - grad_phi_p + beta * grad_lap_u + kappa * q_grad_q - lam * grad_r
|
|
du = Q16Array.zeros(grid_size)
|
|
for i in range(grid_size):
|
|
term_advect = Q16_16.mul(u[i], grad_u[i])
|
|
term_visc = Q16_16.mul(nu, lap_u[i])
|
|
term_disp = Q16_16.mul(beta, grad_lap_u[i])
|
|
term_charge = Q16_16.mul(kappa, q_grad_q[i])
|
|
term_drain = Q16_16.mul(lam, grad_r[i])
|
|
|
|
val = Q16_16.sub(term_visc, term_advect)
|
|
val = Q16_16.sub(val, grad_phi_p[i])
|
|
val = Q16_16.add(val, term_disp)
|
|
val = Q16_16.add(val, term_charge)
|
|
val = Q16_16.sub(val, term_drain)
|
|
du[i] = val
|
|
|
|
# Update u
|
|
u_next = [Q16_16.add(ui, Q16_16.mul(dui, dt)) for ui, dui in zip(u, du)]
|
|
|
|
# Charge conservation update with decay at composite/scar lanes (where phi_p has local maxima)
|
|
# dq/dt = -grad(q * u) - bleed_loss
|
|
q_u = [Q16_16.mul(qi, ui) for qi, ui in zip(q, u)]
|
|
grad_qu = Q16Array.grad(q_u)
|
|
|
|
dq = Q16Array.zeros(grid_size)
|
|
for i in range(grid_size):
|
|
# Bleed loss is proportional to potential (impedance)
|
|
bleed_loss = Q16_16.mul(q[i], Q16_16.mul(phi_p[i], Q16_16.from_float(0.1)))
|
|
dq[i] = Q16_16.sub(Q16_16.neg(grad_qu[i]), bleed_loss)
|
|
|
|
# Record bleed in ledger
|
|
b[i] = Q16_16.add(b[i], Q16_16.mul(bleed_loss, dt))
|
|
underverse_total_payment += Q16_16.mul(bleed_loss, dt)
|
|
|
|
q_next = [Q16_16.add(qi, Q16_16.mul(dqi, dt)) for qi, dqi in zip(q, dq)]
|
|
|
|
# Reaction tail drainage
|
|
# dr/dt = bleed_loss - gamma * r
|
|
dr = Q16Array.zeros(grid_size)
|
|
for i in range(grid_size):
|
|
bleed_loss = Q16_16.mul(q[i], Q16_16.mul(phi_p[i], Q16_16.from_float(0.1)))
|
|
decay = Q16_16.mul(gamma, r[i])
|
|
dr[i] = Q16_16.sub(bleed_loss, decay)
|
|
|
|
r_next = [Q16_16.add(ri, Q16_16.mul(dri, dt)) for ri, dri in zip(r, dr)]
|
|
|
|
# DST: Dimensional Shock Trim - keep only high-amplitude front
|
|
# Dimensions with u < 0.1 are trimmed, adding to ledger
|
|
for i in range(grid_size):
|
|
if u_next[i] < Q16_16.from_float(0.1):
|
|
discarded_pressure_total += u_next[i]
|
|
u_next[i] = 0
|
|
q_next[i] = 0
|
|
r_next[i] = 0
|
|
|
|
# Calculate step entropy
|
|
for i in range(grid_size):
|
|
if q_next[i] > 0:
|
|
entropy_total += q_next[i] // 100
|
|
|
|
u = u_next
|
|
q = q_next
|
|
r = r_next
|
|
|
|
# Extract surviving path
|
|
surviving_indices = [i for i, ui in enumerate(u) if ui > 0]
|
|
|
|
# Solve exact Cramer 4D linear system on first 4 active nodes
|
|
cramer_weights = [0, 0, 0, 0]
|
|
if len(surviving_indices) >= 4:
|
|
a_matrix = []
|
|
b_vector = []
|
|
for idx in surviving_indices[:4]:
|
|
a_matrix.append([
|
|
u[idx],
|
|
q[idx],
|
|
r[idx],
|
|
Q16_16.from_float(1.0)
|
|
])
|
|
b_vector.append(phi_p[idx])
|
|
cramer_weights = Cramer4D.solve(a_matrix, b_vector)
|
|
|
|
# Build metrics receipt
|
|
receipt = {
|
|
"schema": "braid_shock_16d_receipt_v1",
|
|
"grid_size": grid_size,
|
|
"steps": steps,
|
|
"underverse_ledger": {
|
|
"total_bleed_payment_q16": underverse_total_payment,
|
|
"discarded_pressure_q16": discarded_pressure_total,
|
|
"entropy_acc_q16": entropy_total,
|
|
"final_surviving_front_nodes": len(surviving_indices)
|
|
},
|
|
"cramer_4d_alignment_weights_q16": cramer_weights,
|
|
"surviving_path": surviving_indices,
|
|
"u_final_q16": u,
|
|
"q_final_q16": q,
|
|
"r_final_q16": r,
|
|
"b_final_q16": b
|
|
}
|
|
|
|
# Self-validation check
|
|
data_str = json.dumps(receipt, sort_keys=True)
|
|
receipt_hash = hashlib.sha256(data_str.encode("utf-8")).hexdigest()
|
|
receipt["receipt_sha256"] = receipt_hash
|
|
|
|
return receipt
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="16D BraidShock Simulation")
|
|
parser.parse_arguments = parser.add_argument("--output", type=str, required=True, help="Output JSON receipt path")
|
|
parser.parse_arguments = parser.add_argument("--grid-size", type=int, default=256, help="Simulation grid size")
|
|
parser.parse_arguments = parser.add_argument("--steps", type=int, default=100, help="Simulation steps")
|
|
|
|
args = parser.parse_args()
|
|
|
|
print(f"Initializing BraidShock PrimeFold 16D Simulation (grid_size={args.grid_size}, steps={args.steps})...")
|
|
receipt = run_simulation(args.grid_size, args.steps)
|
|
|
|
os.makedirs(os.path.dirname(args.output), exist_ok=True)
|
|
with open(args.output, "w") as f:
|
|
json.dump(receipt, f, indent=2)
|
|
|
|
print(f"Simulation complete. Receipt written to {args.output}")
|
|
print(f"Receipt SHA256: {receipt['receipt_sha256']}")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|