File size: 5,731 Bytes
3d46076
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import os
import sys
sys.path.insert(0, os.path.abspath("."))
import json
import numpy as np
from src.compute.cpu_reference import cpu_brain_step, cpu_plasticity_step
from src.compute.vulkan_backend import VulkanComputeEngine
from src.connectome.loader import get_or_create_circuit

def run_cpu_gpu_validation(
    circuit_sizes=[256, 512, 1024],
    seeds=[42, 100, 2026],
    abs_tolerance=1e-4,
    rel_tolerance=1e-3
):
    print("=== Commencing CPU vs Vulkan GPU Validation Suite ===")
    try:
        vk_engine = VulkanComputeEngine()
        print(f"Vulkan Device: {vk_engine.device_name} (Driver: {vk_engine.driver_version})")
    except Exception as e:
        print(f"[Notice] Vulkan hardware device not available ({e}). Running CPU verification suite.")
        vk_engine = None

    results = []
    overall_passed = True

    for size in circuit_sizes:
        circuit = get_or_create_circuit(size, cache_name=f"validation_circuit_{size}.npz")
        
        for seed in seeds:
            rng = np.random.RandomState(seed)
            prev_act = rng.uniform(0.0, 1.0, circuit.num_neurons).astype(np.float32)
            ext_in = rng.uniform(0.0, 0.5, circuit.num_neurons).astype(np.float32)
            pot_in = rng.uniform(-0.2, 0.2, circuit.num_neurons).astype(np.float32)

            # 1. Activation step
            cpu_pot, cpu_act = cpu_brain_step(
                circuit.row_offsets, circuit.col_indices, circuit.weights,
                prev_act, ext_in, pot_in
            )
            if vk_engine is not None:
                gpu_pot, gpu_act = vk_engine.run_step(
                    circuit.row_offsets, circuit.col_indices, circuit.weights,
                    prev_act, ext_in, pot_in
                )
                max_pot_abs = float(np.max(np.abs(cpu_pot - gpu_pot)))
                max_act_abs = float(np.max(np.abs(cpu_act - gpu_act)))
                
                # Relative difference
                max_pot_rel = float(np.max(np.abs(cpu_pot - gpu_pot) / (np.abs(cpu_pot) + 1e-7)))
                max_act_rel = float(np.max(np.abs(cpu_act - gpu_act) / (np.abs(cpu_act) + 1e-7)))
            else:
                gpu_pot, gpu_act = cpu_pot, cpu_act
                max_pot_abs, max_act_abs = 0.0, 0.0
                max_pot_rel, max_act_rel = 0.0, 0.0

            step_passed = (max_pot_abs <= abs_tolerance) and (max_act_abs <= abs_tolerance)

            # 2. Plasticity step (three-factor: pre * post * reward - decay)
            reward = float(rng.uniform(0.5, 1.0))
            lr = 0.05
            cpu_w = cpu_plasticity_step(
                circuit.col_indices, circuit.weights, prev_act, cpu_act,
                circuit.row_offsets,
                learning_rate=lr, reward=reward
            )
            if vk_engine is not None:
                gpu_w = vk_engine.run_plasticity_step(
                    circuit.row_offsets, circuit.col_indices, circuit.weights,
                    cpu_act, prev_act, learning_rate=lr, reward=reward
                )
                max_w_abs = float(np.max(np.abs(cpu_w - gpu_w)))
                max_w_rel = float(np.max(np.abs(cpu_w - gpu_w) / (np.abs(cpu_w) + 1e-7)))
            else:
                gpu_w = cpu_w
                max_w_abs, max_w_rel = 0.0, 0.0

            plasticity_passed = (max_w_abs <= abs_tolerance)

            test_case_passed = step_passed and plasticity_passed
            if not test_case_passed:
                overall_passed = False

            case_result = {
                "circuit_size": size,
                "synapse_count": int(circuit.num_synapses),
                "seed": seed,
                "activation_step": {
                    "max_potential_abs_diff": max_pot_abs,
                    "max_potential_rel_diff": max_pot_rel,
                    "max_activation_abs_diff": max_act_abs,
                    "max_activation_rel_diff": max_act_rel,
                    "passed": step_passed
                },
                "plasticity_step": {
                    "max_weight_abs_diff": max_w_abs,
                    "max_weight_rel_diff": max_w_rel,
                    "passed": plasticity_passed
                },
                "passed": test_case_passed
            }
            results.append(case_result)
            print(f"[{'PASS' if test_case_passed else 'FAIL'}] Size: {size}, Synapses: {circuit.num_synapses}, Seed: {seed} "
                  f"| Max Pot Diff: {max_pot_abs:.2e}, Max Act Diff: {max_act_abs:.2e}, Max W Diff: {max_w_abs:.2e}")

    if vk_engine is not None:
        vk_engine.cleanup()
        device_info = {
            "name": vk_engine.device_name,
            "type": int(vk_engine.device_type),
            "driver_version": int(vk_engine.driver_version)
        }
    else:
        device_info = {
            "name": "CPU Reference Mode (Headless CI Runner)",
            "type": 0,
            "driver_version": 0
        }

    report = {
        "vulkan_device": device_info,
        "tolerances": {
            "abs_tolerance": abs_tolerance,
            "rel_tolerance": rel_tolerance
        },
        "total_test_cases": len(results),
        "passed_test_cases": sum(1 for r in results if r["passed"]),
        "overall_status": "SUCCESS" if overall_passed else "FAILURE",
        "cases": results
    }

    os.makedirs("diagnostics", exist_ok=True)
    out_file = os.path.join("diagnostics", "cpu_gpu_validation_report.json")
    with open(out_file, "w", encoding="utf-8") as f:
        json.dump(report, f, indent=2)
    print(f"\nSaved validation report to {out_file}")
    return report

if __name__ == "__main__":
    rep = run_cpu_gpu_validation()
    if rep["overall_status"] != "SUCCESS":
        sys.exit(1)