| """Simulator vs Chip Comparison Benchmark
|
| ==========================================
|
| Demonstrates both backends with the same network, comparing spike counts.
|
|
|
| When no FPGA is connected, runs simulator-only and shows expected chip commands.
|
|
|
| Features demonstrated: Backend abstraction, deploy/inject/run API, RunResult.
|
| """
|
|
|
| import sys, os, time
|
| sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
|
|
| import neurocore as nc
|
| from neurocore.compiler import Compiler
|
|
|
|
|
| def build_network():
|
| """Build a moderately complex E/I network."""
|
| net = nc.Network()
|
| exc = net.population(64, params={
|
| "threshold": 800, "leak": 5, "refrac": 3,
|
| }, label="excitatory")
|
| inh = net.population(16, params={
|
| "threshold": 600, "leak": 2, "refrac": 2,
|
| }, label="inhibitory")
|
|
|
| net.connect(exc, exc, topology="random_sparse", p=0.1, weight=200, seed=42)
|
| net.connect(exc, inh, topology="all_to_all", weight=150)
|
| net.connect(inh, exc, topology="all_to_all", weight=-300, compartment=0)
|
|
|
| return net, exc, inh
|
|
|
|
|
| def run_simulator(net, exc, inh, timesteps=100):
|
| """Run on the software simulator."""
|
| sim = nc.Simulator()
|
| sim.deploy(net)
|
|
|
|
|
| sim.inject(exc[:8], current=1200)
|
| result = sim.run(timesteps)
|
| return result
|
|
|
|
|
| def main():
|
| print("=" * 60)
|
| print(" Simulator vs Chip Comparison Benchmark")
|
| print("=" * 60)
|
|
|
| net, exc, inh = build_network()
|
| timesteps = 100
|
|
|
|
|
| compiled = Compiler().compile(net)
|
| print(f"\nNetwork: {net.total_neurons()} neurons "
|
| f"({net.populations[0].size} exc + {net.populations[1].size} inh)")
|
| print(f"Compiled: {compiled.summary()}")
|
|
|
|
|
| print(f"\n--- Simulator ({timesteps} timesteps) ---")
|
| t0 = time.perf_counter()
|
| result = run_simulator(net, exc, inh, timesteps)
|
| elapsed = time.perf_counter() - t0
|
|
|
| print(f"Total spikes: {result.total_spikes}")
|
| print(f"Active neurons: {len(result.spike_trains)}/{net.total_neurons()}")
|
| print(f"Elapsed: {elapsed * 1000:.1f}ms")
|
|
|
| rates = result.firing_rates()
|
| if rates:
|
| max_rate = max(rates.values())
|
| avg_rate = sum(rates.values()) / len(rates)
|
| print(f"Max firing rate: {max_rate:.2f} Hz")
|
| print(f"Avg firing rate: {avg_rate:.2f} Hz (active neurons only)")
|
|
|
| timeseries = result.spike_count_timeseries()
|
| peak_t = max(range(len(timeseries)), key=lambda i: timeseries[i])
|
| print(f"Peak activity: timestep {peak_t} ({timeseries[peak_t]} spikes)")
|
|
|
|
|
| print(f"\n--- Chip Commands (would be sent via UART) ---")
|
| print(f"PROG_NEURON commands: {len(compiled.prog_neuron_cmds)}")
|
| print(f"PROG_INDEX commands: {len(compiled.prog_index_cmds)}")
|
| print(f"PROG_POOL commands: {len(compiled.prog_pool_cmds)}")
|
| print(f"PROG_ROUTE commands: {len(compiled.prog_route_cmds)}")
|
| print(f"PROG_DELAY commands: {len(compiled.prog_delay_cmds)}")
|
| total_bytes = (len(compiled.prog_neuron_cmds) * 7
|
| + len(compiled.prog_index_cmds) * 10
|
| + len(compiled.prog_pool_cmds) * 9
|
| + len(compiled.prog_route_cmds) * 10)
|
| print(f"Total deploy payload: ~{total_bytes} bytes")
|
|
|
|
|
| print(f"\n--- Chip Backend ---")
|
| try:
|
| chip = nc.Chip(port="COM3")
|
| chip.deploy(net)
|
| chip.inject(exc[:8], current=1200)
|
| chip_result = chip.run(timesteps)
|
| print(f"Chip spikes: {chip_result.total_spikes}")
|
| print(f"Match: {'YES' if chip_result.total_spikes == result.total_spikes else 'NO'}")
|
| chip.close()
|
| except Exception as e:
|
| print(f"No FPGA connected ({type(e).__name__})")
|
| print(" Run with --port <port> when FPGA is attached")
|
|
|
| print("\nDone!")
|
|
|
|
|
| if __name__ == "__main__":
|
| main()
|
|
|