mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
257 lines
9.8 KiB
Python
257 lines
9.8 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
rgflow_gpu_pipeline.py — WebGPU compute dispatch for the RGFlow adaptation surface.
|
||
|
||
Uses wgpu (Python WebGPU bindings) with Vulkan backend to fill a 262,144-entry
|
||
adaptation surface on the GPU. Each entry is 28 bytes (7 × u32):
|
||
flags, cost, margin, rg_depth, recovery_depth, attractor_id, failure_mask
|
||
|
||
Outputs:
|
||
5-Applications/out/rgflow_adaptation_surface.bin — raw binary (7,340,032 bytes)
|
||
5-Applications/out/rgflow_adaptation_surface.json — summary + sample entries
|
||
"""
|
||
|
||
import json
|
||
import struct
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
import numpy as np
|
||
import wgpu
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════
|
||
# Configuration
|
||
# ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
ADDR_SPACE = 262_144
|
||
ENTRY_BYTES = 7 * 4 # 7 × u32
|
||
TOTAL_BYTES = ADDR_SPACE * ENTRY_BYTES
|
||
WORKGROUP_SIZE = 64
|
||
NUM_WORKGROUPS = (ADDR_SPACE + WORKGROUP_SIZE - 1) // WORKGROUP_SIZE
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════
|
||
# WGSL shader source
|
||
# ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
WGSL_PATH = Path(__file__).parent / "rgflow_compute.wgsl"
|
||
WGSL_CODE = WGSL_PATH.read_text() if WGSL_PATH.exists() else ""
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════
|
||
# WebGPU dispatch
|
||
# ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
|
||
def dispatch_compute() -> np.ndarray:
|
||
"""Create WebGPU device, dispatch compute shader, read back results."""
|
||
print("[INFO] Requesting WebGPU adapter (Vulkan backend)...")
|
||
adapter = wgpu.gpu.request_adapter_sync(power_preference="high-performance")
|
||
if adapter is None:
|
||
raise RuntimeError("No GPU adapter found.")
|
||
print(f"[INFO] Adapter: {adapter.summary}")
|
||
|
||
print("[INFO] Creating device...")
|
||
device = adapter.request_device_sync()
|
||
|
||
print("[INFO] Loading WGSL compute shader...")
|
||
shader_module = device.create_shader_module(code=WGSL_CODE)
|
||
|
||
# Create output storage buffer (GPU-only, writable by shader)
|
||
print("[INFO] Creating GPU buffers...")
|
||
output_buf = device.create_buffer(
|
||
size=TOTAL_BYTES,
|
||
usage=wgpu.BufferUsage.STORAGE | wgpu.BufferUsage.COPY_SRC,
|
||
)
|
||
|
||
# Staging buffer for CPU readback (GPU→CPU copy destination)
|
||
staging_buf = device.create_buffer(
|
||
size=TOTAL_BYTES,
|
||
usage=wgpu.BufferUsage.MAP_READ | wgpu.BufferUsage.COPY_DST,
|
||
)
|
||
|
||
# Bind group layout and pipeline
|
||
bind_group_layout = device.create_bind_group_layout(
|
||
entries=[
|
||
{
|
||
"binding": 0,
|
||
"visibility": wgpu.ShaderStage.COMPUTE,
|
||
"buffer": {"type": wgpu.BufferBindingType.storage},
|
||
}
|
||
]
|
||
)
|
||
|
||
pipeline_layout = device.create_pipeline_layout(
|
||
bind_group_layouts=[bind_group_layout]
|
||
)
|
||
|
||
compute_pipeline = device.create_compute_pipeline(
|
||
layout=pipeline_layout,
|
||
compute={
|
||
"module": shader_module,
|
||
"entry_point": "main",
|
||
},
|
||
)
|
||
|
||
bind_group = device.create_bind_group(
|
||
layout=bind_group_layout,
|
||
entries=[
|
||
{
|
||
"binding": 0,
|
||
"resource": {"buffer": output_buf, "offset": 0, "size": TOTAL_BYTES},
|
||
}
|
||
],
|
||
)
|
||
|
||
# Encode commands
|
||
print(f"[INFO] Dispatching {NUM_WORKGROUPS} workgroups × {WORKGROUP_SIZE} threads...")
|
||
encoder = device.create_command_encoder()
|
||
compute_pass = encoder.begin_compute_pass()
|
||
compute_pass.set_pipeline(compute_pipeline)
|
||
compute_pass.set_bind_group(0, bind_group, [], 0, 999999)
|
||
compute_pass.dispatch_workgroups(NUM_WORKGROUPS)
|
||
compute_pass.end()
|
||
|
||
# Copy output → staging
|
||
encoder.copy_buffer_to_buffer(
|
||
source=output_buf, source_offset=0,
|
||
destination=staging_buf, destination_offset=0,
|
||
size=TOTAL_BYTES,
|
||
)
|
||
|
||
# Submit
|
||
command_buffer = encoder.finish()
|
||
device.queue.submit([command_buffer])
|
||
|
||
# Read back from staging buffer
|
||
print("[INFO] Reading back GPU results...")
|
||
staging_buf.map_sync(mode=wgpu.MapMode.READ)
|
||
mapped = staging_buf.read_mapped()
|
||
result = np.frombuffer(mapped, dtype=np.uint32).copy()
|
||
staging_buf.unmap()
|
||
|
||
return result
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════
|
||
# Post-processing & serialization
|
||
# ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
|
||
def unpack_entries(flat: np.ndarray) -> list:
|
||
"""Unpack flat u32 array into list of dict entries."""
|
||
entries = []
|
||
for i in range(ADDR_SPACE):
|
||
base = i * 7
|
||
flags = int(flat[base + 0])
|
||
entries.append(
|
||
{
|
||
"address": i,
|
||
"lawful_now": bool(flags & 1),
|
||
"lawful_flow": bool(flags & 2),
|
||
"lawful_attractor": bool(flags & 4),
|
||
"noise_flow": bool(flags & 8),
|
||
"sabotage_flow": bool(flags & 16),
|
||
"final_lawful": bool(flags & 32),
|
||
"cost": int(flat[base + 1]),
|
||
"margin": int(flat[base + 2]),
|
||
"rg_depth": int(flat[base + 3]),
|
||
"recovery_depth": int(flat[base + 4]),
|
||
"attractor_id": int(flat[base + 5]),
|
||
"failure_mask": f"0x{int(flat[base + 6]):04X}",
|
||
}
|
||
)
|
||
return entries
|
||
|
||
|
||
def save_results(flat: np.ndarray, out_dir: Path):
|
||
"""Save binary and JSON outputs."""
|
||
out_dir.mkdir(parents=True, exist_ok=True)
|
||
|
||
# Raw binary
|
||
bin_path = out_dir / "rgflow_adaptation_surface.bin"
|
||
with open(bin_path, "wb") as f:
|
||
f.write(flat.tobytes())
|
||
print(f"[OK] Binary surface written: {bin_path} ({bin_path.stat().st_size:,} bytes)")
|
||
|
||
# JSON summary
|
||
entries = unpack_entries(flat)
|
||
|
||
stats = {
|
||
"lawful_now": sum(1 for e in entries if e["lawful_now"]),
|
||
"lawful_flow": sum(1 for e in entries if e["lawful_flow"]),
|
||
"lawful_attractor": sum(1 for e in entries if e["lawful_attractor"]),
|
||
"noise_flow": sum(1 for e in entries if e["noise_flow"]),
|
||
"sabotage_flow": sum(1 for e in entries if e["sabotage_flow"]),
|
||
"final_lawful": sum(1 for e in entries if e["final_lawful"]),
|
||
}
|
||
|
||
# Depth distribution
|
||
depth_counts = {}
|
||
recovery_counts = {}
|
||
for e in entries:
|
||
d = e["rg_depth"]
|
||
depth_counts[d] = depth_counts.get(d, 0) + 1
|
||
r = e["recovery_depth"]
|
||
if r > 0:
|
||
recovery_counts[r] = recovery_counts.get(r, 0) + 1
|
||
|
||
json_path = out_dir / "rgflow_adaptation_surface.json"
|
||
with open(json_path, "w") as f:
|
||
json.dump(
|
||
{
|
||
"meta": {
|
||
"addr_space": ADDR_SPACE,
|
||
"entry_bytes": ENTRY_BYTES,
|
||
"total_bytes": TOTAL_BYTES,
|
||
"workgroups": NUM_WORKGROUPS,
|
||
"workgroup_size": WORKGROUP_SIZE,
|
||
},
|
||
"statistics": stats,
|
||
"statistics_fractions": {
|
||
k: round(v / ADDR_SPACE, 4) for k, v in stats.items()
|
||
},
|
||
"depth_distribution": depth_counts,
|
||
"recovery_distribution": recovery_counts,
|
||
"sample_entries": entries[:10] + entries[ADDR_SPACE // 2 : ADDR_SPACE // 2 + 5],
|
||
},
|
||
f,
|
||
indent=2,
|
||
)
|
||
print(f"[OK] JSON summary written: {json_path}")
|
||
|
||
# Print stats
|
||
print("\n=== RGFlow Adaptation Surface Statistics ===")
|
||
for k, v in stats.items():
|
||
pct = 100.0 * v / ADDR_SPACE
|
||
print(f" {k:20s}: {v:6d} / {ADDR_SPACE} ({pct:5.2f}%)")
|
||
|
||
print("\n=== Depth Distribution ===")
|
||
for d in sorted(depth_counts.keys()):
|
||
print(f" depth={d}: {depth_counts[d]:6d}")
|
||
|
||
print("\n=== Recovery Distribution ===")
|
||
for r in sorted(recovery_counts.keys()):
|
||
print(f" recovery_at={r}: {recovery_counts[r]:6d}")
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════
|
||
# Main
|
||
# ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
|
||
def main():
|
||
if not WGSL_CODE:
|
||
print(f"[ERROR] WGSL shader not found at {WGSL_PATH}", file=sys.stderr)
|
||
sys.exit(1)
|
||
|
||
print("=" * 60)
|
||
print("Sovereign Informatic Manifold — RGFlow GPU Pipeline")
|
||
print("=" * 60)
|
||
|
||
flat = dispatch_compute()
|
||
save_results(flat, Path("out"))
|
||
|
||
print("\n[OK] RGFlow adaptation surface complete.")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|