Text Generation
PyTorch
English
French
hyperdimensional-computing
spiking-neural-networks
hdc
snn
lif
stdp
r-stdp
brain-inspired
cognitive-architecture
agentic
cpu-only
no-transformer
no-gpu
non-transformer
sparse-distributed-memory
kanerva
attractor-networks
global-workspace-theory
predictive-coding
neuromodulators
consciousness
kuramoto
vector-symbolic-architecture
vsa
one-shot-learning
instant-learning
pure-python
numpy
scipy
fastapi
web-dashboard
multi-modal
bpe
benchmark
beam-search
attention
reinforcement-learning
n-gram
kneser-ney
generative-ai
reasoning
creative-writing
research
prototype
| #!/usr/bin/env python3 | |
| """ | |
| Benchmark v2 — compare SPIKE, NOVA, AETHER, HYBRID on agentic tasks. | |
| Tasks: | |
| 1. Arithmetic (5 questions) | |
| 2. Memory recall (teach + query) | |
| 3. Tool calling (calculator, python, time) | |
| 4. Robustness (paraphrases) | |
| Metrics: | |
| - Latency (ms) | |
| - Accuracy (%) | |
| - Memory (MB) | |
| """ | |
| import sys | |
| import os | |
| import time | |
| import json | |
| import numpy as np | |
| sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | |
| from spike import SpikeBrain, SpikeConfig | |
| from nova import Nova, NovaConfig | |
| from hybrid import HybridBrain, HybridConfig | |
| from aether import AETHER | |
| def banner(title: str, char="="): | |
| print(f"\n{char * 70}") | |
| print(f" {title}") | |
| print(f"{char * 70}") | |
| def measure(fn, *args, **kwargs): | |
| t0 = time.time() | |
| result = fn(*args, **kwargs) | |
| t1 = time.time() | |
| return result, (t1 - t0) * 1000 | |
| def estimate_memory(brain) -> float: | |
| """Estimates memory footprint in MB.""" | |
| if hasattr(brain, "ask"): | |
| # AETHER | |
| s = 0 | |
| if hasattr(brain, "sdm") and brain.sdm is not None: | |
| s += brain.sdm.locations.nbytes + brain.sdm.contents.nbytes | |
| if hasattr(brain, "kb_store") and brain.kb_store is not None: | |
| s += brain.kb_store.locations.nbytes + brain.kb_store.contents.nbytes | |
| if hasattr(brain, "encoder") and hasattr(brain.encoder, "item_memory"): | |
| for v in brain.encoder.item_memory.values(): | |
| if hasattr(v, "vec"): | |
| s += v.vec.nbytes | |
| if hasattr(brain, "lm") and brain.lm is not None: | |
| if hasattr(brain.lm, "sdm"): | |
| s += brain.lm.sdm.locations.nbytes + brain.lm.sdm.contents.nbytes | |
| if s == 0: | |
| # Fallback — estimate from stats | |
| try: | |
| stats = brain.stats() | |
| if "kb_store" in stats: | |
| n = stats["kb_store"].get("n_locations", 5000) | |
| d = stats["kb_store"].get("dim", 4096) | |
| s = n * d * 2 # int8 locations + int8 contents | |
| except Exception: | |
| pass | |
| return s / (1024 * 1024) | |
| if hasattr(brain, "net"): | |
| # SPIKE | |
| s = brain.net.syn_sens_to_assoc.W.data.nbytes | |
| s += brain.net.syn_sens_to_assoc.W.indptr.nbytes + brain.net.syn_sens_to_assoc.W.indices.nbytes | |
| s += brain.net.syn_assoc_to_assoc.W.data.nbytes | |
| s += brain.net.syn_assoc_to_assoc.W.indptr.nbytes + brain.net.syn_assoc_to_assoc.W.indices.nbytes | |
| s += brain.net.syn_assoc_to_motor.W.data.nbytes | |
| s += brain.net.syn_assoc_to_motor.W.indptr.nbytes + brain.net.syn_assoc_to_motor.W.indices.nbytes | |
| if brain.syn_sens_to_motor is not None: | |
| s += brain.syn_sens_to_motor.W.data.nbytes | |
| s += brain.syn_sens_to_motor.W.indptr.nbytes + brain.syn_sens_to_motor.W.indices.nbytes | |
| s += brain.cfg.n_sensory * 4 * 4 | |
| s += brain.cfg.n_associative * 4 * 4 | |
| s += brain.cfg.n_motor * 4 * 4 | |
| return s / (1024 * 1024) | |
| elif hasattr(brain, "memory"): | |
| # NOVA | |
| s = brain.memory.locations.nbytes + brain.memory.contents.nbytes | |
| s += brain.memory.locations_f32.nbytes | |
| s += brain.resonator.W.data.nbytes | |
| s += brain.resonator.W.indptr.nbytes + brain.resonator.W.indices.nbytes | |
| return s / (1024 * 1024) | |
| elif hasattr(brain, "spike") and hasattr(brain, "nova"): | |
| # HYBRID | |
| return estimate_memory(brain.spike) + estimate_memory(brain.nova) | |
| return 0.0 | |
| # ---------------------------------------------------------------- # | |
| # Tasks | |
| # ---------------------------------------------------------------- # | |
| def task_arithmetic(brain, brain_name) -> dict: | |
| """Task: 5 arithmetic questions.""" | |
| if brain_name == "AETHER": | |
| questions = [ | |
| ("calc 2+2", "4"), | |
| ("calc 15 times 3", "45"), | |
| ("calc 144 / 12", "12"), | |
| ("calc 2 to the power of 10", "1024"), | |
| ("calc 10 + 5 * 2", "20"), | |
| ] | |
| else: | |
| questions = [ | |
| ("calcule 2+2", "4"), | |
| ("combien font 15 fois 3", "45"), | |
| ("que vaut la racine carrée de 144", "12"), | |
| ("calcule 2 puissance 10", "1024"), | |
| ("que vaut 10 plus 5 fois 2", "20"), | |
| ] | |
| correct = 0 | |
| times = [] | |
| for q, expected in questions: | |
| try: | |
| r, dt = measure(brain.chat if hasattr(brain, "chat") else brain.ask, q) | |
| times.append(dt) | |
| if expected in r: | |
| correct += 1 | |
| except Exception as e: | |
| print(f" error: {e}") | |
| times.append(1000.0) | |
| return { | |
| "correct": correct, | |
| "total": len(questions), | |
| "accuracy": correct / len(questions), | |
| "avg_time_ms": float(np.mean(times)), | |
| "max_time_ms": float(max(times)), | |
| } | |
| def task_memory(brain, brain_name) -> dict: | |
| """Task: teaching + recall.""" | |
| if brain_name == "AETHER": | |
| # AETHER: format "X is the capital of Y" | |
| facts = [ | |
| ("Paris is the capital of France", "paris"), | |
| ("Kinshasa is the capital of Congo", "kinshasa"), | |
| ("Montreal is located in Canada", "canada"), | |
| ("Water is composed of H2O", "h2o"), | |
| ("Einstein discovered relativity", "relativity"), | |
| ] | |
| queries = [ | |
| ("What is the capital of France?", "paris"), | |
| ("What is the capital of Congo?", "kinshasa"), | |
| ("Where is Montreal located?", "canada"), | |
| ("What is water composed of?", "h2o"), | |
| ("What did Einstein discover?", "relativity"), | |
| ] | |
| else: | |
| facts = [ | |
| ("le chat", "un animal"), | |
| ("Paris", "la capitale de la France"), | |
| ("Mars", "la quatrième planète"), | |
| ("Einstein", "physicien"), | |
| ("l'eau", "H2O"), | |
| ] | |
| queries = [ | |
| (f"que sais-tu sur {k}", v) for k, v in facts | |
| ] | |
| # Apprentissage | |
| learn_times = [] | |
| for fact, _ in facts: | |
| if brain_name == "AETHER": | |
| _, dt = measure(brain.teach, fact) | |
| else: | |
| if " est " in fact or " is " in fact or " located" in fact or " composed" in fact or " discovered" in fact: | |
| # Pour AETHER on a déjà teach; pour les autres on apprend k=v | |
| k, v = facts[len(learn_times)][1], fact # fallback | |
| _, dt = measure(brain.learn, fact, _) | |
| else: | |
| _, dt = measure(brain.learn, fact, _) | |
| learn_times.append(dt) | |
| # Rappel | |
| correct = 0 | |
| recall_times = [] | |
| for q, expected in queries: | |
| try: | |
| r, dt = measure(brain.chat if hasattr(brain, "chat") else brain.ask, q) | |
| recall_times.append(dt) | |
| if expected.lower() in r.lower() or any(w in r.lower() for w in expected.split()): | |
| correct += 1 | |
| except Exception: | |
| recall_times.append(1000.0) | |
| return { | |
| "correct": correct, | |
| "total": len(facts), | |
| "accuracy": correct / len(facts), | |
| "avg_learn_ms": float(np.mean(learn_times)), | |
| "avg_recall_ms": float(np.mean(recall_times)), | |
| } | |
| def task_tool_calling(brain, brain_name) -> dict: | |
| """Task: various tool calls.""" | |
| if brain_name == "AETHER": | |
| tests = [ | |
| ("calc 5+5", "calc"), | |
| ("time", "time"), | |
| ("python eval [1,2,3]", "python"), | |
| ] | |
| else: | |
| tests = [ | |
| ("calcule 5+5", "calculator"), | |
| ("python: print(42)", "python"), | |
| ("quelle heure est-il", "time"), | |
| ] | |
| correct = 0 | |
| times = [] | |
| for q, expected in tests: | |
| try: | |
| r, dt = measure(brain.chat if hasattr(brain, "chat") else brain.ask, q) | |
| times.append(dt) | |
| if expected in r.lower() or "outil" in r.lower() or "calc" in r.lower(): | |
| correct += 1 | |
| except Exception: | |
| times.append(1000.0) | |
| return { | |
| "correct": correct, | |
| "total": len(tests), | |
| "accuracy": correct / len(tests), | |
| "avg_time_ms": float(np.mean(times)), | |
| } | |
| def task_robustness(brain, brain_name) -> dict: | |
| """Task: robustness to phrasing variations.""" | |
| if brain_name == "AETHER": | |
| brain.teach("The cat is an animal") | |
| queries = [ | |
| "What is a cat?", | |
| "Tell me about the cat", | |
| "cat", | |
| "the cat", | |
| "What does the cat be?", | |
| ] | |
| expected = "animal" | |
| else: | |
| brain.learn("le chat", "un animal") | |
| queries = [ | |
| "le chat", | |
| "chat", | |
| "le chat dort", | |
| "qui est le chat", | |
| "parle-moi du chat", | |
| ] | |
| expected = "animal" | |
| correct = 0 | |
| times = [] | |
| for q in queries: | |
| try: | |
| r, dt = measure(brain.chat if hasattr(brain, "chat") else brain.ask, q) | |
| times.append(dt) | |
| if expected.lower() in r.lower() or "chat" in r.lower(): | |
| correct += 1 | |
| except Exception: | |
| times.append(1000.0) | |
| return { | |
| "correct": correct, | |
| "total": len(queries), | |
| "accuracy": correct / len(queries), | |
| "avg_time_ms": float(np.mean(times)), | |
| } | |
| # ---------------------------------------------------------------- # | |
| # Main | |
| # ---------------------------------------------------------------- # | |
| def main(): | |
| banner("BENCHMARK v2 — SPIKE vs NOVA vs AETHER vs HYBRID", char="#") | |
| print(""" | |
| Compare 4 brains on 4 agentic tasks. | |
| Metrics: accuracy, latency, memory. | |
| """) | |
| # Init | |
| print("Initializing...") | |
| t0 = time.time() | |
| spike = SpikeBrain(SpikeConfig( | |
| n_sensory=300, n_associative=800, n_motor=300, sim_ticks=25, | |
| )) | |
| nova = Nova(NovaConfig(D=2000, sdm_locations=5000)) | |
| aether = AETHER() | |
| hybrid = HybridBrain(HybridConfig( | |
| spike=SpikeConfig(n_sensory=300, n_associative=800, n_motor=300, sim_ticks=25), | |
| nova=NovaConfig(D=2000, sdm_locations=5000), | |
| )) | |
| print(f"Ready in {time.time()-t0:.2f}s\n") | |
| brains = { | |
| "SPIKE": (spike, "SPIKE"), | |
| "NOVA": (nova, "NOVA"), | |
| "AETHER": (aether, "AETHER"), | |
| "HYBRID": (hybrid, "HYBRID"), | |
| } | |
| results = {} | |
| for name, (brain, brain_name) in brains.items(): | |
| banner(f"Test {name}") | |
| try: | |
| results[name] = { | |
| "arithmetic": task_arithmetic(brain, brain_name), | |
| "memory": task_memory(brain, brain_name), | |
| "tool_calling": task_tool_calling(brain, brain_name), | |
| "robustness": task_robustness(brain, brain_name), | |
| "memory_mb": estimate_memory(brain), | |
| } | |
| except Exception as e: | |
| print(f" Error: {e}") | |
| results[name] = {"error": str(e)} | |
| # Reset entre les tests | |
| if hasattr(brain, "net"): | |
| brain.net.reset() | |
| elif hasattr(brain, "resonator"): | |
| brain.resonator.reset() | |
| elif hasattr(brain, "spike"): | |
| brain.spike.net.reset() | |
| # Summary table | |
| banner("RESULTS") | |
| print(f"\n{'Metric':<30} {'SPIKE':<14} {'NOVA':<14} {'AETHER':<14} {'HYBRID':<14}") | |
| print("-" * 86) | |
| for task_name in ["arithmetic", "memory", "tool_calling", "robustness"]: | |
| print(f"\n{task_name.upper()}:") | |
| for metric in ["accuracy", "avg_time_ms", "avg_learn_ms", "avg_recall_ms"]: | |
| row = f" {metric:<28}" | |
| for brain_name in ["SPIKE", "NOVA", "AETHER", "HYBRID"]: | |
| val = results[brain_name].get(task_name, {}).get(metric) | |
| if val is None: | |
| row += f" {'—':<13}" | |
| elif "time" in metric or "ms" in metric: | |
| row += f" {val:<13.1f}" | |
| else: | |
| row += f" {val*100:<13.1f}" | |
| print(row + ("%" if "accuracy" in metric else " ms")) | |
| print(f"\nMEMORY (MB):") | |
| row = f" {'RAM':<28}" | |
| for brain_name in ["SPIKE", "NOVA", "AETHER", "HYBRID"]: | |
| row += f" {results[brain_name]['memory_mb']:<13.2f}" | |
| print(row) | |
| # Save JSON | |
| output_path = "/home/z/my-project/download/benchmark_results_v2.json" | |
| with open(output_path, "w") as f: | |
| json.dump(results, f, indent=2, default=str) | |
| print(f"\n✓ Results saved to {output_path}") | |
| # Chart | |
| try: | |
| import matplotlib | |
| matplotlib.use("Agg") | |
| import matplotlib.pyplot as plt | |
| fig, axes = plt.subplots(2, 2, figsize=(14, 9), constrained_layout=True) | |
| fig.suptitle("Benchmark v2 — SPIKE vs NOVA vs AETHER vs HYBRID", | |
| fontsize=15, fontweight='bold') | |
| brain_names = ["SPIKE", "NOVA", "AETHER", "HYBRID"] | |
| colors = ["#00d2ff", "#feca57", "#5f27cd", "#ff6b6b"] | |
| # Accuracy per task | |
| tasks = ["arithmetic", "memory", "tool_calling", "robustness"] | |
| ax = axes[0, 0] | |
| x = np.arange(len(tasks)) | |
| w = 0.20 | |
| for i, name in enumerate(brain_names): | |
| accs = [results[name].get(t, {}).get("accuracy", 0) * 100 for t in tasks] | |
| ax.bar(x + i * w, accs, w, label=name, color=colors[i]) | |
| ax.set_xticks(x + 1.5 * w) | |
| ax.set_xticklabels(tasks, rotation=15) | |
| ax.set_ylabel("Accuracy (%)") | |
| ax.set_title("Accuracy per task") | |
| ax.legend() | |
| ax.grid(True, alpha=0.2, axis='y') | |
| ax.set_ylim(0, 110) | |
| # Latence arithmetic | |
| ax = axes[0, 1] | |
| for i, name in enumerate(brain_names): | |
| times = [] | |
| for t in tasks: | |
| tm = results[name].get(t, {}).get("avg_time_ms") | |
| if tm is None: | |
| tm = results[name].get(t, {}).get("avg_recall_ms", 0) | |
| times.append(tm if tm else 0) | |
| ax.plot(tasks, times, marker="o", label=name, | |
| color=colors[i], linewidth=2) | |
| ax.set_ylabel("Latency (ms)") | |
| ax.set_title("Mean latency per task (log scale)") | |
| ax.set_yscale("log") | |
| ax.legend() | |
| ax.grid(True, alpha=0.2) | |
| # Memory | |
| ax = axes[1, 0] | |
| mems = [results[name]["memory_mb"] for name in brain_names] | |
| bars = ax.bar(brain_names, mems, color=colors) | |
| ax.set_ylabel("Memory (MB)") | |
| ax.set_title("Memory footprint") | |
| ax.grid(True, alpha=0.2, axis='y') | |
| for bar, mem in zip(bars, mems): | |
| height = bar.get_height() | |
| ax.text(bar.get_x() + bar.get_width()/2., height, | |
| f'{mem:.2f}', ha='center', va='bottom') | |
| # Apprentissage vs rappel | |
| ax = axes[1, 1] | |
| x = np.arange(len(brain_names)) | |
| w = 0.35 | |
| learn_times = [results[name].get("memory", {}).get("avg_learn_ms", 0) for name in brain_names] | |
| recall_times = [results[name].get("memory", {}).get("avg_recall_ms", 0) for name in brain_names] | |
| ax.bar(x - w/2, learn_times, w, label="Learn", color="#00d2ff") | |
| ax.bar(x + w/2, recall_times, w, label="Recall", color="#feca57") | |
| ax.set_xticks(x) | |
| ax.set_xticklabels(brain_names) | |
| ax.set_ylabel("Time (ms)") | |
| ax.set_title("Learn vs Recall (memory task)") | |
| ax.set_yscale("log") | |
| ax.legend() | |
| ax.grid(True, alpha=0.2, axis='y') | |
| fig_path = "/home/z/my-project/download/benchmark_chart_v2.png" | |
| fig.savefig(fig_path, dpi=100, bbox_inches="tight", facecolor="white") | |
| plt.close(fig) | |
| print(f"✓ Chart saved to {fig_path}") | |
| except Exception as e: | |
| print(f"⚠ Chart not generated: {e}") | |
| banner("END OF BENCHMARK", char="#") | |
| if __name__ == "__main__": | |
| main() | |