| | """Visualize async vs sync mode β the key P12 feature."""
|
| |
|
| | import sys
|
| | sys.path.insert(0, r"C:\Users\mrwab\neuromorphic-chip\sdk")
|
| |
|
| | import matplotlib
|
| | matplotlib.use("Agg")
|
| | import matplotlib.pyplot as plt
|
| | import matplotlib.gridspec as gridspec
|
| | import matplotlib.patches as mpatches
|
| | import numpy as np
|
| | from collections import defaultdict
|
| |
|
| | import neurocore as nc
|
| | from neurocore.result import RunResult
|
| | from neurocore.constants import NEURONS_PER_CORE
|
| |
|
| | BG = "#0a0a1a"
|
| | PANEL = "#0f1029"
|
| | TEXT = "#e0e0e0"
|
| | CYAN = "#00ffcc"
|
| | RED = "#ff6b6b"
|
| | GOLD = "#ffd93d"
|
| | BLUE = "#6bcfff"
|
| | PURPLE = "#c084fc"
|
| | GREEN = "#2ed573"
|
| |
|
| | def run_chain(async_mode):
|
| | net = nc.Network()
|
| | pops = []
|
| | for i in range(8):
|
| | p = net.population(1, params={"threshold": 100, "leak": 0, "refrac": 1},
|
| | label=f"N{i}")
|
| | pops.append(p)
|
| | for i in range(7):
|
| | net.connect(pops[i], pops[i+1], topology="all_to_all", weight=200)
|
| |
|
| | sim = nc.Simulator()
|
| | sim.deploy(net)
|
| | sim.set_learning(async_mode=async_mode)
|
| |
|
| | trains = defaultdict(list)
|
| | total = 0
|
| | for t in range(12):
|
| | if t == 0:
|
| | sim.inject(pops[0], current=200)
|
| | result = sim.run(1)
|
| | total += result.total_spikes
|
| | for gid, times in result.spike_trains.items():
|
| | trains[gid].extend([t])
|
| | return trains, total, sim._compiled.placement, pops
|
| |
|
| | sync_trains, sync_total, placement, pops = run_chain(False)
|
| | async_trains, async_total, _, _ = run_chain(True)
|
| |
|
| | def run_ei(async_mode, timesteps=150):
|
| | net = nc.Network()
|
| | exc = net.population(64, params={"threshold": 500, "leak": 2, "refrac": 2}, label="Excitatory")
|
| | inh = net.population(16, params={"threshold": 400, "leak": 2, "refrac": 2}, label="Inhibitory")
|
| | net.connect(exc, exc, topology="random_sparse", p=0.15, weight=300, seed=42)
|
| | net.connect(exc, inh, topology="fixed_fan_out", fan_out=16, weight=250, seed=42)
|
| | net.connect(inh, exc, topology="fixed_fan_out", fan_out=32, weight=-200, seed=42)
|
| |
|
| | sim = nc.Simulator()
|
| | sim.deploy(net)
|
| | sim.set_learning(async_mode=async_mode)
|
| |
|
| | trains = defaultdict(list)
|
| | counts = []
|
| | total = 0
|
| | for t in range(timesteps):
|
| | sim.inject(exc[:16], current=600)
|
| | result = sim.run(1)
|
| | total += result.total_spikes
|
| | counts.append(result.total_spikes)
|
| | for gid, times in result.spike_trains.items():
|
| | trains[gid].extend([t])
|
| | return dict(trains), counts, total, sim._compiled.placement, exc, inh
|
| |
|
| | sync_ei_trains, sync_ei_counts, sync_ei_total, ei_place, exc, inh = run_ei(False)
|
| | async_ei_trains, async_ei_counts, async_ei_total, _, _, _ = run_ei(True)
|
| |
|
| | fig = plt.figure(figsize=(22, 18), facecolor=BG)
|
| | fig.suptitle("NEUROCORE β Async Event-Driven Mode (Phase 12 GALS)",
|
| | fontsize=20, color=CYAN, fontweight="bold", fontfamily="monospace", y=0.98)
|
| | fig.text(0.5, 0.955, "Togglable via set_learning(async_mode=True) | "
|
| | "Cores fire only on pending spikes | Quiescence detection ends timestep",
|
| | ha="center", fontsize=9, color="#666", fontfamily="monospace")
|
| |
|
| | gs = gridspec.GridSpec(3, 2, figure=fig, hspace=0.32, wspace=0.25,
|
| | left=0.05, right=0.96, top=0.93, bottom=0.05)
|
| |
|
| | ax1 = fig.add_subplot(gs[0, 0])
|
| | ax1.set_facecolor(PANEL)
|
| | ax1.set_title("SYNC Mode β 8-Neuron Chain", color=TEXT, fontsize=12,
|
| | fontfamily="monospace", pad=10)
|
| |
|
| | for gid, times in sync_trains.items():
|
| | neuron = gid % NEURONS_PER_CORE
|
| | ax1.scatter(times, [neuron] * len(times), s=120, c=CYAN, marker="|", linewidths=2.5)
|
| | for t in times:
|
| | ax1.annotate(f"N{neuron}", (t + 0.15, neuron), fontsize=7, color="#888",
|
| | fontfamily="monospace")
|
| |
|
| | ax1.set_xlabel("Timestep", color=TEXT, fontsize=9, fontfamily="monospace")
|
| | ax1.set_ylabel("Neuron", color=TEXT, fontsize=9, fontfamily="monospace")
|
| | ax1.set_xlim(-0.5, 11.5)
|
| | ax1.set_ylim(-0.5, 7.5)
|
| | ax1.set_yticks(range(8))
|
| | ax1.set_yticklabels([f"N{i}" for i in range(8)])
|
| | ax1.tick_params(colors="#666", labelsize=8)
|
| | for spine in ax1.spines.values():
|
| | spine.set_color("#222")
|
| |
|
| |
|
| | ax1.annotate("", xy=(7.5, 7), xytext=(0.5, 0),
|
| | arrowprops=dict(arrowstyle="->", color=GOLD, lw=1.5, ls="--"))
|
| | ax1.text(5, 2.5, f"7 timesteps\n{sync_total} total spikes", fontsize=10,
|
| | color=GOLD, fontfamily="monospace", ha="center",
|
| | bbox=dict(boxstyle="round,pad=0.4", facecolor=PANEL, edgecolor=GOLD, alpha=0.8))
|
| |
|
| | ax2 = fig.add_subplot(gs[0, 1])
|
| | ax2.set_facecolor(PANEL)
|
| | ax2.set_title("ASYNC Mode β 8-Neuron Chain (same network)", color=TEXT, fontsize=12,
|
| | fontfamily="monospace", pad=10)
|
| |
|
| | for gid, times in async_trains.items():
|
| | neuron = gid % NEURONS_PER_CORE
|
| | ax2.scatter(times, [neuron] * len(times), s=120, c=GREEN, marker="|", linewidths=2.5)
|
| | for t in times:
|
| | ax2.annotate(f"N{neuron}", (t + 0.15, neuron), fontsize=7, color="#888",
|
| | fontfamily="monospace")
|
| |
|
| | ax2.set_xlabel("Timestep", color=TEXT, fontsize=9, fontfamily="monospace")
|
| | ax2.set_ylabel("Neuron", color=TEXT, fontsize=9, fontfamily="monospace")
|
| | ax2.set_xlim(-0.5, 11.5)
|
| | ax2.set_ylim(-0.5, 7.5)
|
| | ax2.set_yticks(range(8))
|
| | ax2.set_yticklabels([f"N{i}" for i in range(8)])
|
| | ax2.tick_params(colors="#666", labelsize=8)
|
| | for spine in ax2.spines.values():
|
| | spine.set_color("#222")
|
| |
|
| |
|
| | ax2.text(0.5, 4, f"1 timestep!\n{async_total} spikes\n(micro-steps)", fontsize=10,
|
| | color=GREEN, fontfamily="monospace", ha="center",
|
| | bbox=dict(boxstyle="round,pad=0.4", facecolor=PANEL, edgecolor=GREEN, alpha=0.8))
|
| |
|
| | ax3 = fig.add_subplot(gs[1, 0])
|
| | ax3.set_facecolor(PANEL)
|
| | ax3.set_title(f"SYNC E/I Network β {sync_ei_total:,} spikes / 150 ts",
|
| | color=TEXT, fontsize=12, fontfamily="monospace", pad=10)
|
| |
|
| | for gid, times in sync_ei_trains.items():
|
| | local = gid % NEURONS_PER_CORE
|
| | color = CYAN if local < 64 else RED
|
| | ax3.scatter(times, [gid] * len(times), s=0.6, c=color, marker="|", linewidths=0.3)
|
| |
|
| | ax3.set_xlabel("Timestep", color=TEXT, fontsize=9, fontfamily="monospace")
|
| | ax3.set_ylabel("Neuron ID", color=TEXT, fontsize=9, fontfamily="monospace")
|
| | ax3.tick_params(colors="#666", labelsize=7)
|
| | for spine in ax3.spines.values():
|
| | spine.set_color("#222")
|
| | exc_p = mpatches.Patch(color=CYAN, label="Exc")
|
| | inh_p = mpatches.Patch(color=RED, label="Inh")
|
| | ax3.legend(handles=[exc_p, inh_p], loc="upper right", fontsize=7,
|
| | facecolor=PANEL, edgecolor="#333", labelcolor=TEXT)
|
| |
|
| | ax4 = fig.add_subplot(gs[1, 1])
|
| | ax4.set_facecolor(PANEL)
|
| | ax4.set_title(f"ASYNC E/I Network β {async_ei_total:,} spikes / 150 ts",
|
| | color=TEXT, fontsize=12, fontfamily="monospace", pad=10)
|
| |
|
| | for gid, times in async_ei_trains.items():
|
| | local = gid % NEURONS_PER_CORE
|
| | color = GREEN if local < 64 else PURPLE
|
| | ax4.scatter(times, [gid] * len(times), s=0.6, c=color, marker="|", linewidths=0.3)
|
| |
|
| | ax4.set_xlabel("Timestep", color=TEXT, fontsize=9, fontfamily="monospace")
|
| | ax4.set_ylabel("Neuron ID", color=TEXT, fontsize=9, fontfamily="monospace")
|
| | ax4.tick_params(colors="#666", labelsize=7)
|
| | for spine in ax4.spines.values():
|
| | spine.set_color("#222")
|
| | exc_p2 = mpatches.Patch(color=GREEN, label="Exc (async)")
|
| | inh_p2 = mpatches.Patch(color=PURPLE, label="Inh (async)")
|
| | ax4.legend(handles=[exc_p2, inh_p2], loc="upper right", fontsize=7,
|
| | facecolor=PANEL, edgecolor="#333", labelcolor=TEXT)
|
| |
|
| | ax5 = fig.add_subplot(gs[2, 0])
|
| | ax5.set_facecolor(PANEL)
|
| | ax5.set_title("Network Activity β Sync vs Async", color=TEXT, fontsize=12,
|
| | fontfamily="monospace", pad=10)
|
| |
|
| | window = 5
|
| | sync_ma = np.convolve(sync_ei_counts, np.ones(window)/window, mode="valid")
|
| | async_ma = np.convolve(async_ei_counts, np.ones(window)/window, mode="valid")
|
| | x = range(window - 1, 150)
|
| |
|
| | ax5.fill_between(x, sync_ma, alpha=0.15, color=CYAN)
|
| | ax5.plot(x, sync_ma, color=CYAN, lw=1.5, label=f"Sync ({sync_ei_total:,} spikes)")
|
| | ax5.fill_between(x, async_ma, alpha=0.15, color=GREEN)
|
| | ax5.plot(x, async_ma, color=GREEN, lw=1.5, label=f"Async ({async_ei_total:,} spikes)")
|
| |
|
| | ax5.set_xlabel("Timestep", color=TEXT, fontsize=9, fontfamily="monospace")
|
| | ax5.set_ylabel("Spikes / ts (5-pt avg)", color=TEXT, fontsize=9, fontfamily="monospace")
|
| | ax5.tick_params(colors="#666", labelsize=7)
|
| | ax5.legend(fontsize=8, facecolor=PANEL, edgecolor="#333", labelcolor=TEXT)
|
| | for spine in ax5.spines.values():
|
| | spine.set_color("#222")
|
| |
|
| | ax6 = fig.add_subplot(gs[2, 1])
|
| | ax6.set_facecolor(PANEL)
|
| | ax6.set_title("P12 Async Architecture β GALS Event Loop", color=TEXT, fontsize=12,
|
| | fontfamily="monospace", pad=10)
|
| | ax6.set_xlim(0, 10)
|
| | ax6.set_ylim(0, 8)
|
| | ax6.axis("off")
|
| |
|
| |
|
| | boxes = [
|
| | (5, 7, "IDLE", "#555"),
|
| | (5, 5.5, "ASYNC_ACTIVE\n(main loop)", GREEN),
|
| | (1.5, 3.5, "INJECT\n(drain PCIF)", BLUE),
|
| | (5, 3.5, "ROUTE\n(inter-core)", GOLD),
|
| | (8.5, 3.5, "RESTART\n(deferred)", PURPLE),
|
| | (5, 1.5, "QUIESCENT\n(timestep done)", CYAN),
|
| | ]
|
| |
|
| | for bx, by, label, color in boxes:
|
| | rect = mpatches.FancyBboxPatch((bx - 1.1, by - 0.55), 2.2, 1.1,
|
| | boxstyle="round,pad=0.15",
|
| | facecolor=color, alpha=0.15,
|
| | edgecolor=color, linewidth=1.5)
|
| | ax6.add_patch(rect)
|
| | ax6.text(bx, by, label, ha="center", va="center", fontsize=7.5,
|
| | color=color, fontweight="bold", fontfamily="monospace")
|
| |
|
| |
|
| | arrow_style = dict(arrowstyle="->", lw=1.2)
|
| | arrows = [
|
| | ((5, 6.4), (5, 6.1), "#555"),
|
| | ((3.8, 5.2), (2.6, 4.1), BLUE),
|
| | ((5, 4.9), (5, 4.1), GOLD),
|
| | ((6.2, 5.2), (7.4, 4.1), PURPLE),
|
| | ((2.6, 3.0), (3.8, 5.0), BLUE),
|
| | ((4.0, 3.8), (3.8, 5.0), GOLD),
|
| | ((7.4, 3.0), (6.2, 5.0), PURPLE),
|
| | ((5, 4.9), (5, 2.1), CYAN),
|
| | ]
|
| |
|
| | for start, end, color in arrows:
|
| | ax6.annotate("", xy=end, xytext=start,
|
| | arrowprops=dict(arrowstyle="->", color=color, lw=1.2))
|
| |
|
| |
|
| | ax6.text(2.2, 4.8, "PCIF\nnon-empty", fontsize=6, color=BLUE,
|
| | fontfamily="monospace", ha="center")
|
| | ax6.text(5.7, 4.5, "capture\nFIFO", fontsize=6, color=GOLD,
|
| | fontfamily="monospace", ha="center")
|
| | ax6.text(7.8, 4.8, "core\nspiked", fontsize=6, color=PURPLE,
|
| | fontfamily="monospace", ha="center")
|
| | ax6.text(3.8, 2.3, "all quiet", fontsize=6, color=CYAN,
|
| | fontfamily="monospace", ha="center")
|
| |
|
| |
|
| | ax6.text(5, 0.5,
|
| | "Key: chains collapse into micro-steps within 1 timestep\n"
|
| | "Quiescence = all cores idle + no restarts + all FIFOs empty",
|
| | ha="center", va="center", fontsize=7, color="#888",
|
| | fontfamily="monospace", style="italic",
|
| | bbox=dict(boxstyle="round,pad=0.4", facecolor="#0a0a1a",
|
| | edgecolor="#333", alpha=0.8))
|
| |
|
| |
|
| | output = r"C:\Users\mrwab\neuromorphic-chip\sdk\async_dashboard.png"
|
| | plt.savefig(output, dpi=180, facecolor=BG, bbox_inches="tight")
|
| | plt.close()
|
| | print(f"Saved to: {output}")
|
| |
|