| | """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}")
|
| |
|