| """Visualize P13 Loihi Parity features — CSR pool, multicast, 3-factor learning."""
|
|
|
| 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 matplotlib.patheffects as pe
|
| import numpy as np
|
| from collections import defaultdict
|
|
|
| import neurocore as nc
|
| from neurocore.result import RunResult
|
| from neurocore.constants import NEURONS_PER_CORE, POOL_DEPTH, ROUTE_FANOUT
|
|
|
| BG = "#0a0a1a"
|
| PANEL = "#0f1029"
|
| TEXT = "#e0e0e0"
|
| CYAN = "#00ffcc"
|
| RED = "#ff6b6b"
|
| GOLD = "#ffd93d"
|
| BLUE = "#6bcfff"
|
| PURPLE = "#c084fc"
|
| GREEN = "#2ed573"
|
| ORANGE = "#ff9f43"
|
| PINK = "#ff6b9d"
|
|
|
| print("Running CSR pool demo...")
|
| net_csr = nc.Network()
|
| hub = net_csr.population(1, params={"threshold": 100, "leak": 0, "refrac": 1}, label="Hub")
|
| fan_out_pop = net_csr.population(100, params={"threshold": 100, "leak": 0, "refrac": 1}, label="Fan-out targets")
|
| sparse_src = net_csr.population(50, params={"threshold": 100, "leak": 0, "refrac": 1}, label="Sparse sources")
|
|
|
| net_csr.connect(hub, fan_out_pop, topology="all_to_all", weight=200)
|
|
|
| net_csr.connect(sparse_src, fan_out_pop, topology="fixed_fan_out", fan_out=3, weight=150, seed=42)
|
|
|
| sim_csr = nc.Simulator()
|
| sim_csr.deploy(net_csr)
|
| compiled = sim_csr._compiled
|
|
|
|
|
| fanout_per_neuron = {}
|
| for cmd in compiled.prog_index_cmds:
|
| fanout_per_neuron[cmd["neuron"]] = cmd["count"]
|
|
|
|
|
| csr_trains = defaultdict(list)
|
| csr_total = 0
|
| for t in range(30):
|
| if t < 3:
|
| sim_csr.inject(hub, current=200)
|
| sim_csr.inject(sparse_src[:10], current=200)
|
| result = sim_csr.run(1)
|
| csr_total += result.total_spikes
|
| for gid, times in result.spike_trains.items():
|
| csr_trains[gid].extend([t])
|
|
|
| print("Running multicast routing demo...")
|
| net_mcast = nc.Network()
|
| src_core = net_mcast.population(NEURONS_PER_CORE, params={"threshold": 100, "leak": 0, "refrac": 2},
|
| label="Source core")
|
| targets = []
|
| for i in range(6):
|
|
|
| t = net_mcast.population(1, params={"threshold": 100, "leak": 0, "refrac": 2},
|
| label=f"Target {i}")
|
| targets.append(t)
|
| net_mcast.connect(src_core, t, topology="all_to_all", weight=200)
|
|
|
| sim_mcast = nc.Simulator()
|
| sim_mcast.deploy(net_mcast)
|
| mcast_compiled = sim_mcast._compiled
|
|
|
|
|
| routes_per_src = defaultdict(int)
|
| for cmd in mcast_compiled.prog_route_cmds:
|
| routes_per_src[cmd["src_neuron"]] += 1
|
|
|
| print("Running 3-factor learning demo...")
|
|
|
| def run_3factor(reward_time, reward_value, label):
|
| net = nc.Network()
|
| pre = net.population(1, params={"threshold": 100, "leak": 0, "refrac": 2}, label="Pre")
|
| post = net.population(1, params={"threshold": 100, "leak": 0, "refrac": 2}, label="Post")
|
| net.connect(pre, post, topology="all_to_all", weight=500)
|
|
|
| sim = nc.Simulator()
|
| sim.deploy(net)
|
| sim.set_learning(learn=True, three_factor=True)
|
|
|
| weights_over_time = []
|
| elig_over_time = []
|
|
|
| for t in range(60):
|
|
|
| if t % 8 == 0 and t < 40:
|
| sim.inject(pre, current=200)
|
| if t % 8 == 2 and t < 40:
|
| sim.inject(post, current=200)
|
|
|
|
|
| if t == reward_time:
|
| sim.reward(reward_value)
|
|
|
| sim.run(1)
|
|
|
|
|
| w = 500
|
| for targets in sim._adjacency.values():
|
| for _, wt, _ in targets:
|
| w = wt
|
| weights_over_time.append(w)
|
|
|
|
|
| total_elig = sum(abs(v) for v in sim._eligibility.values())
|
| elig_over_time.append(total_elig)
|
|
|
| return weights_over_time, elig_over_time
|
|
|
|
|
| w_pos, e_pos = run_3factor(20, 800, "Positive reward")
|
|
|
| w_neg, e_neg = run_3factor(20, -800, "Negative reward")
|
|
|
| w_none, e_none = run_3factor(999, 0, "No reward")
|
|
|
| w_delayed, e_delayed = run_3factor(35, 800, "Delayed reward")
|
|
|
| print("Running E/I network at 1024 scale...")
|
| net_scale = nc.Network()
|
| exc = net_scale.population(256, params={"threshold": 500, "leak": 2, "refrac": 2}, label="Excitatory")
|
| inh = net_scale.population(64, params={"threshold": 400, "leak": 2, "refrac": 2}, label="Inhibitory")
|
|
|
| net_scale.connect(exc, exc, topology="random_sparse", p=0.12, weight=250, seed=42)
|
| net_scale.connect(exc, inh, topology="fixed_fan_out", fan_out=48, weight=200, seed=42)
|
| net_scale.connect(inh, exc, topology="fixed_fan_out", fan_out=64, weight=-180, seed=42)
|
|
|
| sim_scale = nc.Simulator()
|
| sim_scale.deploy(net_scale)
|
| scale_compiled = sim_scale._compiled
|
|
|
| scale_trains = defaultdict(list)
|
| scale_counts = []
|
| scale_total = 0
|
| for t in range(200):
|
| sim_scale.inject(exc[:32], current=600)
|
| result = sim_scale.run(1)
|
| scale_total += result.total_spikes
|
| scale_counts.append(result.total_spikes)
|
| for gid, times in result.spike_trains.items():
|
| scale_trains[gid].extend([t])
|
|
|
| print("Building figure...")
|
| fig = plt.figure(figsize=(24, 22), facecolor=BG)
|
| fig.suptitle("NEUROCORE v0.2.0 — Phase 13: Loihi 1 Parity",
|
| fontsize=22, color=CYAN, fontweight="bold", fontfamily="monospace", y=0.98)
|
| fig.text(0.5, 0.96,
|
| "1024 neurons/core | CSR variable fanout (32K pool) | "
|
| "8× multicast routing | 3-factor eligibility learning",
|
| ha="center", fontsize=10, color="#666", fontfamily="monospace")
|
|
|
| gs = gridspec.GridSpec(3, 3, figure=fig, hspace=0.35, wspace=0.28,
|
| left=0.05, right=0.96, top=0.93, bottom=0.04)
|
|
|
| ax1 = fig.add_subplot(gs[0, 0])
|
| ax1.set_facecolor(PANEL)
|
| ax1.set_title("P13a: CSR Variable Fanout", color=TEXT, fontsize=12,
|
| fontfamily="monospace", pad=10)
|
|
|
|
|
| fanouts = sorted(fanout_per_neuron.values())
|
| unique_vals = sorted(set(fanouts))
|
| counts_per = [fanouts.count(v) for v in unique_vals]
|
| colors = [GOLD if v > 32 else CYAN for v in unique_vals]
|
| bars = ax1.bar(range(len(unique_vals)), counts_per, color=colors, alpha=0.8, width=0.6)
|
|
|
| ax1.set_xticks(range(len(unique_vals)))
|
| ax1.set_xticklabels([str(v) for v in unique_vals], fontsize=8)
|
| ax1.set_xlabel("Connections per neuron", color=TEXT, fontsize=9, fontfamily="monospace")
|
| ax1.set_ylabel("Neuron count", color=TEXT, fontsize=9, fontfamily="monospace")
|
| ax1.tick_params(colors="#666", labelsize=8)
|
| for spine in ax1.spines.values():
|
| spine.set_color("#222")
|
|
|
|
|
| if any(v > 32 for v in unique_vals):
|
| ax1.text(0.95, 0.95, f"Hub: 100 targets!\n(was limited to 32)",
|
| transform=ax1.transAxes, fontsize=8, color=GOLD,
|
| fontfamily="monospace", ha="right", va="top",
|
| bbox=dict(boxstyle="round,pad=0.3", facecolor=PANEL, edgecolor=GOLD, alpha=0.8))
|
|
|
|
|
| old_p = mpatches.Patch(color=CYAN, label="Within old limit (≤32)")
|
| new_p = mpatches.Patch(color=GOLD, label="Exceeds old limit (>32)")
|
| ax1.legend(handles=[old_p, new_p], loc="center right", fontsize=7,
|
| facecolor=PANEL, edgecolor="#333", labelcolor=TEXT)
|
|
|
| ax2 = fig.add_subplot(gs[0, 1])
|
| ax2.set_facecolor(PANEL)
|
| ax2.set_title("CSR Pool Architecture", color=TEXT, fontsize=12,
|
| fontfamily="monospace", pad=10)
|
| ax2.set_xlim(0, 10)
|
| ax2.set_ylim(0, 8)
|
| ax2.axis("off")
|
|
|
|
|
| ax2.add_patch(mpatches.FancyBboxPatch((0.3, 5.5), 3.5, 2,
|
| boxstyle="round,pad=0.15", facecolor=CYAN, alpha=0.12,
|
| edgecolor=CYAN, linewidth=1.5))
|
| ax2.text(2.05, 7.2, "INDEX TABLE", ha="center", fontsize=9, color=CYAN,
|
| fontweight="bold", fontfamily="monospace")
|
| ax2.text(2.05, 6.6, "1024 entries", ha="center", fontsize=7, color="#888",
|
| fontfamily="monospace")
|
| ax2.text(2.05, 6.1, "neuron → {base, count}", ha="center", fontsize=7,
|
| color=CYAN, fontfamily="monospace")
|
|
|
|
|
| ax2.add_patch(mpatches.FancyBboxPatch((5, 5.5), 4.5, 2,
|
| boxstyle="round,pad=0.15", facecolor=GOLD, alpha=0.12,
|
| edgecolor=GOLD, linewidth=1.5))
|
| ax2.text(7.25, 7.2, "CONNECTION POOL", ha="center", fontsize=9, color=GOLD,
|
| fontweight="bold", fontfamily="monospace")
|
| ax2.text(7.25, 6.6, "32,768 entries (shared)", ha="center", fontsize=7,
|
| color="#888", fontfamily="monospace")
|
| ax2.text(7.25, 6.1, "pool[addr] → {tgt, wt, comp}", ha="center", fontsize=7,
|
| color=GOLD, fontfamily="monospace")
|
|
|
|
|
| ax2.annotate("", xy=(5, 6.5), xytext=(3.8, 6.5),
|
| arrowprops=dict(arrowstyle="->", color=GREEN, lw=2))
|
| ax2.text(4.4, 6.8, "base_addr", fontsize=6, color=GREEN, fontfamily="monospace",
|
| ha="center")
|
|
|
|
|
| examples = [
|
| (0.5, 4.5, "N0: base=0, count=100", GOLD),
|
| (0.5, 3.8, "N1: base=100, count=3", CYAN),
|
| (0.5, 3.1, "N2: base=103, count=50", PURPLE),
|
| (0.5, 2.4, "...", "#555"),
|
| ]
|
| for x, y, label, color in examples:
|
| ax2.text(x, y, label, fontsize=7.5, color=color, fontfamily="monospace")
|
|
|
|
|
| ax2.add_patch(mpatches.FancyBboxPatch((5.3, 1.8), 4, 2.8,
|
| boxstyle="round,pad=0.15", facecolor=RED, alpha=0.08,
|
| edgecolor=RED, linewidth=1, ls="--"))
|
| ax2.text(7.3, 4.3, "OLD: Fixed 32 slots/neuron", ha="center", fontsize=7.5,
|
| color=RED, fontweight="bold", fontfamily="monospace")
|
| ax2.text(7.3, 3.7, "N0: [slot0][slot1]...[slot31]", ha="center", fontsize=7,
|
| color=RED, fontfamily="monospace", alpha=0.7)
|
| ax2.text(7.3, 3.1, "Always scan all 32 slots", ha="center", fontsize=7,
|
| color=RED, fontfamily="monospace", alpha=0.7)
|
| ax2.text(7.3, 2.4, "Wasted cycles on empty slots", ha="center", fontsize=7,
|
| color=RED, fontfamily="monospace", alpha=0.7)
|
|
|
|
|
| ax2.text(5, 1.2, "Savings: sparse neurons (3 conn) take 17 cycles\n"
|
| "instead of 192 cycles → 11× speedup",
|
| ha="center", fontsize=7, color=GREEN, fontfamily="monospace",
|
| style="italic",
|
| bbox=dict(boxstyle="round,pad=0.3", facecolor="#0a0a1a",
|
| edgecolor="#333", alpha=0.8))
|
|
|
| ax3 = fig.add_subplot(gs[0, 2])
|
| ax3.set_facecolor(PANEL)
|
| ax3.set_title(f"P13b: Multicast Routing ({ROUTE_FANOUT}×)", color=TEXT,
|
| fontsize=12, fontfamily="monospace", pad=10)
|
| ax3.set_xlim(0, 10)
|
| ax3.set_ylim(0, 8)
|
| ax3.axis("off")
|
|
|
|
|
| src_x, src_y = 1.5, 4
|
| ax3.add_patch(mpatches.FancyBboxPatch((src_x-1.2, src_y-0.8), 2.4, 1.6,
|
| boxstyle="round,pad=0.15", facecolor=CYAN, alpha=0.15,
|
| edgecolor=CYAN, linewidth=2))
|
| ax3.text(src_x, src_y+0.3, "Core 0", ha="center", fontsize=9, color=CYAN,
|
| fontweight="bold", fontfamily="monospace")
|
| ax3.text(src_x, src_y-0.3, "N0 fires", ha="center", fontsize=7, color=CYAN,
|
| fontfamily="monospace")
|
|
|
|
|
| target_colors = [GREEN, GOLD, PURPLE, BLUE, ORANGE, PINK]
|
| target_positions = [(7, 7), (9, 6), (9, 4), (9, 2), (7, 1), (5, 1)]
|
| for i, ((tx, ty), color) in enumerate(zip(target_positions, target_colors)):
|
| ax3.add_patch(mpatches.FancyBboxPatch((tx-0.7, ty-0.5), 1.4, 1,
|
| boxstyle="round,pad=0.1", facecolor=color, alpha=0.15,
|
| edgecolor=color, linewidth=1.5))
|
| ax3.text(tx, ty, f"Core {i+1}", ha="center", fontsize=7.5, color=color,
|
| fontweight="bold", fontfamily="monospace")
|
|
|
| ax3.annotate("", xy=(tx-0.7, ty), xytext=(src_x+1.2, src_y),
|
| arrowprops=dict(arrowstyle="->", color=color, lw=1.2, alpha=0.7))
|
|
|
|
|
| ax3.text(5, 4.8, "Slot 0", fontsize=6, color=GREEN, fontfamily="monospace",
|
| rotation=20)
|
| ax3.text(5.5, 5.5, "Slot 1", fontsize=6, color=GOLD, fontfamily="monospace",
|
| rotation=10)
|
|
|
|
|
| ax3.text(1.5, 7.5, "OLD: 1 route per source", fontsize=8, color=RED,
|
| fontfamily="monospace", ha="center",
|
| bbox=dict(boxstyle="round,pad=0.2", facecolor=PANEL, edgecolor=RED, alpha=0.8))
|
| ax3.text(1.5, 6.7, f"NEW: {ROUTE_FANOUT} slots per source", fontsize=8, color=GREEN,
|
| fontfamily="monospace", ha="center",
|
| bbox=dict(boxstyle="round,pad=0.2", facecolor=PANEL, edgecolor=GREEN, alpha=0.8))
|
|
|
| ax4 = fig.add_subplot(gs[1, 0])
|
| ax4.set_facecolor(PANEL)
|
| ax4.set_title("P13c: Eligibility Traces", color=TEXT, fontsize=12,
|
| fontfamily="monospace", pad=10)
|
|
|
| t_axis = range(60)
|
| ax4.fill_between(t_axis, e_pos, alpha=0.15, color=CYAN)
|
| ax4.plot(t_axis, e_pos, color=CYAN, lw=1.5, label="+ reward @ t=20")
|
| ax4.fill_between(t_axis, e_delayed, alpha=0.15, color=GOLD)
|
| ax4.plot(t_axis, e_delayed, color=GOLD, lw=1.5, label="+ reward @ t=35")
|
| ax4.fill_between(t_axis, e_none, alpha=0.15, color="#666")
|
| ax4.plot(t_axis, e_none, color="#666", lw=1.5, label="No reward")
|
|
|
|
|
| ax4.axvline(20, color=CYAN, ls=":", alpha=0.5, lw=1)
|
| ax4.axvline(35, color=GOLD, ls=":", alpha=0.5, lw=1)
|
| ax4.text(20.5, max(e_pos)*0.9, "R+", fontsize=8, color=CYAN, fontfamily="monospace")
|
| ax4.text(35.5, max(e_delayed)*0.7, "R+", fontsize=8, color=GOLD, fontfamily="monospace")
|
|
|
| ax4.set_xlabel("Timestep", color=TEXT, fontsize=9, fontfamily="monospace")
|
| ax4.set_ylabel("Total |eligibility|", color=TEXT, fontsize=9, fontfamily="monospace")
|
| ax4.tick_params(colors="#666", labelsize=7)
|
| ax4.legend(fontsize=7, facecolor=PANEL, edgecolor="#333", labelcolor=TEXT, loc="upper right")
|
| for spine in ax4.spines.values():
|
| spine.set_color("#222")
|
|
|
| ax5 = fig.add_subplot(gs[1, 1])
|
| ax5.set_facecolor(PANEL)
|
| ax5.set_title("P13c: Weight Change via Reward", color=TEXT, fontsize=12,
|
| fontfamily="monospace", pad=10)
|
|
|
| ax5.plot(t_axis, w_pos, color=GREEN, lw=2, label="Positive reward")
|
| ax5.plot(t_axis, w_neg, color=RED, lw=2, label="Negative reward")
|
| ax5.plot(t_axis, w_delayed, color=GOLD, lw=2, ls="--", label="Delayed reward")
|
| ax5.plot(t_axis, w_none, color="#666", lw=1.5, ls=":", label="No reward (control)")
|
|
|
| ax5.axhline(500, color="#444", ls=":", lw=0.5)
|
| ax5.axvline(20, color="#444", ls=":", alpha=0.5, lw=1)
|
| ax5.axvline(35, color="#444", ls=":", alpha=0.5, lw=1)
|
| ax5.text(20.5, min(min(w_neg), 400), "reward\n@ t=20", fontsize=6, color="#888",
|
| fontfamily="monospace")
|
| ax5.text(35.5, min(min(w_neg), 400), "delayed\n@ t=35", fontsize=6, color="#888",
|
| fontfamily="monospace")
|
|
|
| ax5.set_xlabel("Timestep", color=TEXT, fontsize=9, fontfamily="monospace")
|
| ax5.set_ylabel("Synapse weight", color=TEXT, fontsize=9, fontfamily="monospace")
|
| ax5.tick_params(colors="#666", labelsize=7)
|
| ax5.legend(fontsize=7, facecolor=PANEL, edgecolor="#333", labelcolor=TEXT, loc="center right")
|
| for spine in ax5.spines.values():
|
| spine.set_color("#222")
|
|
|
| ax6 = fig.add_subplot(gs[1, 2])
|
| ax6.set_facecolor(PANEL)
|
| ax6.set_title("3-Factor Learning Pipeline", color=TEXT, fontsize=12,
|
| fontfamily="monospace", pad=10)
|
| ax6.set_xlim(0, 10)
|
| ax6.set_ylim(0, 8)
|
| ax6.axis("off")
|
|
|
|
|
| boxes = [
|
| (2, 7, "STDP\nCorrelation", CYAN),
|
| (5, 7, "Eligibility\nAccumulate", PURPLE),
|
| (8, 7, "Eligibility\nDecay", ORANGE),
|
| (5, 4.5, "REWARD\nSignal", GOLD),
|
| (5, 2.2, "Weight\nUpdate", GREEN),
|
| ]
|
| for bx, by, label, color in boxes:
|
| ax6.add_patch(mpatches.FancyBboxPatch((bx-1.3, by-0.7), 2.6, 1.4,
|
| boxstyle="round,pad=0.15", facecolor=color, alpha=0.12,
|
| edgecolor=color, linewidth=1.5))
|
| ax6.text(bx, by, label, ha="center", va="center", fontsize=8,
|
| color=color, fontweight="bold", fontfamily="monospace")
|
|
|
|
|
| arrows = [
|
| ((3.3, 7), (3.7, 7), CYAN),
|
| ((6.3, 7), (6.7, 7), PURPLE),
|
| ((5, 6.3), (5, 5.2), PURPLE),
|
| ((5, 3.8), (5, 2.9), GREEN),
|
| ]
|
| for start, end, color in arrows:
|
| ax6.annotate("", xy=end, xytext=start,
|
| arrowprops=dict(arrowstyle="->", color=color, lw=1.5))
|
|
|
|
|
| ax6.text(5, 3.7, "×", fontsize=16, color=GOLD, fontfamily="monospace",
|
| ha="center", va="center", fontweight="bold")
|
|
|
|
|
| ax6.text(1.5, 5.5, "pre/post\nspike\ntiming", fontsize=7, color=CYAN,
|
| fontfamily="monospace", ha="center", style="italic")
|
| ax6.annotate("", xy=(2, 6.3), xytext=(1.5, 5.8),
|
| arrowprops=dict(arrowstyle="->", color=CYAN, lw=1, alpha=0.5))
|
|
|
| ax6.text(8.5, 4.5, "external\nreward\nsignal", fontsize=7, color=GOLD,
|
| fontfamily="monospace", ha="center", style="italic")
|
| ax6.annotate("", xy=(6.3, 4.5), xytext=(7.8, 4.5),
|
| arrowprops=dict(arrowstyle="->", color=GOLD, lw=1, alpha=0.5))
|
|
|
|
|
| ax6.text(5, 1.1,
|
| "Δw = (eligibility × reward) >> 7\n"
|
| "elig_decay: elig -= elig >> 3 (~12.5%/ts)",
|
| ha="center", fontsize=7, color="#888", fontfamily="monospace",
|
| bbox=dict(boxstyle="round,pad=0.3", facecolor="#0a0a1a",
|
| edgecolor="#333", alpha=0.8))
|
|
|
| ax7 = fig.add_subplot(gs[2, 0:2])
|
| ax7.set_facecolor(PANEL)
|
| ax7.set_title(f"E/I Network — 320 neurons, fan-out up to 64 (P13 CSR) — {scale_total:,} spikes / 200 ts",
|
| color=TEXT, fontsize=11, fontfamily="monospace", pad=10)
|
|
|
| for gid, times in scale_trains.items():
|
| local = gid % NEURONS_PER_CORE
|
| color = CYAN if local < 256 else RED
|
| ax7.scatter(times, [gid] * len(times), s=0.4, c=color, marker="|", linewidths=0.2)
|
|
|
| ax7.set_xlabel("Timestep", color=TEXT, fontsize=9, fontfamily="monospace")
|
| ax7.set_ylabel("Neuron ID", color=TEXT, fontsize=9, fontfamily="monospace")
|
| ax7.tick_params(colors="#666", labelsize=7)
|
| for spine in ax7.spines.values():
|
| spine.set_color("#222")
|
| exc_p = mpatches.Patch(color=CYAN, label="Excitatory (256)")
|
| inh_p = mpatches.Patch(color=RED, label="Inhibitory (64)")
|
| ax7.legend(handles=[exc_p, inh_p], loc="upper right", fontsize=7,
|
| facecolor=PANEL, edgecolor="#333", labelcolor=TEXT)
|
|
|
| ax8 = fig.add_subplot(gs[2, 2])
|
| ax8.set_facecolor(PANEL)
|
| ax8.set_title("P12 → P13 Feature Gains", color=TEXT, fontsize=12,
|
| fontfamily="monospace", pad=10)
|
| ax8.axis("off")
|
|
|
| features = [
|
| ("Neurons/core", "256", "1,024", "4×"),
|
| ("Max fanout", "32 (fixed)", "~1,024 (pool)", "32×"),
|
| ("Pool depth", "8,192", "32,768", "4×"),
|
| ("Inter-core routes", "1/source", f"{ROUTE_FANOUT}/source", f"{ROUTE_FANOUT}×"),
|
| ("Learning", "2-factor STDP", "3-factor elig.", "+reward"),
|
| ("Total neurons", "32,768", "131,072", "4×"),
|
| ]
|
|
|
|
|
| y = 0.92
|
| ax8.text(0.05, y, "Feature", fontsize=8, color=CYAN, fontweight="bold",
|
| fontfamily="monospace", transform=ax8.transAxes)
|
| ax8.text(0.38, y, "P12", fontsize=8, color=RED, fontweight="bold",
|
| fontfamily="monospace", transform=ax8.transAxes)
|
| ax8.text(0.60, y, "P13", fontsize=8, color=GREEN, fontweight="bold",
|
| fontfamily="monospace", transform=ax8.transAxes)
|
| ax8.text(0.85, y, "Gain", fontsize=8, color=GOLD, fontweight="bold",
|
| fontfamily="monospace", transform=ax8.transAxes)
|
|
|
| y -= 0.04
|
| ax8.plot([0.02, 0.98], [y, y], color="#333", lw=0.5,
|
| transform=ax8.transAxes, clip_on=False)
|
|
|
| for feat, old, new, gain in features:
|
| y -= 0.1
|
| ax8.text(0.05, y, feat, fontsize=7.5, color=TEXT,
|
| fontfamily="monospace", transform=ax8.transAxes)
|
| ax8.text(0.38, y, old, fontsize=7.5, color="#888",
|
| fontfamily="monospace", transform=ax8.transAxes)
|
| ax8.text(0.60, y, new, fontsize=7.5, color=GREEN,
|
| fontfamily="monospace", transform=ax8.transAxes)
|
| ax8.text(0.85, y, gain, fontsize=7.5, color=GOLD, fontweight="bold",
|
| fontfamily="monospace", transform=ax8.transAxes)
|
|
|
|
|
| ax8.text(0.5, 0.05,
|
| f"Pool: {len(compiled.prog_pool_cmds)} entries | "
|
| f"Routes: {len(mcast_compiled.prog_route_cmds):,} | "
|
| f"Cores: {scale_compiled.placement.num_cores_used}",
|
| ha="center", fontsize=7, color="#666", fontfamily="monospace",
|
| transform=ax8.transAxes)
|
|
|
|
|
| output = r"C:\Users\mrwab\neuromorphic-chip\sdk\p13_dashboard.png"
|
| plt.savefig(output, dpi=180, facecolor=BG, bbox_inches="tight")
|
| plt.close()
|
| print(f"Saved to: {output}")
|
|
|