rmems commited on
Commit
a14c76d
·
1 Parent(s): b09241f

Delete dataset/examples

Browse files
dataset/examples/fpga_deployment_guide.ipynb DELETED
@@ -1,1010 +0,0 @@
1
- {
2
- "cells": [
3
- {
4
- "cell_type": "markdown",
5
- "metadata": {},
6
- "source": [
7
- "# 🔧 Spikenaut SNN v2 - FPGA Deployment Guide\n",
8
- "\n",
9
- "Complete guide for deploying Spikenaut SNN v2 to Xilinx Artix-7 Basys3 FPGA.\n",
10
- "\n",
11
- "## What you'll learn:\n",
12
- "- Understanding Q8.8 fixed-point format\n",
13
- "- Loading parameters into FPGA memory\n",
14
- "- Verilog implementation basics\n",
15
- "- Hardware verification\n",
16
- "- Performance optimization"
17
- ]
18
- },
19
- {
20
- "cell_type": "markdown",
21
- "metadata": {},
22
- "source": [
23
- "## 1. Hardware Requirements"
24
- ]
25
- },
26
- {
27
- "cell_type": "code",
28
- "execution_count": null,
29
- "metadata": {},
30
- "outputs": [],
31
- "source": [
32
- "# Hardware specifications\n",
33
- "hardware_specs = {\n",
34
- " 'fpga_board': 'Xilinx Artix-7 Basys3',\n",
35
- " 'target_device': 'XC7A35T-1CPG236C',\n",
36
- " 'logic_cells': 5200,\n",
37
- " 'bram': 1800, # 18Kb blocks\n",
38
- " 'dsp_slices': 90,\n",
39
- " 'clock_speed': '1kHz (1ms resolution)',\n",
40
- " 'power_consumption': '~97mW dynamic',\n",
41
- " 'interface': 'UART, GPIO, PMOD'\n",
42
- "}\n",
43
- "\n",
44
- "print(\"🔧 Hardware Requirements:\")\n",
45
- "for key, value in hardware_specs.items():\n",
46
- " print(f\" {key}: {value}\")\n",
47
- "\n",
48
- "# Memory requirements\n",
49
- "memory_requirements = {\n",
50
- " 'neuron_thresholds': 16 * 2, # 16 neurons, 2 bytes each\n",
51
- " 'synaptic_weights': 16 * 8 * 2, # 16x8 matrix, 2 bytes each\n",
52
- " 'decay_constants': 16 * 2, # 16 decay values\n",
53
- " 'input_buffer': 8 * 2, # 8 input features\n",
54
- " 'output_buffer': 3 * 2, # 3 output classes\n",
55
- " 'total_memory_kb': (16 * 2 + 16 * 8 * 2 + 16 * 2 + 8 * 2 + 3 * 2) / 1024\n",
56
- "}\n",
57
- "\n",
58
- "print(f\"\\n💾 Memory Requirements:\")\n",
59
- "print(f\" Total memory needed: {memory_requirements['total_memory_kb']:.2f} KB\")\n",
60
- "print(f\" Available BRAM: {hardware_specs['bram']} * 18Kb = {hardware_specs['bram'] * 18 / 1024:.1f} MB\")\n",
61
- "print(f\" Memory utilization: {(memory_requirements['total_memory_kb'] / (hardware_specs['bram'] * 18 / 1024) * 100):.1f}%\")"
62
- ]
63
- },
64
- {
65
- "cell_type": "markdown",
66
- "metadata": {},
67
- "source": [
68
- "## 2. Q8.8 Fixed-Point Format"
69
- ]
70
- },
71
- {
72
- "cell_type": "code",
73
- "execution_count": null,
74
- "metadata": {},
75
- "outputs": [],
76
- "source": [
77
- "import numpy as np\n",
78
- "import matplotlib.pyplot as plt\n",
79
- "\n",
80
- "def float_to_q8_8(value):\n",
81
- " \"\"\"Convert float to Q8.8 fixed-point format\"\"\"\n",
82
- " # Clamp to Q8.8 range\n",
83
- " value = np.clip(value, -128, 127.996)\n",
84
- " # Convert to fixed-point\n",
85
- " q8_8 = int(value * 256)\n",
86
- " return q8_8\n",
87
- "\n",
88
- "def q8_8_to_float(q8_8):\n",
89
- " \"\"\"Convert Q8.8 fixed-point to float\"\"\"\n",
90
- " # Convert to signed integer\n",
91
- " if q8_8 >= 32768: # Negative number in two's complement\n",
92
- " q8_8 = q8_8 - 65536\n",
93
- " # Convert to float\n",
94
- " return q8_8 / 256.0\n",
95
- "\n",
96
- "# Demonstrate Q8.8 conversion\n",
97
- "test_values = [-1.0, -0.5, 0.0, 0.5, 1.0, 2.5, 10.0, 100.0]\n",
98
- "\n",
99
- "print(\"🔢 Q8.8 Fixed-Point Conversion Examples:\")\n",
100
- "print(\"Float -> Q8.8 (Hex) -> Back to Float\")\n",
101
- "print(\"-\" * 50)\n",
102
- "\n",
103
- "for val in test_values:\n",
104
- " q8_8 = float_to_q8_8(val)\n",
105
- " back_to_float = q8_8_to_float(q8_8)\n",
106
- " error = abs(back_to_float - val)\n",
107
- " \n",
108
- " print(f\"{val:6.2f} -> {q8_8:04X} -> {back_to_float:6.2f} (error: {error:.6f})\")\n",
109
- "\n",
110
- "# Show precision characteristics\n",
111
- "print(\"\\n📊 Q8.8 Precision Characteristics:\")\n",
112
- "print(f\" Range: [-128.0, +127.996]\")\n",
113
- "print(f\" Resolution: 1/256 ≈ 0.0039\")\n",
114
- "print(f\" Dynamic range: ~128/0.0039 ≈ 32768:1\")\n",
115
- "print(f\" Quantization step: 0.00390625\")\n",
116
- "\n",
117
- "# Visualize quantization error\n",
118
- "fine_values = np.linspace(-2, 2, 1000)\n",
119
- "quantized = [q8_8_to_float(float_to_q8_8(val)) for val in fine_values]\n",
120
- "quantization_error = np.array(quantized) - fine_values\n",
121
- "\n",
122
- "plt.figure(figsize=(12, 4))\n",
123
- "\n",
124
- "plt.subplot(1, 2, 1)\n",
125
- "plt.plot(fine_values, quantized, 'b-', alpha=0.7, label='Quantized')\n",
126
- "plt.plot(fine_values, fine_values, 'r--', alpha=0.5, label='Original')\n",
127
- "plt.xlabel('Input Value')\n",
128
- "plt.ylabel('Output Value')\n",
129
- "plt.title('Q8.8 Quantization Characteristic')\n",
130
- "plt.legend()\n",
131
- "plt.grid(True, alpha=0.3)\n",
132
- "\n",
133
- "plt.subplot(1, 2, 2)\n",
134
- "plt.plot(fine_values, quantization_error, 'g-', alpha=0.7)\n",
135
- "plt.xlabel('Input Value')\n",
136
- "plt.ylabel('Quantization Error')\n",
137
- "plt.title('Q8.8 Quantization Error')\n",
138
- "plt.grid(True, alpha=0.3)\n",
139
- "\n",
140
- "plt.tight_layout()\n",
141
- "plt.show()"
142
- ]
143
- },
144
- {
145
- "cell_type": "markdown",
146
- "metadata": {},
147
- "source": [
148
- "## 3. Loading FPGA Parameters"
149
- ]
150
- },
151
- {
152
- "cell_type": "code",
153
- "execution_count": null,
154
- "metadata": {},
155
- "outputs": [],
156
- "source": [
157
- "import os\n",
158
- "from pathlib import Path\n",
159
- "\n",
160
- "# Check if parameter files exist\n",
161
- "parameter_files = {\n",
162
- " 'thresholds': 'parameters/parameters.mem',\n",
163
- " 'weights': 'parameters/parameters_weights.mem',\n",
164
- " 'decay': 'parameters/parameters_decay.mem'\n",
165
- "}\n",
166
- "\n",
167
- "print(\"📂 Checking Parameter Files:\")\n",
168
- "for name, filepath in parameter_files.items():\n",
169
- " if os.path.exists(filepath):\n",
170
- " print(f\" ✅ {name}: {filepath}\")\n",
171
- " else:\n",
172
- " print(f\" ❌ {name}: {filepath} (not found)\")\n",
173
- "\n",
174
- "# Load and display parameters if files exist\n",
175
- "def load_mem_file(filepath, max_lines=10):\n",
176
- " \"\"\"Load parameters from .mem file\"\"\"\n",
177
- " if not os.path.exists(filepath):\n",
178
- " return None\n",
179
- " \n",
180
- " parameters = []\n",
181
- " with open(filepath, 'r') as f:\n",
182
- " for line_num, line in enumerate(f):\n",
183
- " if line_num >= max_lines:\n",
184
- " break\n",
185
- " line = line.strip()\n",
186
- " if line:\n",
187
- " # Convert hex to integer, then to float\n",
188
- " hex_val = int(line, 16)\n",
189
- " float_val = q8_8_to_float(hex_val)\n",
190
- " parameters.append(float_val)\n",
191
- " \n",
192
- " return parameters\n",
193
- "\n",
194
- "# Load and display sample parameters\n",
195
- "print(\"\\n🔍 Sample Parameters:\")\n",
196
- "for name, filepath in parameter_files.items():\n",
197
- " params = load_mem_file(filepath, max_lines=5)\n",
198
- " if params:\n",
199
- " print(f\"\\n{name.upper()} (first 5 values):\")\n",
200
- " for i, val in enumerate(params):\n",
201
- " print(f\" [{i}]: {val:.6f}\")\n",
202
- " else:\n",
203
- " print(f\"\\n{name.upper()}: File not found\")\n",
204
- "\n",
205
- "# Create sample parameters if files don't exist\n",
206
- "if not all(os.path.exists(f) for f in parameter_files.values()):\n",
207
- " print(\"\\n🔧 Creating sample parameter files...\")\n",
208
- " \n",
209
- " os.makedirs('parameters', exist_ok=True)\n",
210
- " \n",
211
- " # Sample thresholds (16 neurons)\n",
212
- " with open('parameters/parameters.mem', 'w') as f:\n",
213
- " for i in range(16):\n",
214
- " threshold = 0.5 + i * 0.1 # 0.5 to 2.0\n",
215
- " q8_8 = float_to_q8_8(threshold)\n",
216
- " f.write(f\"{q8_8:04X}\\n\")\n",
217
- " \n",
218
- " # Sample weights (16x8 matrix)\n",
219
- " with open('parameters/parameters_weights.mem', 'w') as f:\n",
220
- " for i in range(16):\n",
221
- " for j in range(8):\n",
222
- " weight = np.random.randn() * 0.2 # Small random weights\n",
223
- " q8_8 = float_to_q8_8(weight)\n",
224
- " f.write(f\"{q8_8:04X}\\n\")\n",
225
- " \n",
226
- " # Sample decay constants (16 neurons)\n",
227
- " with open('parameters/parameters_decay.mem', 'w') as f:\n",
228
- " for i in range(16):\n",
229
- " decay = 0.8 + i * 0.01 # 0.8 to 0.95\n",
230
- " q8_8 = float_to_q8_8(decay)\n",
231
- " f.write(f\"{q8_8:04X}\\n\")\n",
232
- " \n",
233
- " print(\"✅ Sample parameter files created in 'parameters/' directory\")"
234
- ]
235
- },
236
- {
237
- "cell_type": "markdown",
238
- "metadata": {},
239
- "source": [
240
- "## 4. Verilog Implementation"
241
- ]
242
- },
243
- {
244
- "cell_type": "code",
245
- "execution_count": null,
246
- "metadata": {},
247
- "outputs": [],
248
- "source": [
249
- "# Generate Verilog code for SNN implementation\n",
250
- "verilog_code = '''\n",
251
- "// Spikenaut SNN v2 - FPGA Implementation\n",
252
- "// Xilinx Artix-7 Basys3 Target\n",
253
- "// 16-neuron spiking neural network with Q8.8 fixed-point arithmetic\n",
254
- "\n",
255
- "module spikenaut_snn_v2 (\n",
256
- " // Clock and reset\n",
257
- " input wire clk,\n",
258
- " input wire rst_n,\n",
259
- " \n",
260
- " // Input interface (8 features)\n",
261
- " input wire [15:0] input_feature_0,\n",
262
- " input wire [15:0] input_feature_1,\n",
263
- " input wire [15:0] input_feature_2,\n",
264
- " input wire [15:0] input_feature_3,\n",
265
- " input wire [15:0] input_feature_4,\n",
266
- " input wire [15:0] input_feature_5,\n",
267
- " input wire [15:0] input_feature_6,\n",
268
- " input wire [15:0] input_feature_7,\n",
269
- " \n",
270
- " // Control signals\n",
271
- " input wire start_computation,\n",
272
- " output reg computation_done,\n",
273
- " \n",
274
- " // Output interface (3 classes)\n",
275
- " output reg [15:0] output_class_0,\n",
276
- " output reg [15:0] output_class_1,\n",
277
- " output reg [15:0] output_class_2,\n",
278
- " \n",
279
- " // Debug signals\n",
280
- " output reg [3:0] active_neuron,\n",
281
- " output reg [15:0] membrane_potential\n",
282
- ");\n",
283
- "\n",
284
- "// Parameters\n",
285
- "parameter NEURONS = 16;\n",
286
- "parameter INPUTS = 8;\n",
287
- "parameter OUTPUTS = 3;\n",
288
- "parameter FIXED_POINT_SHIFT = 8;\n",
289
- "\n",
290
- "// Memory arrays for parameters\n",
291
- "reg [15:0] neuron_thresholds [0:NEURONS-1];\n",
292
- "reg [15:0] synaptic_weights [0:NEURONS-1] [0:INPUTS-1];\n",
293
- "reg [15:0] decay_constants [0:NEURONS-1];\n",
294
- "\n",
295
- "// Internal state\n",
296
- "reg [15:0] membrane_potentials [0:NEURONS-1];\n",
297
- "reg spike_outputs [0:NEURONS-1];\n",
298
- "reg [31:0] weighted_sum;\n",
299
- "reg [3:0] neuron_index;\n",
300
- "reg [2:0] input_index;\n",
301
- "reg [1:0] state;\n",
302
- "\n",
303
- "// States\n",
304
- "localparam IDLE = 2'b00;\n",
305
- "localparam COMPUTE = 2'b01;\n",
306
- "localparam OUTPUT = 2'b10;\n",
307
- "\n",
308
- "// Input feature array\n",
309
- "wire [15:0] input_features [0:INPUTS-1];\n",
310
- "assign input_features[0] = input_feature_0;\n",
311
- "assign input_features[1] = input_feature_1;\n",
312
- "assign input_features[2] = input_feature_2;\n",
313
- "assign input_features[3] = input_feature_3;\n",
314
- "assign input_features[4] = input_feature_4;\n",
315
- "assign input_features[5] = input_feature_5;\n",
316
- "assign input_features[6] = input_feature_6;\n",
317
- "assign input_features[7] = input_feature_7;\n",
318
- "\n",
319
- "// Main state machine\n",
320
- "always @(posedge clk or negedge rst_n) begin\n",
321
- " if (!rst_n) begin\n",
322
- " // Reset state\n",
323
- " state <= IDLE;\n",
324
- " computation_done <= 0;\n",
325
- " neuron_index <= 0;\n",
326
- " input_index <= 0;\n",
327
- " \n",
328
- " // Clear membrane potentials\n",
329
- " for (integer i = 0; i < NEURONS; i = i + 1) begin\n",
330
- " membrane_potentials[i] <= 16'h0000;\n",
331
- " spike_outputs[i] <= 0;\n",
332
- " end\n",
333
- " \n",
334
- " // Clear outputs\n",
335
- " output_class_0 <= 16'h0000;\n",
336
- " output_class_1 <= 16'h0000;\n",
337
- " output_class_2 <= 16'h0000;\n",
338
- " active_neuron <= 4'h0;\n",
339
- " membrane_potential <= 16'h0000;\n",
340
- " \n",
341
- " end else begin\n",
342
- " case (state)\n",
343
- " IDLE: begin\n",
344
- " computation_done <= 0;\n",
345
- " if (start_computation) begin\n",
346
- " state <= COMPUTE;\n",
347
- " neuron_index <= 0;\n",
348
- " input_index <= 0;\n",
349
- " end\n",
350
- " end\n",
351
- " \n",
352
- " COMPUTE: begin\n",
353
- " // Compute weighted sum for current neuron\n",
354
- " if (input_index < INPUTS) begin\n",
355
- " // Multiply-accumulate (Q8.8 fixed-point)\n",
356
- " weighted_sum <= weighted_sum + \n",
357
- " ($signed(input_features[input_index]) * $signed(synaptic_weights[neuron_index][input_index]));\n",
358
- " input_index <= input_index + 1;\n",
359
- " end else begin\n",
360
- " // Update membrane potential with decay\n",
361
- " membrane_potentials[neuron_index] <= \n",
362
- " ($signed(membrane_potentials[neuron_index] * decay_constants[neuron_index]) >>> FIXED_POINT_SHIFT) + \n",
363
- " ($signed(weighted_sum) >>> FIXED_POINT_SHIFT);\n",
364
- " \n",
365
- " // Generate spike\n",
366
- " if ($signed(membrane_potentials[neuron_index]) >= $signed(neuron_thresholds[neuron_index])) begin\n",
367
- " spike_outputs[neuron_index] <= 1;\n",
368
- " membrane_potentials[neuron_index] <= 16'h0000; // Reset\n",
369
- " end else begin\n",
370
- " spike_outputs[neuron_index] <= 0;\n",
371
- " end\n",
372
- " \n",
373
- " // Move to next neuron\n",
374
- " if (neuron_index < NEURONS - 1) begin\n",
375
- " neuron_index <= neuron_index + 1;\n",
376
- " input_index <= 0;\n",
377
- " weighted_sum <= 32'h00000000;\n",
378
- " end else begin\n",
379
- " state <= OUTPUT;\n",
380
- " end\n",
381
- " end\n",
382
- " end\n",
383
- " \n",
384
- " OUTPUT: begin\n",
385
- " // Compute output classes (simple weighted sum of spikes)\n",
386
- " // Class 0: Neurons 0-5 (Kaspa)\n",
387
- " // Class 1: Neurons 6-10 (Monero)\n",
388
- " // Class 2: Neurons 11-15 (Other)\n",
389
- " \n",
390
- " output_class_0 <= spike_outputs[0] + spike_outputs[1] + spike_outputs[2] + \n",
391
- " spike_outputs[3] + spike_outputs[4] + spike_outputs[5];\n",
392
- " output_class_1 <= spike_outputs[6] + spike_outputs[7] + spike_outputs[8] + \n",
393
- " spike_outputs[9] + spike_outputs[10];\n",
394
- " output_class_2 <= spike_outputs[11] + spike_outputs[12] + spike_outputs[13] + \n",
395
- " spike_outputs[14] + spike_outputs[15];\n",
396
- " \n",
397
- " // Update debug signals\n",
398
- " active_neuron <= neuron_index;\n",
399
- " membrane_potential <= membrane_potentials[neuron_index];\n",
400
- " \n",
401
- " state <= IDLE;\n",
402
- " computation_done <= 1;\n",
403
- " end\n",
404
- " endcase\n",
405
- " end\n",
406
- "end\n",
407
- "\n",
408
- "// Initialize parameters from memory files (in simulation)\n",
409
- "initial begin\n",
410
- " // Load thresholds\n",
411
- " $readmemh(\"parameters/parameters.mem\", neuron_thresholds);\n",
412
- " // Load weights\n",
413
- " $readmemh(\"parameters/parameters_weights.mem\", synaptic_weights);\n",
414
- " // Load decay constants\n",
415
- " $readmemh(\"parameters/parameters_decay.mem\", decay_constants);\n",
416
- "end\n",
417
- "\n",
418
- "endmodule\n",
419
- "'''\n",
420
- "\n",
421
- "# Save Verilog code\n",
422
- "with open('spikenaut_snn_v2.v', 'w') as f:\n",
423
- " f.write(verilog_code)\n",
424
- "\n",
425
- "print(\"✅ Verilog module generated: spikenaut_snn_v2.v\")\n",
426
- "print(\"\\n📝 Key Features:\")\n",
427
- "print(\" • 16 neurons, 8 inputs, 3 outputs\")\n",
428
- "print(\" • Q8.8 fixed-point arithmetic\")\n",
429
- "print(\" • Parallel weighted sum computation\")\n",
430
- "print(\" • Configurable thresholds and decay\")\n",
431
- "print(\" • Debug signals for monitoring\")\n",
432
- "print(\" • Memory initialization from .mem files\")"
433
- ]
434
- },
435
- {
436
- "cell_type": "markdown",
437
- "metadata": {},
438
- "source": [
439
- "## 5. Testbench for Verification"
440
- ]
441
- },
442
- {
443
- "cell_type": "code",
444
- "execution_count": null,
445
- "metadata": {},
446
- "outputs": [],
447
- "source": [
448
- "# Generate testbench for FPGA verification\n",
449
- "testbench_code = '''\n",
450
- "// Testbench for Spikenaut SNN v2\n",
451
- "// Verifies correct operation of the FPGA implementation\n",
452
- "\n",
453
- "`timescale 1ns / 1ps\n",
454
- "\n",
455
- "module spikenaut_snn_v2_tb;\n",
456
- "\n",
457
- "// Test signals\n",
458
- "reg clk;\n",
459
- "reg rst_n;\n",
460
- "reg [15:0] input_features [0:7];\n",
461
- "reg start_computation;\n",
462
- "wire computation_done;\n",
463
- "wire [15:0] output_classes [0:2];\n",
464
- "wire [3:0] active_neuron;\n",
465
- "wire [15:0] membrane_potential;\n",
466
- "\n",
467
- "// Device Under Test\n",
468
- "spikenaut_snn_v2 dut (\n",
469
- " .clk(clk),\n",
470
- " .rst_n(rst_n),\n",
471
- " .input_feature_0(input_features[0]),\n",
472
- " .input_feature_1(input_features[1]),\n",
473
- " .input_feature_2(input_features[2]),\n",
474
- " .input_feature_3(input_features[3]),\n",
475
- " .input_feature_4(input_features[4]),\n",
476
- " .input_feature_5(input_features[5]),\n",
477
- " .input_feature_6(input_features[6]),\n",
478
- " .input_feature_7(input_features[7]),\n",
479
- " .start_computation(start_computation),\n",
480
- " .computation_done(computation_done),\n",
481
- " .output_class_0(output_classes[0]),\n",
482
- " .output_class_1(output_classes[1]),\n",
483
- " .output_class_2(output_classes[2]),\n",
484
- " .active_neuron(active_neuron),\n",
485
- " .membrane_potential(membrane_potential)\n",
486
- ");\n",
487
- "\n",
488
- "// Clock generation (1kHz)\n",
489
- "initial begin\n",
490
- " clk = 0;\n",
491
- " forever #500000 clk = ~clk; // 1ms period\n",
492
- "end\n",
493
- "\n",
494
- "// Test stimulus\n",
495
- "initial begin\n",
496
- " // Initialize inputs\n",
497
- " rst_n = 0;\n",
498
- " start_computation = 0;\n",
499
- " for (integer i = 0; i < 8; i = i + 1) begin\n",
500
- " input_features[i] = 16'h0000;\n",
501
- " end\n",
502
- " \n",
503
- " // Release reset\n",
504
- " #1000000; // 1ms\n",
505
- " rst_n = 1;\n",
506
- " #1000000; // 1ms\n",
507
- " \n",
508
- " // Test Case 1: Kaspa telemetry\n",
509
- " $display(\"Test Case 1: Kaspa telemetry\");\n",
510
- " input_features[0] = 16'h0066; // hashrate_spike = 1 (0.4 in Q8.8)\n",
511
- " input_features[1] = 16'h0000; // power_spike = 0\n",
512
- " input_features[2] = 16'h0000; // temp_spike = 0\n",
513
- " input_features[3] = 16'h00CC; // qubic_spike = 1 (0.8 in Q8.8)\n",
514
- " input_features[4] = 16'h0066; // hashrate_normalized = 0.4\n",
515
- " input_features[5] = 16'h0000; // power_efficiency = 0\n",
516
- " input_features[6] = 16'h0000; // thermal_efficiency = 0\n",
517
- " input_features[7] = 16'h00CC; // composite_reward = 0.8\n",
518
- " \n",
519
- " start_computation = 1;\n",
520
- " #2000000; // 2ms\n",
521
- " start_computation = 0;\n",
522
- " \n",
523
- " // Wait for completion\n",
524
- " wait(computation_done);\n",
525
- " #1000000; // 1ms\n",
526
- " \n",
527
- " $display(\"Results:\");\n",
528
- " $display(\" Class 0 (Kaspa): %d\", output_classes[0]);\n",
529
- " $display(\" Class 1 (Monero): %d\", output_classes[1]);\n",
530
- " $display(\" Class 2 (Other): %d\", output_classes[2]);\n",
531
- " \n",
532
- " // Test Case 2: Monero telemetry\n",
533
- " $display(\"Test Case 2: Monero telemetry\");\n",
534
- " input_features[0] = 16'h0000; // hashrate_spike = 0\n",
535
- " input_features[1] = 16'h00CC; // power_spike = 1 (0.8 in Q8.8)\n",
536
- " input_features[2] = 16'h0066; // temp_spike = 1 (0.4 in Q8.8)\n",
537
- " input_features[3] = 16'h0000; // qubic_spike = 0\n",
538
- " input_features[4] = 16'h0033; // hashrate_normalized = 0.2\n",
539
- " input_features[5] = 16'h0066; // power_efficiency = 0.4\n",
540
- " input_features[6] = 16'h0033; // thermal_efficiency = 0.2\n",
541
- " input_features[7] = 16'h0066; // composite_reward = 0.4\n",
542
- " \n",
543
- " start_computation = 1;\n",
544
- " #2000000; // 2ms\n",
545
- " start_computation = 0;\n",
546
- " \n",
547
- " // Wait for completion\n",
548
- " wait(computation_done);\n",
549
- " #1000000; // 1ms\n",
550
- " \n",
551
- " $display(\"Results:\");\n",
552
- " $display(\" Class 0 (Kaspa): %d\", output_classes[0]);\n",
553
- " $display(\" Class 1 (Monero): %d\", output_classes[1]);\n",
554
- " $display(\" Class 2 (Other): %d\", output_classes[2]);\n",
555
- " \n",
556
- " // Test Case 3: No activity\n",
557
- " $display(\"Test Case 3: No activity\");\n",
558
- " for (integer i = 0; i < 8; i = i + 1) begin\n",
559
- " input_features[i] = 16'h0000;\n",
560
- " end\n",
561
- " \n",
562
- " start_computation = 1;\n",
563
- " #2000000; // 2ms\n",
564
- " start_computation = 0;\n",
565
- " \n",
566
- " // Wait for completion\n",
567
- " wait(computation_done);\n",
568
- " #1000000; // 1ms\n",
569
- " \n",
570
- " $display(\"Results:\");\n",
571
- " $display(\" Class 0 (Kaspa): %d\", output_classes[0]);\n",
572
- " $display(\" Class 1 (Monero): %d\", output_classes[1]);\n",
573
- " $display(\" Class 2 (Other): %d\", output_classes[2]);\n",
574
- " \n",
575
- " // Finish simulation\n",
576
- " $display(\"All tests completed\");\n",
577
- " $finish;\n",
578
- "end\n",
579
- "\n",
580
- "// Monitor changes\n",
581
- "initial begin\n",
582
- " $monitor(\"Time: %0t | State: %s | Active Neuron: %d | Membrane: %d\",\n",
583
- " $time, dut.state, active_neuron, membrane_potential);\n",
584
- "end\n",
585
- "\n",
586
- "endmodule\n",
587
- "'''\n",
588
- "\n",
589
- "# Save testbench\n",
590
- "with open('spikenaut_snn_v2_tb.v', 'w') as f:\n",
591
- " f.write(testbench_code)\n",
592
- "\n",
593
- "print(\"✅ Testbench generated: spikenaut_snn_v2_tb.v\")\n",
594
- "print(\"\\n🧪 Test Cases:\")\n",
595
- "print(\" 1. Kaspa telemetry (should activate Class 0)\")\n",
596
- "print(\" 2. Monero telemetry (should activate Class 1)\")\n",
597
- "print(\" 3. No activity (baseline test)\")\n",
598
- "print(\"\\n⚡ Simulation Commands:\")\n",
599
- "print(\" vlog spikenaut_snn_v2.v spikenaut_snn_v2_tb.v\")\n",
600
- "print(\" vsim -t ps spikenaut_snn_v2_tb\")\n",
601
- "print(\" run -all\")"
602
- ]
603
- },
604
- {
605
- "cell_type": "markdown",
606
- "metadata": {},
607
- "source": [
608
- "## 6. Performance Analysis"
609
- ]
610
- },
611
- {
612
- "cell_type": "code",
613
- "execution_count": null,
614
- "metadata": {},
615
- "outputs": [],
616
- "source": [
617
- "# Performance estimation\n",
618
- "performance_metrics = {\n",
619
- " 'clock_frequency': '1 kHz',\n",
620
- " 'computation_cycles': 16 * 8 + 16, # 16 neurons * 8 inputs + overhead\n",
621
- " 'latency_ms': (16 * 8 + 16) / 1000, # At 1kHz clock\n",
622
- " 'throughput_samples_per_second': 1000 / ((16 * 8 + 16) / 1000),\n",
623
- " 'power_consumption_mw': 97,\n",
624
- " 'energy_per_inference_uj': 97 / 1000, # μJ per inference\n",
625
- " 'logic_utilization_percent': 15, # Estimated\n",
626
- " 'bram_utilization_percent': 5, # Estimated\n",
627
- " 'dsp_utilization_percent': 10 # Estimated\n",
628
- "}\n",
629
- "\n",
630
- "print(\"⚡ Performance Analysis:\")\n",
631
- "for metric, value in performance_metrics.items():\n",
632
- " print(f\" {metric}: {value}\")\n",
633
- "\n",
634
- "# Compare with software implementation\n",
635
- "software_comparison = {\n",
636
- " 'CPU (Python)': {'latency_ms': 50, 'power_mw': 15000},\n",
637
- " 'GPU (CUDA)': {'latency_ms': 5, 'power_mw': 250000},\n",
638
- " 'FPGA (Spikenaut)': {'latency_ms': performance_metrics['latency_ms'], 'power_mw': performance_metrics['power_consumption_mw']}\n",
639
- "}\n",
640
- "\n",
641
- "print(\"\\n🔄 Performance Comparison:\")\n",
642
- "for platform, metrics in software_comparison.items():\n",
643
- " print(f\" {platform}:\")\n",
644
- " print(f\" Latency: {metrics['latency_ms']} ms\")\n",
645
- " print(f\" Power: {metrics['power_mw']} mW\")\n",
646
- " print(f\" Energy: {metrics['latency_ms'] * metrics['power_mw'] / 1000:.2f} μJ\")\n",
647
- "\n",
648
- "# Calculate speedup and efficiency\n",
649
- "fpga_energy = performance_metrics['latency_ms'] * performance_metrics['power_consumption_mw'] / 1000\n",
650
- "cpu_energy = software_comparison['CPU (Python)']['latency_ms'] * software_comparison['CPU (Python)']['power_mw'] / 1000\n",
651
- "gpu_energy = software_comparison['GPU (CUDA)']['latency_ms'] * software_comparison['GPU (CUDA)']['power_mw'] / 1000\n",
652
- "\n",
653
- "print(f\"\\n🚀 Efficiency Improvements:\")\n",
654
- "print(f\" FPGA vs CPU: {cpu_energy / fpga_energy:.1f}x more energy efficient\")\n",
655
- "print(f\" FPGA vs GPU: {gpu_energy / fpga_energy:.1f}x more energy efficient\")\n",
656
- "print(f\" Latency improvement vs CPU: {software_comparison['CPU (Python)']['latency_ms'] / performance_metrics['latency_ms']:.1f}x\")\n",
657
- "print(f\" Latency improvement vs GPU: {software_comparison['GPU (CUDA)']['latency_ms'] / performance_metrics['latency_ms']:.1f}x\")\n",
658
- "\n",
659
- "# Visualize performance comparison\n",
660
- "import matplotlib.pyplot as plt\n",
661
- "\n",
662
- "platforms = list(software_comparison.keys())\n",
663
- "latencies = [software_comparison[p]['latency_ms'] for p in platforms]\n",
664
- "powers = [software_comparison[p]['power_mw'] for p in platforms]\n",
665
- "energies = [l * p / 1000 for l, p in zip(latencies, powers)]\n",
666
- "\n",
667
- "fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(15, 4))\n",
668
- "\n",
669
- "# Latency comparison\n",
670
- "ax1.bar(platforms, latencies, color=['blue', 'red', 'green'])\n",
671
- "ax1.set_ylabel('Latency (ms)')\n",
672
- "ax1.set_title('Latency Comparison')\n",
673
- "ax1.set_yscale('log')\n",
674
- "\n",
675
- "# Power comparison\n",
676
- "ax2.bar(platforms, powers, color=['blue', 'red', 'green'])\n",
677
- "ax2.set_ylabel('Power (mW)')\n",
678
- "ax2.set_title('Power Comparison')\n",
679
- "ax2.set_yscale('log')\n",
680
- "\n",
681
- "# Energy comparison\n",
682
- "ax3.bar(platforms, energies, color=['blue', 'red', 'green'])\n",
683
- "ax3.set_ylabel('Energy per Inference (μJ)')\n",
684
- "ax3.set_title('Energy Comparison')\n",
685
- "ax3.set_yscale('log')\n",
686
- "\n",
687
- "plt.tight_layout()\n",
688
- "plt.show()"
689
- ]
690
- },
691
- {
692
- "cell_type": "markdown",
693
- "metadata": {},
694
- "source": [
695
- "## 7. Deployment Checklist"
696
- ]
697
- },
698
- {
699
- "cell_type": "code",
700
- "execution_count": null,
701
- "metadata": {},
702
- "outputs": [],
703
- "source": [
704
- "# Deployment checklist\n",
705
- "deployment_checklist = {\n",
706
- " 'Hardware': [\n",
707
- " '✅ Basys3 FPGA board connected',\n",
708
- " '✅ USB-JTAG programmer configured',\n",
709
- " '✅ Power supply stable',\n",
710
- " '✅ Clock source verified'\n",
711
- " ],\n",
712
- " 'Software': [\n",
713
- " '✅ Vivado installed and licensed',\n",
714
- " '✅ Verilog testbench passing',\n",
715
- " '✅ Synthesis completed without errors',\n",
716
- " '✅ Implementation successful'\n",
717
- " ],\n",
718
- " 'Parameters': [\n",
719
- " '✅ Q8.8 conversion verified',\n",
720
- " '✅ Parameter files generated',\n",
721
- " '✅ Memory initialization tested',\n",
722
- " '✅ Weight loading confirmed'\n",
723
- " ],\n",
724
- " 'Verification': [\n",
725
- " '✅ Simulation results match expectations',\n",
726
- " '✅ Timing constraints met',\n",
727
- " '✅ Power analysis within budget',\n",
728
- " '✅ Resource utilization acceptable'\n",
729
- " ],\n",
730
- " 'Integration': [\n",
731
- " '✅ UART interface configured',\n",
732
- " '✅ GPIO connections verified',\n",
733
- " '✅ Real-time telemetry input tested',\n",
734
- " '✅ Output format validated'\n",
735
- " ]\n",
736
- "}\n",
737
- "\n",
738
- "print(\"🚀 FPGA Deployment Checklist:\")\n",
739
- "for category, items in deployment_checklist.items():\n",
740
- " print(f\"\\n{category}:\")\n",
741
- " for item in items:\n",
742
- " print(f\" {item}\")\n",
743
- "\n",
744
- "# Generate deployment script\n",
745
- "deployment_script = '''#!/bin/bash\n",
746
- "# Spikenaut SNN v2 FPGA Deployment Script\n",
747
- "\n",
748
- "echo \"🦁 Spikenaut SNN v2 - FPGA Deployment\"\n",
749
- "echo \"=========================================\"\n",
750
- "\n",
751
- "# Check prerequisites\n",
752
- "echo \"📋 Checking prerequisites...\"\n",
753
- "if ! command -v vivado &> /dev/null; then\n",
754
- " echo \"❌ Vivado not found. Please install Xilinx Vivado.\"\n",
755
- " exit 1\n",
756
- "fi\n",
757
- "echo \"✅ Vivado found\"\n",
758
- "\n",
759
- "# Check parameter files\n",
760
- "echo \"📂 Checking parameter files...\"\n",
761
- "for file in parameters/parameters.mem parameters/parameters_weights.mem parameters/parameters_decay.mem; do\n",
762
- " if [ ! -f \"$file\" ]; then\n",
763
- " echo \"❌ Missing file: $file\"\n",
764
- " exit 1\n",
765
- " fi\n",
766
- "done\n",
767
- "echo \"✅ All parameter files found\"\n",
768
- "\n",
769
- "# Run synthesis\n",
770
- "echo \"🔨 Running synthesis...\"\n",
771
- "vivado -mode batch -source synthesis_script.tcl\n",
772
- "if [ $? -ne 0 ]; then\n",
773
- " echo \"❌ Synthesis failed\"\n",
774
- " exit 1\n",
775
- "fi\n",
776
- "echo \"✅ Synthesis completed\"\n",
777
- "\n",
778
- "# Run implementation\n",
779
- "echo \"🏗️ Running implementation...\"\n",
780
- "vivado -mode batch -source implementation_script.tcl\n",
781
- "if [ $? -ne 0 ]; then\n",
782
- " echo \"❌ Implementation failed\"\n",
783
- " exit 1\n",
784
- "fi\n",
785
- "echo \"✅ Implementation completed\"\n",
786
- "\n",
787
- "# Generate bitstream\n",
788
- "echo \"💾 Generating bitstream...\"\n",
789
- "vivado -mode batch -source bitstream_script.tcl\n",
790
- "if [ $? -ne 0 ]; then\n",
791
- " echo \"❌ Bitstream generation failed\"\n",
792
- " exit 1\n",
793
- "fi\n",
794
- "echo \"✅ Bitstream generated\"\n",
795
- "\n",
796
- "# Program FPGA\n",
797
- "echo \"🔌 Programming FPGA...\"\n",
798
- "vivado -mode batch -source program_script.tcl\n",
799
- "if [ $? -ne 0 ]; then\n",
800
- " echo \"❌ FPGA programming failed\"\n",
801
- " exit 1\n",
802
- "fi\n",
803
- "echo \"✅ FPGA programmed successfully\"\n",
804
- "\n",
805
- "echo \"🎉 Deployment completed successfully!\"\n",
806
- "echo \"🦁 Spikenaut SNN v2 is running on FPGA!\"\n",
807
- "'''\n",
808
- "\n",
809
- "# Save deployment script\n",
810
- "with open('deploy_fpga.sh', 'w') as f:\n",
811
- " f.write(deployment_script)\n",
812
- "\n",
813
- "print(f\"\\n📜 Deployment script generated: deploy_fpga.sh\")\n",
814
- "print(f\"\\n🔧 Usage:\")\n",
815
- "print(f\" chmod +x deploy_fpga.sh\")\n",
816
- "print(f\" ./deploy_fpga.sh\")"
817
- ]
818
- },
819
- {
820
- "cell_type": "markdown",
821
- "metadata": {},
822
- "source": [
823
- "## 8. Troubleshooting Guide"
824
- ]
825
- },
826
- {
827
- "cell_type": "code",
828
- "execution_count": null,
829
- "metadata": {},
830
- "outputs": [],
831
- "source": [
832
- "# Common issues and solutions\n",
833
- "troubleshooting_guide = {\n",
834
- " 'Synthesis Errors': {\n",
835
- " 'Problem': 'Verilog synthesis fails',\n",
836
- " 'Solutions': [\n",
837
- " 'Check for syntax errors in Verilog code',\n",
838
- " 'Verify all signals are properly declared',\n",
839
- " 'Ensure memory initialization syntax is correct',\n",
840
- " 'Check clock domain crossing issues'\n",
841
- " ]\n",
842
- " },\n",
843
- " 'Timing Violations': {\n",
844
- " 'Problem': 'Timing constraints not met',\n",
845
- " 'Solutions': [\n",
846
- " 'Reduce clock frequency',\n",
847
- " 'Add pipeline stages',\n",
848
- " 'Optimize critical paths',\n",
849
- " 'Use DSP slices for multiplication'\n",
850
- " ]\n",
851
- " },\n",
852
- " 'Memory Issues': {\n",
853
- " 'Problem': 'Parameter loading fails',\n",
854
- " 'Solutions': [\n",
855
- " 'Verify .mem file format (hex values)',\n",
856
- " 'Check file paths in $readmemh',\n",
857
- " 'Ensure memory dimensions match',\n",
858
- " 'Test with known good values'\n",
859
- " ]\n",
860
- " },\n",
861
- " 'Incorrect Results': {\n",
862
- " 'Problem': 'FPGA output differs from simulation',\n",
863
- " 'Solutions': [\n",
864
- " 'Check Q8.8 precision handling',\n",
865
- " 'Verify signed arithmetic',\n",
866
- " 'Test with known input patterns',\n",
867
- " 'Compare intermediate values'\n",
868
- " ]\n",
869
- " },\n",
870
- " 'Power Issues': {\n",
871
- " 'Problem': 'Power consumption too high',\n",
872
- " 'Solutions': [\n",
873
- " 'Reduce clock frequency',\n",
874
- " 'Optimize logic utilization',\n",
875
- " 'Use clock gating',\n",
876
- " 'Enable power saving modes'\n",
877
- " ]\n",
878
- " }\n",
879
- "}\n",
880
- "\n",
881
- "print(\"🔧 Troubleshooting Guide:\")\n",
882
- "for issue, details in troubleshooting_guide.items():\n",
883
- " print(f\"\\n{issue}:\")\n",
884
- " print(f\" Problem: {details['Problem']}\")\n",
885
- " print(f\" Solutions:\")\n",
886
- " for solution in details['Solutions']:\n",
887
- " print(f\" • {solution}\")\n",
888
- "\n",
889
- "# Debug commands\n",
890
- "debug_commands = '''\n",
891
- "# Vivado debug commands\n",
892
- "# Open implemented design\n",
893
- "open_project spikenaut_snn_v2.xpr\n",
894
- "open_run impl_1\n",
895
- "\n",
896
- "# Check timing\n",
897
- "report_timing_summary\n",
898
- "report_timing -delay_type max -max_paths 10\n",
899
- "\n",
900
- "# Check utilization\n",
901
- "report_utilization\n",
902
- "report_utilization -hierarchical\n",
903
- "\n",
904
- "# Check power\n",
905
- "report_power\n",
906
- "\n",
907
- "# Debug signals (add to constraints)\n",
908
- "# In XDC file:\n",
909
- "# set_property DEBUG_TRUE [get_nets neuron_*]\n",
910
- "# set_property DEBUG_TRUE [get_nets membrane_*]\n",
911
- "\n",
912
- "# Simulation debug\n",
913
- "# Add to testbench:\n",
914
- "# $display(\"Neuron %d: membrane=%d, spike=%d\", i, membrane[i], spike[i]);\n",
915
- "# $strobe(\"Time=%0t, State=%s\", $time, state);\n",
916
- "'''\n",
917
- "\n",
918
- "print(f\"\\n💻 Debug Commands:\")\n",
919
- "print(debug_commands)"
920
- ]
921
- },
922
- {
923
- "cell_type": "markdown",
924
- "metadata": {},
925
- "source": [
926
- "## 9. Summary and Next Steps"
927
- ]
928
- },
929
- {
930
- "cell_type": "code",
931
- "execution_count": null,
932
- "metadata": {},
933
- "outputs": [],
934
- "source": [
935
- "print(\"🔧 Spikenaut SNN v2 FPGA Deployment Guide Complete!\")\n",
936
- "print(\"=\" * 60)\n",
937
- "print()\n",
938
- "print(\"🎯 What You've Accomplished:\")\n",
939
- "print(\" ✅ Understood Q8.8 fixed-point format\")\n",
940
- "print(\" ✅ Generated Verilog implementation\")\n",
941
- "print(\" ✅ Created comprehensive testbench\")\n",
942
- "print(\" ✅ Analyzed performance characteristics\")\n",
943
- "print(\" ✅ Prepared deployment checklist\")\n",
944
- "print(\" ✅ Generated troubleshooting guide\")\n",
945
- "print()\n",
946
- "print(\"📁 Generated Files:\")\n",
947
- "files_generated = [\n",
948
- " 'spikenaut_snn_v2.v - Main Verilog module',\n",
949
- " 'spikenaut_snn_v2_tb.v - Testbench',\n",
950
- " 'deploy_fpga.sh - Deployment script',\n",
951
- " 'parameters/ - FPGA parameter files'\n",
952
- "]\n",
953
- "for file in files_generated:\n",
954
- " print(f\" 📄 {file}\")\n",
955
- "print()\n",
956
- "print(\"⚡ Key Performance Metrics:\")\n",
957
- "print(f\" • Latency: {performance_metrics['latency_ms']:.1f} ms\")\n",
958
- "print(f\" • Power: {performance_metrics['power_consumption_mw']} mW\")\n",
959
- "print(f\" • Energy: {fpga_energy:.2f} μJ per inference\")\n",
960
- "print(f\" • Efficiency: {cpu_energy / fpga_energy:.1f}x vs CPU\")\n",
961
- "print()\n",
962
- "print(\"🚀 Next Steps:\")\n",
963
- "next_steps = [\n",
964
- " \"1. Run synthesis and implementation in Vivado\",\n",
965
- " \"2. Verify timing constraints are met\",\n",
966
- " \"3. Program Basys3 FPGA with generated bitstream\",\n",
967
- " \"4. Test with real telemetry data\",\n",
968
- " \"5. Integrate with Rust telemetry system\",\n",
969
- " \"6. Optimize for lower power consumption\",\n",
970
- " \"7. Scale to larger neural networks\"\n",
971
- "]\n",
972
- "for step in next_steps:\n",
973
- " print(f\" {step}\")\n",
974
- "print()\n",
975
- "print(\"🔗 Related Resources:\")\n",
976
- "resources = [\n",
977
- " \"• Dataset: https://huggingface.co/datasets/rmems/Spikenaut-SNN-v2-Telemetry-Data-Weights-Parameters\",\n",
978
- " \"• Main repo: https://github.com/rmems/Eagle-Lander\",\n",
979
- " \"• Basys3 documentation: https://reference.digilentinc.com/learn/programmable-logic/tutorials/basys-3-getting-started-with-xilinx-fpga-design-tools\",\n",
980
- " \"• Vivado documentation: https://docs.xilinx.com/v/u/en-US/ug953-vivado-tutorial\"\n",
981
- "]\n",
982
- "for resource in resources:\n",
983
- " print(f\" {resource}\")\n",
984
- "print()\n",
985
- "print(\"🦁 Happy FPGA deployment!\")\n",
986
- "print(\"Your Spikenaut SNN v2 is ready for neuromorphic computing on hardware!\")"
987
- ]
988
- }
989
- ],
990
- "metadata": {
991
- "kernelspec": {
992
- "display_name": "Python 3",
993
- "language": "python",
994
- "name": "python3"
995
- },
996
- "language_info": {
997
- "codemirror_mode": {
998
- "name": "ipython",
999
- "version": 3
1000
- },
1001
- "file_extension": ".py",
1002
- "name": "python",
1003
- "nbconvert_exporter": "python",
1004
- "pygments_lexer": "ipython3",
1005
- "version": "3.8.5"
1006
- }
1007
- },
1008
- "nbformat": 4,
1009
- "nbformat_minor": 4
1010
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dataset/examples/snn_training_demo.ipynb DELETED
@@ -1,871 +0,0 @@
1
- {
2
- "cells": [
3
- {
4
- "cell_type": "markdown",
5
- "metadata": {},
6
- "source": [
7
- "# 🧠 Spikenaut SNN v2 - Training Demo\n",
8
- "\n",
9
- "Complete training pipeline for Spiking Neural Networks using the Spikenaut dataset.\n",
10
- "\n",
11
- "## What you'll learn:\n",
12
- "- Setting up SNN architecture\n",
13
- "- Training with spike-encoded data\n",
14
- "- E-prop learning implementation\n",
15
- "- Performance evaluation\n",
16
- "- Model export for FPGA"
17
- ]
18
- },
19
- {
20
- "cell_type": "markdown",
21
- "metadata": {},
22
- "source": [
23
- "## 1. Setup and Dependencies"
24
- ]
25
- },
26
- {
27
- "cell_type": "code",
28
- "execution_count": null,
29
- "metadata": {},
30
- "outputs": [],
31
- "source": [
32
- "# Install required packages\n",
33
- "!pip install torch torchvision datasets numpy matplotlib seaborn tqdm -q\n",
34
- "\n",
35
- "import torch\n",
36
- "import torch.nn as nn\n",
37
- "import torch.nn.functional as F\n",
38
- "from torch.utils.data import DataLoader, TensorDataset\n",
39
- "import numpy as np\n",
40
- "import matplotlib.pyplot as plt\n",
41
- "import seaborn as sns\n",
42
- "from datasets import load_dataset\n",
43
- "from tqdm import tqdm\n",
44
- "import json\n",
45
- "import time\n",
46
- "from datetime import datetime\n",
47
- "\n",
48
- "print(f\"PyTorch version: {torch.__version__}\")\n",
49
- "print(f\"CUDA available: {torch.cuda.is_available()}\")\n",
50
- "if torch.cuda.is_available():\n",
51
- " print(f\"CUDA device: {torch.cuda.get_device_name()}\")\n",
52
- "\n",
53
- "# Set device\n",
54
- "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
55
- "print(f\"Using device: {device}\")"
56
- ]
57
- },
58
- {
59
- "cell_type": "markdown",
60
- "metadata": {},
61
- "source": [
62
- "## 2. Load and Prepare Data"
63
- ]
64
- },
65
- {
66
- "cell_type": "code",
67
- "execution_count": null,
68
- "metadata": {},
69
- "outputs": [],
70
- "source": [
71
- "# Load the Spikenaut dataset\n",
72
- "print(\"🦁 Loading Spikenaut SNN v2 dataset...\")\n",
73
- "ds = load_dataset(\"rmems/Spikenaut-SNN-v2-Telemetry-Data-Weights-Parameters\")\n",
74
- "\n",
75
- "# Extract spike-encoded features\n",
76
- "def extract_spikes(dataset_split):\n",
77
- " \"\"\"Extract spike features from dataset\"\"\"\n",
78
- " spike_cols = [\n",
79
- " 'spike_hashrate', 'spike_power', 'spike_temp', 'spike_qubic',\n",
80
- " 'hashrate_normalized', 'power_efficiency', 'thermal_efficiency',\n",
81
- " 'composite_reward'\n",
82
- " ]\n",
83
- " \n",
84
- " # Filter available columns\n",
85
- " available_cols = [col for col in spike_cols if col in dataset_split.column_names]\n",
86
- " print(f\"Available spike columns: {available_cols}\")\n",
87
- " \n",
88
- " # Convert to tensors\n",
89
- " data = []\n",
90
- " labels = []\n",
91
- " \n",
92
- " for i in range(len(dataset_split)):\n",
93
- " sample = dataset_split[i]\n",
94
- " \n",
95
- " # Create feature vector\n",
96
- " features = []\n",
97
- " for col in available_cols:\n",
98
- " if 'spike_' in col:\n",
99
- " features.append(float(sample[col])) # Binary spikes\n",
100
- " else:\n",
101
- " features.append(float(sample[col])) # Continuous features\n",
102
- " \n",
103
- " # Create label (blockchain type)\n",
104
- " blockchain = sample['blockchain']\n",
105
- " if blockchain == 'kaspa':\n",
106
- " label = 0\n",
107
- " elif blockchain == 'monero':\n",
108
- " label = 1\n",
109
- " else:\n",
110
- " label = 2\n",
111
- " \n",
112
- " data.append(features)\n",
113
- " labels.append(label)\n",
114
- " \n",
115
- " return torch.tensor(data, dtype=torch.float32), torch.tensor(labels, dtype=torch.long)\n",
116
- "\n",
117
- "# Prepare training data\n",
118
- "X_train, y_train = extract_spikes(ds['train'])\n",
119
- "X_val, y_val = extract_spikes(ds['validation'])\n",
120
- "X_test, y_test = extract_spikes(ds['test'])\n",
121
- "\n",
122
- "print(f\"📊 Data shapes:\")\n",
123
- "print(f\" Train: {X_train.shape}, Labels: {y_train.shape}\")\n",
124
- "print(f\" Val: {X_val.shape}, Labels: {y_val.shape}\")\n",
125
- "print(f\" Test: {X_test.shape}, Labels: {y_test.shape}\")\n",
126
- "\n",
127
- "# Create DataLoaders\n",
128
- "batch_size = 2 # Small batch due to small dataset\n",
129
- "\n",
130
- "train_dataset = TensorDataset(X_train, y_train)\n",
131
- "val_dataset = TensorDataset(X_val, y_val)\n",
132
- "test_dataset = TensorDataset(X_test, y_test)\n",
133
- "\n",
134
- "train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)\n",
135
- "val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)\n",
136
- "test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)\n",
137
- "\n",
138
- "print(f\"🔄 DataLoaders created with batch size {batch_size}\")"
139
- ]
140
- },
141
- {
142
- "cell_type": "markdown",
143
- "metadata": {},
144
- "source": [
145
- "## 3. SNN Architecture"
146
- ]
147
- },
148
- {
149
- "cell_type": "code",
150
- "execution_count": null,
151
- "metadata": {},
152
- "outputs": [],
153
- "source": [
154
- "class LIFNeuron(nn.Module):\n",
155
- " \"\"\"Leaky Integrate-and-Fire Neuron\"\"\"\n",
156
- " \n",
157
- " def __init__(self, input_size, hidden_size, threshold=1.0, decay=0.9):\n",
158
- " super(LIFNeuron, self).__init__()\n",
159
- " self.input_size = input_size\n",
160
- " self.hidden_size = hidden_size\n",
161
- " self.threshold = threshold\n",
162
- " self.decay = decay\n",
163
- " \n",
164
- " # Weight matrix\n",
165
- " self.weight = nn.Parameter(torch.randn(input_size, hidden_size) * 0.1)\n",
166
- " \n",
167
- " # Membrane potential\n",
168
- " self.register_buffer('membrane', torch.zeros(1, hidden_size))\n",
169
- " \n",
170
- " def forward(self, x):\n",
171
- " batch_size = x.size(0)\n",
172
- " \n",
173
- " # Initialize membrane potential for new batch\n",
174
- " if self.membrane.size(0) != batch_size:\n",
175
- " self.membrane = torch.zeros(batch_size, self.hidden_size, device=x.device)\n",
176
- " \n",
177
- " # Input current\n",
178
- " current = torch.matmul(x, self.weight)\n",
179
- " \n",
180
- " # Update membrane potential\n",
181
- " self.membrane = self.membrane * self.decay + current\n",
182
- " \n",
183
- " # Generate spikes\n",
184
- " spikes = (self.membrane > self.threshold).float()\n",
185
- " \n",
186
- " # Reset membrane potential after spike\n",
187
- " self.membrane = self.membrane * (1 - spikes)\n",
188
- " \n",
189
- " return spikes, self.membrane\n",
190
- "\n",
191
- "class SpikenautSNN(nn.Module):\n",
192
- " \"\"\"Spikenaut SNN v2 Architecture\"\"\"\n",
193
- " \n",
194
- " def __init__(self, input_size, hidden_size, num_classes, time_steps=10):\n",
195
- " super(SpikenautSNN, self).__init__()\n",
196
- " self.input_size = input_size\n",
197
- " self.hidden_size = hidden_size\n",
198
- " self.num_classes = num_classes\n",
199
- " self.time_steps = time_steps\n",
200
- " \n",
201
- " # Layers\n",
202
- " self.hidden_layer = LIFNeuron(input_size, hidden_size, threshold=0.5, decay=0.9)\n",
203
- " self.output_layer = nn.Linear(hidden_size, num_classes)\n",
204
- " \n",
205
- " # For E-prop learning\n",
206
- " self.register_buffer('eligibility_trace', torch.zeros(hidden_size, input_size))\n",
207
- " \n",
208
- " def forward(self, x):\n",
209
- " batch_size = x.size(0)\n",
210
- " \n",
211
- " # Store outputs for each time step\n",
212
- " spike_outputs = []\n",
213
- " membrane_outputs = []\n",
214
- " \n",
215
- " # Repeat input for time steps (simulation of temporal processing)\n",
216
- " for t in range(self.time_steps):\n",
217
- " # Add small noise to simulate temporal variation\n",
218
- " x_t = x + torch.randn_like(x) * 0.01\n",
219
- " \n",
220
- " # Forward through hidden layer\n",
221
- " hidden_spikes, hidden_membrane = self.hidden_layer(x_t)\n",
222
- " \n",
223
- " # Output layer (readout)\n",
224
- " output = self.output_layer(hidden_spikes)\n",
225
- " \n",
226
- " spike_outputs.append(output)\n",
227
- " membrane_outputs.append(hidden_membrane)\n",
228
- " \n",
229
- " # Average over time steps\n",
230
- " final_output = torch.mean(torch.stack(spike_outputs), dim=0)\n",
231
- " \n",
232
- " return final_output, torch.stack(membrane_outputs)\n",
233
- " \n",
234
- " def reset_state(self):\n",
235
- " \"\"\"Reset membrane potentials and traces\"\"\"\n",
236
- " self.hidden_layer.membrane.zero_()\n",
237
- " self.eligibility_trace.zero_()\n",
238
- "\n",
239
- "# Initialize SNN\n",
240
- "input_size = X_train.shape[1]\n",
241
- "hidden_size = 16 # Matching Spikenaut architecture\n",
242
- "num_classes = 3 # kaspa, monero, other\n",
243
- "time_steps = 10\n",
244
- "\n",
245
- "snn = SpikenautSNN(input_size, hidden_size, num_classes, time_steps).to(device)\n",
246
- "\n",
247
- "print(f\"🧠 SNN Architecture:\")\n",
248
- "print(f\" Input size: {input_size}\")\n",
249
- "print(f\" Hidden neurons: {hidden_size}\")\n",
250
- "print(f\" Output classes: {num_classes}\")\n",
251
- "print(f\" Time steps: {time_steps}\")\n",
252
- "print(f\" Total parameters: {sum(p.numel() for p in snn.parameters())}\")"
253
- ]
254
- },
255
- {
256
- "cell_type": "markdown",
257
- "metadata": {},
258
- "source": [
259
- "## 4. E-prop Learning Implementation"
260
- ]
261
- },
262
- {
263
- "cell_type": "code",
264
- "execution_count": null,
265
- "metadata": {},
266
- "outputs": [],
267
- "source": [
268
- "class EPropLoss(nn.Module):\n",
269
- " \"\"\"E-prop loss function with surrogate gradients\"\"\"\n",
270
- " \n",
271
- " def __init__(self, surrogate='fast_sigmoid'):\n",
272
- " super(EPropLoss, self).__init__()\n",
273
- " self.surrogate = surrogate\n",
274
- " \n",
275
- " def fast_sigmoid(self, x):\n",
276
- " \"\"\"Fast sigmoid surrogate gradient\"\"\"\n",
277
- " return 1.0 / (1.0 + torch.abs(x))\n",
278
- " \n",
279
- " def forward(self, output, target, membrane_potentials):\n",
280
- " \"\"\"Compute E-prop loss\"\"\"\n",
281
- " # Standard cross-entropy loss\n",
282
- " ce_loss = F.cross_entropy(output, target)\n",
283
- " \n",
284
- " # Add regularization term for spike activity\n",
285
- " spike_activity = torch.mean(membrane_potentials ** 2)\n",
286
- " regularization = 0.01 * spike_activity\n",
287
- " \n",
288
- " total_loss = ce_loss + regularization\n",
289
- " \n",
290
- " return total_loss, ce_loss, regularization\n",
291
- "\n",
292
- "class EPropOptimizer:\n",
293
- " \"\"\"Custom optimizer for E-prop learning\"\"\"\n",
294
- " \n",
295
- " def __init__(self, model, lr=0.001, beta=0.9):\n",
296
- " self.model = model\n",
297
- " self.lr = lr\n",
298
- " self.beta = beta\n",
299
- " \n",
300
- " # Initialize momentum\n",
301
- " self.momentum = {}\n",
302
- " for name, param in model.named_parameters():\n",
303
- " self.momentum[name] = torch.zeros_like(param)\n",
304
- " \n",
305
- " def step(self, loss):\n",
306
- " \"\"\"Perform E-prop optimization step\"\"\"\n",
307
- " # Backward pass\n",
308
- " loss.backward()\n",
309
- " \n",
310
- " # Update parameters with momentum\n",
311
- " for name, param in self.model.named_parameters():\n",
312
- " if param.grad is not None:\n",
313
- " # Update momentum\n",
314
- " self.momentum[name] = self.beta * self.momentum[name] + (1 - self.beta) * param.grad\n",
315
- " \n",
316
- " # Update parameters\n",
317
- " param.data = param.data - self.lr * self.momentum[name]\n",
318
- " \n",
319
- " # Clip gradients\n",
320
- " param.grad.data.clamp_(-1.0, 1.0)\n",
321
- " \n",
322
- " # Clear gradients\n",
323
- " self.model.zero_grad()\n",
324
- " \n",
325
- " def zero_grad(self):\n",
326
- " \"\"\"Zero gradients\"\"\"\n",
327
- " self.model.zero_grad()\n",
328
- "\n",
329
- "# Initialize loss and optimizer\n",
330
- "criterion = EPropLoss()\n",
331
- "optimizer = EPropOptimizer(snn, lr=0.01, beta=0.9)\n",
332
- "\n",
333
- "print(\"🔬 E-prop learning components initialized\")\n",
334
- "print(f\" Loss function: E-prop with fast sigmoid surrogate\")\n",
335
- "print(f\" Optimizer: Custom E-prop with momentum (lr=0.01, beta=0.9)\")"
336
- ]
337
- },
338
- {
339
- "cell_type": "markdown",
340
- "metadata": {},
341
- "source": [
342
- "## 5. Training Loop"
343
- ]
344
- },
345
- {
346
- "cell_type": "code",
347
- "execution_count": null,
348
- "metadata": {},
349
- "outputs": [],
350
- "source": [
351
- "def train_epoch(model, train_loader, criterion, optimizer, device):\n",
352
- " \"\"\"Train for one epoch\"\"\"\n",
353
- " model.train()\n",
354
- " total_loss = 0\n",
355
- " total_ce_loss = 0\n",
356
- " total_reg_loss = 0\n",
357
- " correct = 0\n",
358
- " total = 0\n",
359
- " \n",
360
- " for batch_idx, (data, target) in enumerate(train_loader):\n",
361
- " data, target = data.to(device), target.to(device)\n",
362
- " \n",
363
- " # Reset SNN state\n",
364
- " model.reset_state()\n",
365
- " \n",
366
- " # Forward pass\n",
367
- " output, membrane_potentials = model(data)\n",
368
- " \n",
369
- " # Compute loss\n",
370
- " loss, ce_loss, reg_loss = criterion(output, target, membrane_potentials)\n",
371
- " \n",
372
- " # Backward pass\n",
373
- " optimizer.step(loss)\n",
374
- " \n",
375
- " # Statistics\n",
376
- " total_loss += loss.item()\n",
377
- " total_ce_loss += ce_loss.item()\n",
378
- " total_reg_loss += reg_loss.item()\n",
379
- " \n",
380
- " # Accuracy\n",
381
- " pred = output.argmax(dim=1)\n",
382
- " correct += pred.eq(target).sum().item()\n",
383
- " total += target.size(0)\n",
384
- " \n",
385
- " avg_loss = total_loss / len(train_loader)\n",
386
- " avg_ce_loss = total_ce_loss / len(train_loader)\n",
387
- " avg_reg_loss = total_reg_loss / len(train_loader)\n",
388
- " accuracy = 100. * correct / total\n",
389
- " \n",
390
- " return avg_loss, avg_ce_loss, avg_reg_loss, accuracy\n",
391
- "\n",
392
- "def validate(model, val_loader, criterion, device):\n",
393
- " \"\"\"Validate the model\"\"\"\n",
394
- " model.eval()\n",
395
- " total_loss = 0\n",
396
- " correct = 0\n",
397
- " total = 0\n",
398
- " \n",
399
- " with torch.no_grad():\n",
400
- " for data, target in val_loader:\n",
401
- " data, target = data.to(device), target.to(device)\n",
402
- " \n",
403
- " # Reset SNN state\n",
404
- " model.reset_state()\n",
405
- " \n",
406
- " # Forward pass\n",
407
- " output, membrane_potentials = model(data)\n",
408
- " \n",
409
- " # Compute loss\n",
410
- " loss, ce_loss, reg_loss = criterion(output, target, membrane_potentials)\n",
411
- " \n",
412
- " total_loss += loss.item()\n",
413
- " \n",
414
- " # Accuracy\n",
415
- " pred = output.argmax(dim=1)\n",
416
- " correct += pred.eq(target).sum().item()\n",
417
- " total += target.size(0)\n",
418
- " \n",
419
- " avg_loss = total_loss / len(val_loader)\n",
420
- " accuracy = 100. * correct / total\n",
421
- " \n",
422
- " return avg_loss, accuracy\n",
423
- "\n",
424
- "print(\"🏃 Training functions defined\")"
425
- ]
426
- },
427
- {
428
- "cell_type": "markdown",
429
- "metadata": {},
430
- "source": [
431
- "## 6. Run Training"
432
- ]
433
- },
434
- {
435
- "cell_type": "code",
436
- "execution_count": null,
437
- "metadata": {},
438
- "outputs": [],
439
- "source": [
440
- "# Training configuration\n",
441
- "num_epochs = 50\n",
442
- "print(f\"🚀 Starting training for {num_epochs} epochs...\")\n",
443
- "print(f\"📊 Training samples: {len(train_loader.dataset)}\")\n",
444
- "print(f\"📊 Validation samples: {len(val_loader.dataset)}\")\n",
445
- "print()\n",
446
- "\n",
447
- "# Training history\n",
448
- "train_losses = []\n",
449
- "train_accuracies = []\n",
450
- "val_losses = []\n",
451
- "val_accuracies = []\n",
452
- "\n",
453
- "best_val_acc = 0\n",
454
- "best_model_state = None\n",
455
- "\n",
456
- "start_time = time.time()\n",
457
- "\n",
458
- "for epoch in range(num_epochs):\n",
459
- " # Train\n",
460
- " train_loss, train_ce_loss, train_reg_loss, train_acc = train_epoch(\n",
461
- " snn, train_loader, criterion, optimizer, device\n",
462
- " )\n",
463
- " \n",
464
- " # Validate\n",
465
- " val_loss, val_acc = validate(snn, val_loader, criterion, device)\n",
466
- " \n",
467
- " # Record history\n",
468
- " train_losses.append(train_loss)\n",
469
- " train_accuracies.append(train_acc)\n",
470
- " val_losses.append(val_loss)\n",
471
- " val_accuracies.append(val_acc)\n",
472
- " \n",
473
- " # Save best model\n",
474
- " if val_acc > best_val_acc:\n",
475
- " best_val_acc = val_acc\n",
476
- " best_model_state = snn.state_dict().copy()\n",
477
- " \n",
478
- " # Print progress\n",
479
- " if epoch % 10 == 0 or epoch == num_epochs - 1:\n",
480
- " print(f\"Epoch {epoch:3d}/{num_epochs:3d} | \"\n",
481
- " f\"Train Loss: {train_loss:.4f} (CE: {train_ce_loss:.4f}, Reg: {train_reg_loss:.4f}) | \"\n",
482
- " f\"Train Acc: {train_acc:5.2f}% | \"\n",
483
- " f\"Val Loss: {val_loss:.4f} | \"\n",
484
- " f\"Val Acc: {val_acc:5.2f}% | \"\n",
485
- " f\"Best Val Acc: {best_val_acc:5.2f}%\")\n",
486
- "\n",
487
- "training_time = time.time() - start_time\n",
488
- "print(f\"\\n✅ Training completed in {training_time:.2f} seconds\")\n",
489
- "print(f\"🏆 Best validation accuracy: {best_val_acc:.2f}%\")\n",
490
- "\n",
491
- "# Load best model\n",
492
- "snn.load_state_dict(best_model_state)\n",
493
- "print(\"📦 Best model loaded\")"
494
- ]
495
- },
496
- {
497
- "cell_type": "markdown",
498
- "metadata": {},
499
- "source": [
500
- "## 7. Training Visualization"
501
- ]
502
- },
503
- {
504
- "cell_type": "code",
505
- "execution_count": null,
506
- "metadata": {},
507
- "outputs": [],
508
- "source": [
509
- "# Create training visualization\n",
510
- "fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 5))\n",
511
- "\n",
512
- "# Loss curves\n",
513
- "ax1.plot(train_losses, label='Train Loss', color='blue', alpha=0.8)\n",
514
- "ax1.plot(val_losses, label='Validation Loss', color='red', alpha=0.8)\n",
515
- "ax1.set_xlabel('Epoch')\n",
516
- "ax1.set_ylabel('Loss')\n",
517
- "ax1.set_title('🦁 Spikenaut SNN v2 - Training Loss')\n",
518
- "ax1.legend()\n",
519
- "ax1.grid(True, alpha=0.3)\n",
520
- "\n",
521
- "# Accuracy curves\n",
522
- "ax2.plot(train_accuracies, label='Train Accuracy', color='blue', alpha=0.8)\n",
523
- "ax2.plot(val_accuracies, label='Validation Accuracy', color='red', alpha=0.8)\n",
524
- "ax2.set_xlabel('Epoch')\n",
525
- "ax2.set_ylabel('Accuracy (%)')\n",
526
- "ax2.set_title('🦁 Spikenaut SNN v2 - Training Accuracy')\n",
527
- "ax2.legend()\n",
528
- "ax2.grid(True, alpha=0.3)\n",
529
- "\n",
530
- "plt.tight_layout()\n",
531
- "plt.show()\n",
532
- "\n",
533
- "# Print final statistics\n",
534
- "print(f\"📈 Final Training Statistics:\")\n",
535
- "print(f\" Final train loss: {train_losses[-1]:.4f}\")\n",
536
- "print(f\" Final train accuracy: {train_accuracies[-1]:.2f}%\")\n",
537
- "print(f\" Final validation loss: {val_losses[-1]:.4f}\")\n",
538
- "print(f\" Final validation accuracy: {val_accuracies[-1]:.2f}%\")\n",
539
- "print(f\" Best validation accuracy: {best_val_acc:.2f}%\")\n",
540
- "print(f\" Training time: {training_time:.2f} seconds\")\n",
541
- "print(f\" Samples per second: {len(train_loader.dataset) * num_epochs / training_time:.1f}\")"
542
- ]
543
- },
544
- {
545
- "cell_type": "markdown",
546
- "metadata": {},
547
- "source": [
548
- "## 8. Model Evaluation"
549
- ]
550
- },
551
- {
552
- "cell_type": "code",
553
- "execution_count": null,
554
- "metadata": {},
555
- "outputs": [],
556
- "source": [
557
- "# Test the model\n",
558
- "print(\"🧪 Testing the trained SNN...\")\n",
559
- "\n",
560
- "test_loss, test_acc = validate(snn, test_loader, criterion, device)\n",
561
- "print(f\"Test Loss: {test_loss:.4f}\")\n",
562
- "print(f\"Test Accuracy: {test_acc:.2f}%\")\n",
563
- "\n",
564
- "# Detailed evaluation\n",
565
- "snn.eval()\n",
566
- "all_predictions = []\n",
567
- "all_targets = []\n",
568
- "all_outputs = []\n",
569
- "\n",
570
- "with torch.no_grad():\n",
571
- " for data, target in test_loader:\n",
572
- " data, target = data.to(device), target.to(device)\n",
573
- " \n",
574
- " # Reset SNN state\n",
575
- " snn.reset_state()\n",
576
- " \n",
577
- " # Forward pass\n",
578
- " output, membrane_potentials = snn(data)\n",
579
- " \n",
580
- " # Store results\n",
581
- " pred = output.argmax(dim=1)\n",
582
- " all_predictions.extend(pred.cpu().numpy())\n",
583
- " all_targets.extend(target.cpu().numpy())\n",
584
- " all_outputs.extend(output.cpu().numpy())\n",
585
- "\n",
586
- "# Convert to numpy arrays\n",
587
- "all_predictions = np.array(all_predictions)\n",
588
- "all_targets = np.array(all_targets)\n",
589
- "all_outputs = np.array(all_outputs)\n",
590
- "\n",
591
- "# Class names\n",
592
- "class_names = ['kaspa', 'monero', 'other']\n",
593
- "\n",
594
- "# Print classification report\n",
595
- "from sklearn.metrics import classification_report, confusion_matrix\n",
596
- "print(\"\\n📊 Classification Report:\")\n",
597
- "print(classification_report(all_targets, all_predictions, target_names=class_names))\n",
598
- "\n",
599
- "# Confusion matrix\n",
600
- "cm = confusion_matrix(all_targets, all_predictions)\n",
601
- "plt.figure(figsize=(8, 6))\n",
602
- "sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', \n",
603
- " xticklabels=class_names, yticklabels=class_names)\n",
604
- "plt.title('🦁 Spikenaut SNN v2 - Confusion Matrix')\n",
605
- "plt.xlabel('Predicted')\n",
606
- "plt.ylabel('Actual')\n",
607
- "plt.tight_layout()\n",
608
- "plt.show()"
609
- ]
610
- },
611
- {
612
- "cell_type": "markdown",
613
- "metadata": {},
614
- "source": [
615
- "## 9. Model Export for FPGA"
616
- ]
617
- },
618
- {
619
- "cell_type": "code",
620
- "execution_count": null,
621
- "metadata": {},
622
- "outputs": [],
623
- "source": [
624
- "def export_to_safetensors(model, filepath):\n",
625
- " \"\"\"Export model to safetensors format\"\"\"\n",
626
- " try:\n",
627
- " from safetensors.torch import save_file\n",
628
- " \n",
629
- " # Extract parameters\n",
630
- " state_dict = model.state_dict()\n",
631
- " \n",
632
- " # Save to safetensors\n",
633
- " save_file(state_dict, filepath)\n",
634
- " print(f\"✅ Model exported to {filepath}\")\n",
635
- " \n",
636
- " except ImportError:\n",
637
- " print(\"⚠️ safetensors not installed. Install with: pip install safetensors\")\n",
638
- " # Fallback to PyTorch format\n",
639
- " torch.save(model.state_dict(), filepath.replace('.safetensors', '.pth'))\n",
640
- " print(f\"✅ Model exported to {filepath.replace('.safetensors', '.pth')} (PyTorch format)\")\n",
641
- "\n",
642
- "def export_to_q8_8_format(model, filepath_prefix):\n",
643
- " \"\"\"Export model weights to Q8.8 format for FPGA\"\"\"\n",
644
- " \n",
645
- " def float_to_q8_8(value):\n",
646
- " \"\"\"Convert float to Q8.8 fixed-point\"\"\"\n",
647
- " # Clamp to Q8.8 range\n",
648
- " value = np.clip(value, -128, 127.996)\n",
649
- " # Convert to fixed-point\n",
650
- " q8_8 = int(value * 256)\n",
651
- " return q8_8\n",
652
- " \n",
653
- " # Extract weights\n",
654
- " hidden_weights = model.hidden_layer.weight.data.cpu().numpy()\n",
655
- " output_weights = model.output_layer.weight.data.cpu().numpy()\n",
656
- " \n",
657
- " # Convert to Q8.8\n",
658
- " hidden_weights_q8_8 = [[float_to_q8_8(w) for w in row] for row in hidden_weights]\n",
659
- " output_weights_q8_8 = [[float_to_q8_8(w) for w in row] for row in output_weights]\n",
660
- " \n",
661
- " # Write to .mem files\n",
662
- " with open(f\"{filepath_prefix}_hidden_weights.mem\", 'w') as f:\n",
663
- " for row in hidden_weights_q8_8:\n",
664
- " for weight in row:\n",
665
- " f.write(f\"{weight:04X}\\n\")\n",
666
- " \n",
667
- " with open(f\"{filepath_prefix}_output_weights.mem\", 'w') as f:\n",
668
- " for row in output_weights_q8_8:\n",
669
- " for weight in row:\n",
670
- " f.write(f\"{weight:04X}\\n\")\n",
671
- " \n",
672
- " # Thresholds and decay parameters\n",
673
- " with open(f\"{filepath_prefix}_parameters.mem\", 'w') as f:\n",
674
- " # Hidden layer threshold\n",
675
- " threshold_q8_8 = float_to_q8_8(model.hidden_layer.threshold)\n",
676
- " f.write(f\"{threshold_q8_8:04X}\\n\")\n",
677
- " \n",
678
- " # Hidden layer decay\n",
679
- " decay_q8_8 = float_to_q8_8(model.hidden_layer.decay)\n",
680
- " f.write(f\"{decay_q8_8:04X}\\n\")\n",
681
- " \n",
682
- " # Output layer parameters (if needed)\n",
683
- " for i in range(16): # Pad to 16 parameters\n",
684
- " f.write(f\"0000\\n\")\n",
685
- " \n",
686
- " print(f\"✅ Weights exported to Q8.8 format:\")\n",
687
- " print(f\" - {filepath_prefix}_hidden_weights.mem\")\n",
688
- " print(f\" - {filepath_prefix}_output_weights.mem\")\n",
689
- " print(f\" - {filepath_prefix}_parameters.mem\")\n",
690
- "\n",
691
- "# Export model\n",
692
- "print(\"📤 Exporting trained model...\")\n",
693
- "\n",
694
- "# Export to safetensors\n",
695
- "export_to_safetensors(snn, 'spikenaut_snn_v2.safetensors')\n",
696
- "\n",
697
- "# Export to Q8.8 for FPGA\n",
698
- "export_to_q8_8_format(snn, 'spikenaut_snn_v2')\n",
699
- "\n",
700
- "# Save training metadata\n",
701
- "metadata = {\n",
702
- " 'model_architecture': 'SpikenautSNN',\n",
703
- " 'input_size': input_size,\n",
704
- " 'hidden_size': hidden_size,\n",
705
- " 'num_classes': num_classes,\n",
706
- " 'time_steps': time_steps,\n",
707
- " 'training_accuracy': float(train_accuracies[-1]),\n",
708
- " 'validation_accuracy': float(best_val_acc),\n",
709
- " 'test_accuracy': float(test_acc),\n",
710
- " 'training_time_seconds': training_time,\n",
711
- " 'num_epochs': num_epochs,\n",
712
- " 'dataset': 'Spikenaut-SNN-v2-Telemetry-Data-Weights-Parameters',\n",
713
- " 'export_timestamp': datetime.now().isoformat()\n",
714
- "}\n",
715
- "\n",
716
- "with open('spikenaut_snn_v2_metadata.json', 'w') as f:\n",
717
- " json.dump(metadata, f, indent=2)\n",
718
- "\n",
719
- "print(f\"✅ Training metadata saved to spikenaut_snn_v2_metadata.json\")"
720
- ]
721
- },
722
- {
723
- "cell_type": "markdown",
724
- "metadata": {},
725
- "source": [
726
- "## 10. Inference Demo"
727
- ]
728
- },
729
- {
730
- "cell_type": "code",
731
- "execution_count": null,
732
- "metadata": {},
733
- "outputs": [],
734
- "source": [
735
- "def predict_blockchain(sample_features, model, device):\n",
736
- " \"\"\"Predict blockchain type from telemetry features\"\"\"\n",
737
- " model.eval()\n",
738
- " \n",
739
- " with torch.no_grad():\n",
740
- " # Convert to tensor\n",
741
- " if isinstance(sample_features, (list, np.ndarray)):\n",
742
- " sample_tensor = torch.tensor(sample_features, dtype=torch.float32).unsqueeze(0)\n",
743
- " else:\n",
744
- " sample_tensor = sample_features.unsqueeze(0)\n",
745
- " \n",
746
- " sample_tensor = sample_tensor.to(device)\n",
747
- " \n",
748
- " # Reset SNN state\n",
749
- " model.reset_state()\n",
750
- " \n",
751
- " # Forward pass\n",
752
- " output, membrane_potentials = model(sample_tensor)\n",
753
- " \n",
754
- " # Get prediction\n",
755
- " probabilities = F.softmax(output, dim=1)\n",
756
- " predicted_class = torch.argmax(probabilities, dim=1).item()\n",
757
- " confidence = probabilities[0][predicted_class].item()\n",
758
- " \n",
759
- " return {\n",
760
- " 'predicted_class': predicted_class,\n",
761
- " 'predicted_blockchain': class_names[predicted_class],\n",
762
- " 'confidence': confidence,\n",
763
- " 'probabilities': {\n",
764
- " class_names[i]: prob.item() \n",
765
- " for i, prob in enumerate(probabilities[0])\n",
766
- " },\n",
767
- " 'membrane_potentials': membrane_potentials[0].cpu().numpy()\n",
768
- " }\n",
769
- "\n",
770
- "# Test with sample data\n",
771
- "print(\"🔮 Running inference demo...\")\n",
772
- "\n",
773
- "# Test with a few samples\n",
774
- "for i in range(min(3, len(X_test))):\n",
775
- " sample_features = X_test[i]\n",
776
- " true_label = y_test[i].item()\n",
777
- " true_blockchain = class_names[true_label]\n",
778
- " \n",
779
- " result = predict_blockchain(sample_features, snn, device)\n",
780
- " \n",
781
- " print(f\"\\nSample {i+1}:\")\n",
782
- " print(f\" True blockchain: {true_blockchain}\")\n",
783
- " print(f\" Predicted: {result['predicted_blockchain']}\")\n",
784
- " print(f\" Confidence: {result['confidence']:.3f}\")\n",
785
- " print(f\" Probabilities: {result['probabilities']}\")\n",
786
- " print(f\" Correct: {'✅' if result['predicted_class'] == true_label else '❌'}\")\n",
787
- "\n",
788
- "# Visualize membrane potentials\n",
789
- "if len(result['membrane_potentials']) > 0:\n",
790
- " plt.figure(figsize=(10, 4))\n",
791
- " plt.plot(result['membrane_potentials'], marker='o', linestyle='-')\n",
792
- " plt.title('🧠 Membrane Potentials During Inference')\n",
793
- " plt.xlabel('Hidden Neuron Index')\n",
794
- " plt.ylabel('Membrane Potential')\n",
795
- " plt.grid(True, alpha=0.3)\n",
796
- " plt.show()"
797
- ]
798
- },
799
- {
800
- "cell_type": "markdown",
801
- "metadata": {},
802
- "source": [
803
- "## 11. Summary and Next Steps"
804
- ]
805
- },
806
- {
807
- "cell_type": "code",
808
- "execution_count": null,
809
- "metadata": {},
810
- "outputs": [],
811
- "source": [
812
- "print(\"🦁 Spikenaut SNN v2 Training Demo Complete!\")\n",
813
- "print(\"=\" * 50)\n",
814
- "print()\n",
815
- "print(\"🏆 Results Summary:\")\n",
816
- "print(f\" ✅ Trained {hidden_size}-neuron SNN for {num_epochs} epochs\")\n",
817
- "print(f\" ✅ Final test accuracy: {test_acc:.2f}%\")\n",
818
- "print(f\" ✅ Training time: {training_time:.2f} seconds\")\n",
819
- "print(f\" ✅ Model exported to multiple formats\")\n",
820
- "print()\n",
821
- "print(\"📁 Generated Files:\")\n",
822
- "print(\" 📄 spikenaut_snn_v2.safetensors - PyTorch model\")\n",
823
- "print(\" 📄 spikenaut_snn_v2_hidden_weights.mem - FPGA weights\")\n",
824
- "print(\" 📄 spikenaut_snn_v2_output_weights.mem - FPGA weights\")\n",
825
- "print(\" 📄 spikenaut_snn_v2_parameters.mem - FPGA parameters\")\n",
826
- "print(\" 📄 spikenaut_snn_v2_metadata.json - Training metadata\")\n",
827
- "print()\n",
828
- "print(\"🔬 Key Insights:\")\n",
829
- "print(f\" • E-prop learning achieved {best_val_acc:.1f}% validation accuracy\")\n",
830
- "print(f\" • SNN processes {input_size} features through {hidden_size} hidden neurons\")\n",
831
- "print(f\" • Temporal processing over {time_steps} time steps\")\n",
832
- "print(f\" • Q8.8 format ready for FPGA deployment\")\n",
833
- "print()\n",
834
- "print(\"🚀 Next Steps:\")\n",
835
- "print(\" 1. Deploy Q8.8 weights to Basys3 FPGA\")\n",
836
- "print(\" 2. Test with real-time telemetry data\")\n",
837
- "print(\" 3. Implement online learning/adaptation\")\n",
838
- "print(\" 4. Scale to larger datasets\")\n",
839
- "print(\" 5. Integrate with Julia-Rust hybrid pipeline\")\n",
840
- "print()\n",
841
- "print(\"📚 Related Resources:\")\n",
842
- "print(\" • Dataset: https://huggingface.co/datasets/rmems/Spikenaut-SNN-v2-Telemetry-Data-Weights-Parameters\")\n",
843
- "print(\" • FPGA deployment: See parameters/ folder\")\n",
844
- "print(\" • Main repository: https://github.com/rmems/Eagle-Lander\")\n",
845
- "print()\n",
846
- "print(\"🦁 Happy neuromorphic computing!\")"
847
- ]
848
- }
849
- ],
850
- "metadata": {
851
- "kernelspec": {
852
- "display_name": "Python 3",
853
- "language": "python",
854
- "name": "python3"
855
- },
856
- "language_info": {
857
- "codemirror_mode": {
858
- "name": "ipython",
859
- "version": 3
860
- },
861
- "file_extension": ".py",
862
- "mimetype": "text/x-python",
863
- "name": "python",
864
- "nbconvert_exporter": "python",
865
- "pygments_lexer": "ipython3",
866
- "version": "3.8.5"
867
- }
868
- },
869
- "nbformat": 4,
870
- "nbformat_minor": 4
871
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dataset/examples/spike_encoding_demo.ipynb DELETED
@@ -1,679 +0,0 @@
1
- {
2
- "cells": [
3
- {
4
- "cell_type": "markdown",
5
- "metadata": {},
6
- "source": [
7
- "# 🦁 Spikenaut SNN v2 - Spike Encoding Demo\n",
8
- "\n",
9
- "This notebook demonstrates how to load the Spikenaut SNN v2 dataset and create spike encodings for neuromorphic computing.\n",
10
- "\n",
11
- "## What you'll learn:\n",
12
- "- Loading the Hugging Face dataset\n",
13
- "- Understanding the data structure\n",
14
- "- Creating custom spike encodings\n",
15
- "- Visualizing spike trains\n",
16
- "- Preparing data for SNN training"
17
- ]
18
- },
19
- {
20
- "cell_type": "markdown",
21
- "metadata": {},
22
- "source": [
23
- "## 1. Setup and Imports"
24
- ]
25
- },
26
- {
27
- "cell_type": "code",
28
- "execution_count": null,
29
- "metadata": {},
30
- "outputs": [],
31
- "source": [
32
- "# Install required packages\n",
33
- "!pip install datasets numpy matplotlib seaborn scipy -q\n",
34
- "\n",
35
- "import json\n",
36
- "import numpy as np\n",
37
- "import pandas as pd\n",
38
- "import matplotlib.pyplot as plt\n",
39
- "import seaborn as sns\n",
40
- "from datasets import load_dataset\n",
41
- "from datetime import datetime\n",
42
- "import warnings\n",
43
- "warnings.filterwarnings('ignore')\n",
44
- "\n",
45
- "# Set style for better plots\n",
46
- "plt.style.use('seaborn-v0_8')\n",
47
- "sns.set_palette(\"husl\")"
48
- ]
49
- },
50
- {
51
- "cell_type": "markdown",
52
- "metadata": {},
53
- "source": [
54
- "## 2. Load the Dataset"
55
- ]
56
- },
57
- {
58
- "cell_type": "code",
59
- "execution_count": null,
60
- "metadata": {},
61
- "outputs": [],
62
- "source": [
63
- "# Load the Spikenaut SNN v2 dataset\n",
64
- "print(\"🦁 Loading Spikenaut SNN v2 dataset...\")\n",
65
- "ds = load_dataset(\"rmems/Spikenaut-SNN-v2-Telemetry-Data-Weights-Parameters\")\n",
66
- "\n",
67
- "# Examine the dataset structure\n",
68
- "print(f\"Dataset splits: {list(ds.keys())}\")\n",
69
- "print(f\"Training samples: {len(ds['train'])}\")\n",
70
- "print(f\"Validation samples: {len(ds['validation'])}\")\n",
71
- "print(f\"Test samples: {len(ds['test'])}\")\n",
72
- "\n",
73
- "# Show available features\n",
74
- "print(f\"\\nFeatures: {list(ds['train'].features.keys())}\")"
75
- ]
76
- },
77
- {
78
- "cell_type": "markdown",
79
- "metadata": {},
80
- "source": [
81
- "## 3. Explore the Data Structure"
82
- ]
83
- },
84
- {
85
- "cell_type": "code",
86
- "execution_count": null,
87
- "metadata": {},
88
- "outputs": [],
89
- "source": [
90
- "# Get a sample from the training set\n",
91
- "sample = ds['train'][0]\n",
92
- "print(\"Sample data structure:\")\n",
93
- "print(json.dumps(sample, indent=2, default=str))\n",
94
- "\n",
95
- "# Extract telemetry data\n",
96
- "telemetry = sample['telemetry']\n",
97
- "print(f\"\\n📊 Telemetry Summary:\")\n",
98
- "print(f\" Hashrate: {telemetry['hashrate_mh']} MH/s\")\n",
99
- "print(f\" Power: {telemetry['power_w']} W\")\n",
100
- "print(f\" Temperature: {telemetry['gpu_temp_c']} °C\")\n",
101
- "print(f\" Qubic Trace: {telemetry['qubic_tick_trace']}\")"
102
- ]
103
- },
104
- {
105
- "cell_type": "markdown",
106
- "metadata": {},
107
- "source": [
108
- "## 4. Basic Data Analysis"
109
- ]
110
- },
111
- {
112
- "cell_type": "code",
113
- "execution_count": null,
114
- "metadata": {},
115
- "outputs": [],
116
- "source": [
117
- "# Convert to pandas for easier analysis\n",
118
- "train_df = ds['train'].to_pandas()\n",
119
- "\n",
120
- "# Extract telemetry into separate columns\n",
121
- "telemetry_df = pd.json_normalize(train_df['telemetry'])\n",
122
- "full_df = pd.concat([train_df.drop('telemetry', axis=1), telemetry_df], axis=1)\n",
123
- "\n",
124
- "print(\"📈 Dataset Statistics:\")\n",
125
- "print(full_df.describe())\n",
126
- "\n",
127
- "# Show blockchain distribution\n",
128
- "print(f\"\\n🔗 Blockchain distribution:\")\n",
129
- "print(full_df['blockchain'].value_counts())"
130
- ]
131
- },
132
- {
133
- "cell_type": "markdown",
134
- "metadata": {},
135
- "source": [
136
- "## 5. Visualize Telemetry Data"
137
- ]
138
- },
139
- {
140
- "cell_type": "code",
141
- "execution_count": null,
142
- "metadata": {},
143
- "outputs": [],
144
- "source": [
145
- "# Create subplots for telemetry visualization\n",
146
- "fig, axes = plt.subplots(2, 3, figsize=(15, 10))\n",
147
- "fig.suptitle('🦁 Spikenaut SNN v2 - Telemetry Data Overview', fontsize=16)\n",
148
- "\n",
149
- "# Hashrate distribution\n",
150
- "axes[0, 0].hist(full_df['hashrate_mh'], bins=20, alpha=0.7, color='blue')\n",
151
- "axes[0, 0].set_title('Hashrate Distribution (MH/s)')\n",
152
- "axes[0, 0].set_xlabel('Hashrate (MH/s)')\n",
153
- "axes[0, 0].set_ylabel('Frequency')\n",
154
- "\n",
155
- "# Power consumption\n",
156
- "axes[0, 1].hist(full_df['power_w'], bins=20, alpha=0.7, color='red')\n",
157
- "axes[0, 1].set_title('Power Consumption (W)')\n",
158
- "axes[0, 1].set_xlabel('Power (W)')\n",
159
- "axes[0, 1].set_ylabel('Frequency')\n",
160
- "\n",
161
- "# GPU temperature\n",
162
- "axes[0, 2].hist(full_df['gpu_temp_c'], bins=20, alpha=0.7, color='orange')\n",
163
- "axes[0, 2].set_title('GPU Temperature (°C)')\n",
164
- "axes[0, 2].set_xlabel('Temperature (°C)')\n",
165
- "axes[0, 2].set_ylabel('Frequency')\n",
166
- "\n",
167
- "# Qubic trace\n",
168
- "axes[1, 0].hist(full_df['qubic_tick_trace'], bins=20, alpha=0.7, color='green')\n",
169
- "axes[1, 0].set_title('Qubic Tick Trace')\n",
170
- "axes[1, 0].set_xlabel('Qubic Trace')\n",
171
- "axes[1, 0].set_ylabel('Frequency')\n",
172
- "\n",
173
- "# Blockchain types\n",
174
- "blockchain_counts = full_df['blockchain'].value_counts()\n",
175
- "axes[1, 1].pie(blockchain_counts.values, labels=blockchain_counts.index, autopct='%1.1f%%')\n",
176
- "axes[1, 1].set_title('Blockchain Distribution')\n",
177
- "\n",
178
- "# Time series (if timestamps available)\n",
179
- "if 'timestamp' in full_df.columns:\n",
180
- " timestamps = pd.to_datetime(full_df['timestamp'])\n",
181
- " axes[1, 2].plot(timestamps, full_df['hashrate_mh'], marker='o', linestyle='-', alpha=0.7)\n",
182
- " axes[1, 2].set_title('Hashrate Over Time')\n",
183
- " axes[1, 2].set_xlabel('Time')\n",
184
- " axes[1, 2].set_ylabel('Hashrate (MH/s)')\n",
185
- " axes[1, 2].tick_params(axis='x', rotation=45)\n",
186
- "else:\n",
187
- " axes[1, 2].text(0.5, 0.5, 'Time series data\\nnot available', ha='center', va='center', transform=axes[1, 2].transAxes)\n",
188
- " axes[1, 2].set_title('Hashrate Over Time')\n",
189
- "\n",
190
- "plt.tight_layout()\n",
191
- "plt.show()"
192
- ]
193
- },
194
- {
195
- "cell_type": "markdown",
196
- "metadata": {},
197
- "source": [
198
- "## 6. Custom Spike Encoding"
199
- ]
200
- },
201
- {
202
- "cell_type": "code",
203
- "execution_count": null,
204
- "metadata": {},
205
- "outputs": [],
206
- "source": [
207
- "class SpikenautSpikeEncoder:\n",
208
- " \"\"\"Custom spike encoder for Spikenaut SNN v2 telemetry data\"\"\"\n",
209
- " \n",
210
- " def __init__(self):\n",
211
- " # Adaptive thresholds based on data statistics\n",
212
- " self.thresholds = {\n",
213
- " 'hashrate': 0.9, # MH/s\n",
214
- " 'power': 390, # Watts\n",
215
- " 'temp': 43, # Celsius\n",
216
- " 'qubic': 0.95 # Normalized\n",
217
- " }\n",
218
- " \n",
219
- " # Channel mapping for 16-neuron architecture\n",
220
- " self.channels = [\n",
221
- " 'kaspa_hashrate', 'kaspa_power', 'kaspa_temp', 'kaspa_qubic',\n",
222
- " 'monero_hashrate', 'monero_power', 'monero_temp', 'monero_qubic',\n",
223
- " 'qubic_hashrate', 'qubic_power', 'qubic_temp', 'qubic_qubic',\n",
224
- " 'thermal_stress', 'power_efficiency', 'network_health', 'composite_reward'\n",
225
- " ]\n",
226
- " \n",
227
- " def encode_telemetry(self, telemetry, blockchain):\n",
228
- " \"\"\"Encode telemetry data into 16-channel spike vector\"\"\"\n",
229
- " spikes = np.zeros(16)\n",
230
- " \n",
231
- " # Basic telemetry spikes\n",
232
- " spikes[0] = 1 if telemetry['hashrate_mh'] > self.thresholds['hashrate'] else 0\n",
233
- " spikes[1] = 1 if telemetry['power_w'] > self.thresholds['power'] else 0\n",
234
- " spikes[2] = 1 if telemetry['gpu_temp_c'] > self.thresholds['temp'] else 0\n",
235
- " spikes[3] = 1 if telemetry['qubic_tick_trace'] > self.thresholds['qubic'] else 0\n",
236
- " \n",
237
- " # Blockchain-specific mapping\n",
238
- " if blockchain == 'kaspa':\n",
239
- " spikes[0:4] = [spikes[0], spikes[1], spikes[2], spikes[3]]\n",
240
- " elif blockchain == 'monero':\n",
241
- " spikes[4:8] = [spikes[0], spikes[1], spikes[2], spikes[3]]\n",
242
- " elif blockchain == 'qubic':\n",
243
- " spikes[8:12] = [spikes[0], spikes[1], spikes[2], spikes[3]]\n",
244
- " \n",
245
- " # Derived spikes\n",
246
- " thermal_stress = max(0, (telemetry['gpu_temp_c'] - 40) / 6)\n",
247
- " spikes[12] = 1 if thermal_stress > 0.5 else 0\n",
248
- " \n",
249
- " power_efficiency = telemetry['hashrate_mh'] / (telemetry['power_w'] / 1000)\n",
250
- " spikes[13] = 1 if power_efficiency > 2.5 else 0\n",
251
- " \n",
252
- " network_health = (telemetry['qubic_tick_trace'] + telemetry['qubic_epoch_progress']) / 2\n",
253
- " spikes[14] = 1 if network_health > 0.95 else 0\n",
254
- " \n",
255
- " composite_reward = telemetry['reward_hint']\n",
256
- " spikes[15] = 1 if composite_reward > 0.95 else 0\n",
257
- " \n",
258
- " return spikes\n",
259
- " \n",
260
- " def encode_dataset(self, dataset):\n",
261
- " \"\"\"Encode entire dataset\"\"\"\n",
262
- " spike_trains = []\n",
263
- " \n",
264
- " for i in range(len(dataset)):\n",
265
- " sample = dataset[i]\n",
266
- " spikes = self.encode_telemetry(sample['telemetry'], sample['blockchain'])\n",
267
- " \n",
268
- " spike_trains.append({\n",
269
- " 'timestamp': sample.get('timestamp', f'sample_{i}'),\n",
270
- " 'blockchain': sample['blockchain'],\n",
271
- " 'spike_vector': spikes,\n",
272
- " 'spike_count': int(np.sum(spikes))\n",
273
- " })\n",
274
- " \n",
275
- " return spike_trains\n",
276
- "\n",
277
- "# Initialize encoder\n",
278
- "encoder = SpikenautSpikeEncoder()\n",
279
- "print(\"🔸 Spike encoder initialized\")\n",
280
- "print(f\"Channels: {encoder.channels}\")"
281
- ]
282
- },
283
- {
284
- "cell_type": "markdown",
285
- "metadata": {},
286
- "source": [
287
- "## 7. Generate Spike Trains"
288
- ]
289
- },
290
- {
291
- "cell_type": "code",
292
- "execution_count": null,
293
- "metadata": {},
294
- "outputs": [],
295
- "source": [
296
- "# Generate spike trains for training data\n",
297
- "print(\"🦁 Generating spike trains...\")\n",
298
- "spike_trains = encoder.encode_dataset(ds['train'])\n",
299
- "\n",
300
- "# Convert to numpy for analysis\n",
301
- "spike_matrix = np.array([train['spike_vector'] for train in spike_trains])\n",
302
- "\n",
303
- "print(f\"Generated {len(spike_trains)} spike trains\")\n",
304
- "print(f\"Spike matrix shape: {spike_matrix.shape}\")\n",
305
- "print(f\"Average spikes per sample: {spike_matrix.mean():.3f}\")\n",
306
- "print(f\"Spike rate: {spike_matrix.mean() * 1000:.1f} Hz\")\n",
307
- "\n",
308
- "# Show first few spike trains\n",
309
- "print(\"\\nFirst 5 spike trains:\")\n",
310
- "for i, train in enumerate(spike_trains[:5]):\n",
311
- " active_channels = np.where(train['spike_vector'] == 1)[0]\n",
312
- " print(f\" Sample {i}: {train['spike_count']} spikes -> channels {active_channels}\")"
313
- ]
314
- },
315
- {
316
- "cell_type": "markdown",
317
- "metadata": {},
318
- "source": [
319
- "## 8. Visualize Spike Trains"
320
- ]
321
- },
322
- {
323
- "cell_type": "code",
324
- "execution_count": null,
325
- "metadata": {},
326
- "outputs": [],
327
- "source": [
328
- "# Create spike raster plot\n",
329
- "fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8))\n",
330
- "\n",
331
- "# Raster plot\n",
332
- "for i in range(spike_matrix.shape[1]): # For each channel\n",
333
- " spike_times = np.where(spike_matrix[:, i] == 1)[0]\n",
334
- " ax1.scatter(spike_times, np.ones_like(spike_times) * i, \n",
335
- " s=20, alpha=0.8, label=encoder.channels[i] if i < 4 else \"\")\n",
336
- "\n",
337
- "ax1.set_xlabel('Time (samples)')\n",
338
- "ax1.set_ylabel('Channel')\n",
339
- "ax1.set_title('🦁 Spikenaut SNN v2 - Spike Raster Plot')\n",
340
- "ax1.grid(True, alpha=0.3)\n",
341
- "ax1.set_ylim(-0.5, 15.5)\n",
342
- "\n",
343
- "# Spike rate per channel\n",
344
- "spike_rates = spike_matrix.mean(axis=0)\n",
345
- "channel_labels = [f\"{i}: {name}\" for i, name in enumerate(encoder.channels)]\n",
346
- "\n",
347
- "bars = ax2.bar(range(16), spike_rates, alpha=0.7)\n",
348
- "ax2.set_xlabel('Channel')\n",
349
- "ax2.set_ylabel('Spike Rate')\n",
350
- "ax2.set_title('Spike Rate per Channel')\n",
351
- "ax2.set_xticks(range(16))\n",
352
- "ax2.set_xticklabels([f\"{i}\" for i in range(16)], rotation=45)\n",
353
- "ax2.grid(True, alpha=0.3)\n",
354
- "\n",
355
- "# Add channel labels on top of bars\n",
356
- "for i, (bar, rate) in enumerate(zip(bars, spike_rates)):\n",
357
- " if rate > 0:\n",
358
- " ax2.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.01, \n",
359
- " f'{rate:.2f}', ha='center', va='bottom', fontsize=8)\n",
360
- "\n",
361
- "plt.tight_layout()\n",
362
- "plt.show()"
363
- ]
364
- },
365
- {
366
- "cell_type": "markdown",
367
- "metadata": {},
368
- "source": [
369
- "## 9. Correlation Analysis"
370
- ]
371
- },
372
- {
373
- "cell_type": "code",
374
- "execution_count": null,
375
- "metadata": {},
376
- "outputs": [],
377
- "source": [
378
- "# Compute spike correlation matrix\n",
379
- "correlation_matrix = np.corrcoef(spike_matrix.T)\n",
380
- "\n",
381
- "# Create heatmap\n",
382
- "plt.figure(figsize=(10, 8))\n",
383
- "sns.heatmap(correlation_matrix, \n",
384
- " xticklabels=encoder.channels,\n",
385
- " yticklabels=encoder.channels,\n",
386
- " annot=True, \n",
387
- " cmap='coolwarm', \n",
388
- " center=0,\n",
389
- " fmt='.2f')\n",
390
- "plt.title('🦁 Spikenaut SNN v2 - Spike Correlation Matrix')\n",
391
- "plt.xticks(rotation=45, ha='right')\n",
392
- "plt.yticks(rotation=0)\n",
393
- "plt.tight_layout()\n",
394
- "plt.show()\n",
395
- "\n",
396
- "# Find most correlated channel pairs\n",
397
- "correlation_pairs = []\n",
398
- "for i in range(16):\n",
399
- " for j in range(i+1, 16):\n",
400
- " corr = correlation_matrix[i, j]\n",
401
- " if abs(corr) > 0.3: # Only show significant correlations\n",
402
- " correlation_pairs.append({\n",
403
- " 'channel1': encoder.channels[i],\n",
404
- " 'channel2': encoder.channels[j],\n",
405
- " 'correlation': corr\n",
406
- " })\n",
407
- "\n",
408
- "print(\"🔗 Significant channel correlations (|r| > 0.3):\")\n",
409
- "for pair in sorted(correlation_pairs, key=lambda x: abs(x['correlation']), reverse=True):\n",
410
- " print(f\" {pair['channel1']} ↔ {pair['channel2']}: r = {pair['correlation']:.3f}\")"
411
- ]
412
- },
413
- {
414
- "cell_type": "markdown",
415
- "metadata": {},
416
- "source": [
417
- "## 10. Prepare Data for SNN Training"
418
- ]
419
- },
420
- {
421
- "cell_type": "code",
422
- "execution_count": null,
423
- "metadata": {},
424
- "outputs": [],
425
- "source": [
426
- "class SNNTrainingData:\n",
427
- " \"\"\"Prepare data for Spiking Neural Network training\"\"\"\n",
428
- " \n",
429
- " def __init__(self, spike_trains, window_size=5):\n",
430
- " self.spike_trains = spike_trains\n",
431
- " self.window_size = window_size\n",
432
- " \n",
433
- " def create_sequences(self):\n",
434
- " \"\"\"Create sequences for time-series SNN training\"\"\"\n",
435
- " sequences = []\n",
436
- " targets = []\n",
437
- " \n",
438
- " spike_matrix = np.array([train['spike_vector'] for train in self.spike_trains])\n",
439
- " \n",
440
- " for i in range(len(spike_matrix) - self.window_size):\n",
441
- " # Input sequence\n",
442
- " sequence = spike_matrix[i:i + self.window_size]\n",
443
- " \n",
444
- " # Target (next timestep)\n",
445
- " target = spike_matrix[i + self.window_size]\n",
446
- " \n",
447
- " sequences.append(sequence)\n",
448
- " targets.append(target)\n",
449
- " \n",
450
- " return np.array(sequences), np.array(targets)\n",
451
- " \n",
452
- " def create_classification_dataset(self):\n",
453
- " \"\"\"Create dataset for classification tasks\"\"\"\n",
454
- " X = np.array([train['spike_vector'] for train in self.spike_trains])\n",
455
- " \n",
456
- " # Create labels based on blockchain type\n",
457
- " labels = []\n",
458
- " for train in self.spike_trains:\n",
459
- " if train['blockchain'] == 'kaspa':\n",
460
- " labels.append(0)\n",
461
- " elif train['blockchain'] == 'monero':\n",
462
- " labels.append(1)\n",
463
- " else:\n",
464
- " labels.append(2)\n",
465
- " \n",
466
- " return X, np.array(labels)\n",
467
- "\n",
468
- "# Prepare training data\n",
469
- "snn_data = SNNTrainingData(spike_trains, window_size=3)\n",
470
- "\n",
471
- "# Create sequences for time-series prediction\n",
472
- "X_seq, y_seq = snn_data.create_sequences()\n",
473
- "print(f\"🔄 Sequential data:\")\n",
474
- "print(f\" Sequences shape: {X_seq.shape}\")\n",
475
- "print(f\" Targets shape: {y_seq.shape}\")\n",
476
- "\n",
477
- "# Create classification dataset\n",
478
- "X_cls, y_cls = snn_data.create_classification_dataset()\n",
479
- "print(f\"\\n🎯 Classification data:\")\n",
480
- "print(f\" Features shape: {X_cls.shape}\")\n",
481
- "print(f\" Labels shape: {y_cls.shape}\")\n",
482
- "print(f\" Class distribution: {np.bincount(y_cls)}\")"
483
- ]
484
- },
485
- {
486
- "cell_type": "markdown",
487
- "metadata": {},
488
- "source": [
489
- "## 11. Simple SNN Example"
490
- ]
491
- },
492
- {
493
- "cell_type": "code",
494
- "execution_count": null,
495
- "metadata": {},
496
- "outputs": [],
497
- "source": [
498
- "class SimpleSNN:\n",
499
- " \"\"\"Simple Spiking Neural Network for demonstration\"\"\"\n",
500
- " \n",
501
- " def __init__(self, n_inputs=16, n_hidden=32, n_outputs=3):\n",
502
- " self.n_inputs = n_inputs\n",
503
- " self.n_hidden = n_hidden\n",
504
- " self.n_outputs = n_outputs\n",
505
- " \n",
506
- " # Initialize weights (small random values)\n",
507
- " self.W_in = np.random.randn(n_inputs, n_hidden) * 0.1\n",
508
- " self.W_out = np.random.randn(n_hidden, n_outputs) * 0.1\n",
509
- " \n",
510
- " # Neuron parameters\n",
511
- " self.threshold = 0.5\n",
512
- " self.decay = 0.9\n",
513
- " \n",
514
- " def forward(self, X):\n",
515
- " \"\"\"Forward pass through the SNN\"\"\"\n",
516
- " batch_size = X.shape[0]\n",
517
- " seq_len = X.shape[1] if len(X.shape) > 2 else 1\n",
518
- " \n",
519
- " # Reshape if needed\n",
520
- " if len(X.shape) == 2:\n",
521
- " X = X.reshape(batch_size, 1, -1)\n",
522
- " seq_len = 1\n",
523
- " \n",
524
- " # Initialize membrane potentials\n",
525
- " membrane_hidden = np.zeros((batch_size, self.n_hidden))\n",
526
- " membrane_out = np.zeros((batch_size, self.n_outputs))\n",
527
- " \n",
528
- " # Process sequence\n",
529
- " for t in range(seq_len):\n",
530
- " # Input to hidden\n",
531
- " hidden_input = np.dot(X[:, t, :], self.W_in)\n",
532
- " membrane_hidden = membrane_hidden * self.decay + hidden_input\n",
533
- " hidden_spikes = (membrane_hidden > self.threshold).astype(float)\n",
534
- " \n",
535
- " # Hidden to output\n",
536
- " out_input = np.dot(hidden_spikes, self.W_out)\n",
537
- " membrane_out = membrane_out * self.decay + out_input\n",
538
- " \n",
539
- " return membrane_out, hidden_spikes\n",
540
- "\n",
541
- "# Initialize and test SNN\n",
542
- "snn = SimpleSNN()\n",
543
- "print(\"🧠 Simple SNN initialized\")\n",
544
- "print(f\" Input neurons: {snn.n_inputs}\")\n",
545
- "print(f\" Hidden neurons: {snn.n_hidden}\")\n",
546
- "print(f\" Output neurons: {snn.n_outputs}\")\n",
547
- "\n",
548
- "# Test with sample data\n",
549
- "if len(X_seq) > 0:\n",
550
- " sample_input = X_seq[:1] # Take first sample\n",
551
- " output, hidden_spikes = snn.forward(sample_input)\n",
552
- " \n",
553
- " print(f\"\\n🔬 Test forward pass:\")\n",
554
- " print(f\" Input shape: {sample_input.shape}\")\n",
555
- " print(f\" Hidden spikes: {hidden_spikes.sum()} active\")\n",
556
- " print(f\" Output shape: {output.shape}\")\n",
557
- " print(f\" Output values: {output[0]}")"
558
- ]
559
- },
560
- {
561
- "cell_type": "markdown",
562
- "metadata": {},
563
- "source": [
564
- "## 12. Save Processed Data"
565
- ]
566
- },
567
- {
568
- "cell_type": "code",
569
- "execution_count": null,
570
- "metadata": {},
571
- "outputs": [],
572
- "source": [
573
- "# Save processed spike data for future use\n",
574
- "import pickle\n",
575
- "\n",
576
- "processed_data = {\n",
577
- " 'spike_trains': spike_trains,\n",
578
- " 'spike_matrix': spike_matrix,\n",
579
- " 'sequences': (X_seq, y_seq),\n",
580
- " 'classification': (X_cls, y_cls),\n",
581
- " 'encoder_channels': encoder.channels,\n",
582
- " 'thresholds': encoder.thresholds\n",
583
- "}\n",
584
- "\n",
585
- "# Save to pickle file\n",
586
- "with open('spikenaut_processed_data.pkl', 'wb') as f:\n",
587
- " pickle.dump(processed_data, f)\n",
588
- "\n",
589
- "print(\"💾 Processed data saved to 'spikenaut_processed_data.pkl'\")\n",
590
- "print(\"\\n📁 Files created:\")\n",
591
- "print(\" - spikenaut_processed_data.pkl (processed spike data)\")\n",
592
- "\n",
593
- "# Also save as JSON for compatibility\n",
594
- "json_data = {\n",
595
- " 'spike_trains': spike_trains,\n",
596
- " 'channels': encoder.channels,\n",
597
- " 'thresholds': encoder.thresholds,\n",
598
- " 'statistics': {\n",
599
- " 'total_samples': len(spike_trains),\n",
600
- " 'avg_spikes_per_sample': float(spike_matrix.mean()),\n",
601
- " 'spike_rate_hz': float(spike_matrix.mean() * 1000),\n",
602
- " 'most_active_channel': int(np.argmax(spike_matrix.mean(axis=0))),\n",
603
- " 'channel_correlation_avg': float(np.mean(np.abs(correlation_matrix)))\n",
604
- " }\n",
605
- "}\n",
606
- "\n",
607
- "with open('spike_analysis_results.json', 'w') as f:\n",
608
- " json.dump(json_data, f, indent=2)\n",
609
- "\n",
610
- "print(\" - spike_analysis_results.json (summary statistics)\")"
611
- ]
612
- },
613
- {
614
- "cell_type": "markdown",
615
- "metadata": {},
616
- "source": [
617
- "## 13. Summary and Next Steps"
618
- ]
619
- },
620
- {
621
- "cell_type": "code",
622
- "execution_count": null,
623
- "metadata": {},
624
- "outputs": [],
625
- "source": [
626
- "print(\"🦁 Spikenaut SNN v2 - Spike Encoding Demo Complete!\")\n",
627
- "print(\"=\" * 50)\n",
628
- "print()\n",
629
- "print(\"📊 What we accomplished:\")\n",
630
- "print(f\" ✅ Loaded {len(ds['train'])} training samples\")\n",
631
- "print(f\" ✅ Generated {len(spike_trains)} spike trains\")\n",
632
- "print(f\" ✅ Created {len(X_seq)} sequential samples\")\n",
633
- "print(f\" ✅ Built classification dataset with {len(X_cls)} samples\")\n",
634
- "print(f\" ✅ Analyzed spike correlations across 16 channels\")\n",
635
- "print(f\" ✅ Demonstrated simple SNN forward pass\")\n",
636
- "print()\n",
637
- "print(\"🔬 Key insights:\")\n",
638
- "print(f\" • Average spike rate: {spike_matrix.mean() * 1000:.1f} Hz\")\n",
639
- "print(f\" • Most active channel: {encoder.channels[np.argmax(spike_matrix.mean(axis=0))]}\")\n",
640
- "print(f\" • Spike correlation avg: {np.mean(np.abs(correlation_matrix)):.3f}\")\n",
641
- "print()\n",
642
- "print(\"🚀 Next steps for your research:\")\n",
643
- "print(\" 1. Train a full SNN using the sequential data\")\n",
644
- "print(\" 2. Experiment with different spike encoding thresholds\")\n",
645
- "print(\" 3. Try STDP learning rules on the spike trains\")\n",
646
- "print(\" 4. Deploy to FPGA using the provided parameters\")\n",
647
- "print(\" 5. Extend with real-time telemetry collection\")\n",
648
- "print()\n",
649
- "print(\"📚 Related resources:\")\n",
650
- "print(\" • Dataset: https://huggingface.co/datasets/rmems/Spikenaut-SNN-v2-Telemetry-Data-Weights-Parameters\")\n",
651
- "print(\" • Main repo: https://github.com/rmems/Eagle-Lander\")\n",
652
- "print(\" • FPGA deployment: See parameters/ folder\")\n",
653
- "print()\n",
654
- "print(\"🦁 Happy spiking!\")"
655
- ]
656
- }
657
- ],
658
- "metadata": {
659
- "kernelspec": {
660
- "display_name": "Python 3",
661
- "language": "python",
662
- "name": "python3"
663
- },
664
- "language_info": {
665
- "codemirror_mode": {
666
- "name": "ipython",
667
- "version": 3
668
- },
669
- "file_extension": ".py",
670
- "mimetype": "text/x-python",
671
- "name": "python",
672
- "nbconvert_exporter": "python",
673
- "pygments_lexer": "ipython3",
674
- "version": "3.8.5"
675
- }
676
- },
677
- "nbformat": 4,
678
- "nbformat_minor": 4
679
- }