{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tutorial 1: Getting Started with BrainEvent\n",
    "\n",
    "Welcome to the BrainEvent tutorial series! This tutorial will introduce you to BrainEvent's core data structure - **BinaryArray**, and how to use it for efficient event-driven computation.\n",
    "\n",
    "## 1. What is BinaryArray?\n",
    "\n",
    "In neuroscience modeling, brain computation is performed through **discrete spike events**. These spike events are binary (0 or 1), indicating whether a neuron fires at a specific moment.\n",
    "\n",
    "`BinaryArray`  is the core data structure provided by BrainEvent for representing these binary event arrays. After wrapping your data with `BinaryArray`, subsequent matrix operations will automatically leverage event-driven optimization algorithms, significantly improving computational efficiency.\n",
    "\n",
    "### Core Advantages\n",
    "- ✅ **Automatic Optimization**: Automatically leverage data sparsity for efficient computation\n",
    "- ✅ **Hardware Acceleration**: Support for multiple hardware platforms including CPU, GPU, TPU\n",
    "- ✅ **Seamless Integration**: Fully compatible with JAX/NumPy ecosystem\n",
    "- ✅ **Physical Units**: Support for BrainUnit's unit-aware computation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Installation and Import\n",
    "\n",
    "First, make sure you have BrainEvent installed:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Install BrainEvent\n",
    "# !pip install brainevent -U\n",
    "\n",
    "# Or install the complete BrainX ecosystem\n",
    "# !pip install BrainX -U"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-21T06:25:28.334552Z",
     "start_time": "2025-10-21T06:25:28.326448Z"
    }
   },
   "source": [
    "import brainevent\n",
    "import brainstate\n",
    "import jax.numpy as jnp\n",
    "import numpy as np\n",
    "import jax\n",
    "\n",
    "print(f\"BrainEvent version: {brainevent.__version__}\")\n",
    "print(f\"JAX version: {jax.__version__}\")"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "BrainEvent version: 0.0.4\n",
      "JAX version: 0.7.2\n"
     ]
    }
   ],
   "execution_count": 53
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Creating BinaryArray\n",
    "\n",
    "### 3.1 Creating from Regular Arrays\n",
    "\n",
    "`BinaryArray` Can be created from NumPy arrays, JAX arrays, or Python lists:"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-21T06:25:31.443378Z",
     "start_time": "2025-10-21T06:25:31.436863Z"
    }
   },
   "source": [
    "# Create from list\n",
    "event1 = brainevent.BinaryArray([1, 0, 1, 1, 0, 0, 1])\n",
    "print(\"BinaryArray from list:\")\n",
    "print(event1)\n",
    "print(f\"Shape: {event1.shape}, dtype: {event1.dtype}\")\n",
    "print()"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "BinaryArray from list:\n",
      "BinaryArray(value=Array([1, 0, 1, 1, 0, 0, 1]), dtype=int32)\n",
      "Shape: (7,), dtype: int32\n",
      "\n"
     ]
    }
   ],
   "execution_count": 54
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-21T06:25:33.600554Z",
     "start_time": "2025-10-21T06:25:33.596033Z"
    }
   },
   "source": [
    "# Create from NumPy array\n",
    "np_array = np.array([[1, 0, 1], [0, 1, 0]], dtype=bool)\n",
    "event2 = brainevent.BinaryArray(np_array)\n",
    "print(\"BinaryArray from NumPy array:\")\n",
    "print(event2)\n",
    "print()"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "BinaryArray from NumPy array:\n",
      "BinaryArray(value=Array([[ True, False,  True],\n",
      "                         [False,  True, False]]),\n",
      "            dtype=bool)\n",
      "\n"
     ]
    }
   ],
   "execution_count": 55
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-21T06:25:35.551602Z",
     "start_time": "2025-10-21T06:25:35.548090Z"
    }
   },
   "source": [
    "# Create from JAX array\n",
    "jax_array = jnp.array([1, 1, 0, 1], dtype=bool)\n",
    "event3 = brainevent.BinaryArray(jax_array)\n",
    "print(\"BinaryArray from JAX array:\")\n",
    "print(event3)"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "BinaryArray from JAX array:\n",
      "BinaryArray(value=Array([ True,  True, False,  True]), dtype=bool)\n"
     ]
    }
   ],
   "execution_count": 56
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.2 Simulating Neuron Spikes\n",
    "\n",
    "In practical applications, `BinaryArray` typically used to represent neuron spike firing patterns:"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-21T06:25:38.358675Z",
     "start_time": "2025-10-21T06:25:38.325637Z"
    }
   },
   "source": [
    "# Simulate spike states of 10 neurons at a moment\n",
    "# 1 indicates firing spike, 0 indicates resting\n",
    "num_neurons = 10\n",
    "spike_rate = 0.3  # 30%的神经元fired \n",
    "\n",
    "# Use brainstate.random to generate spike patterns\n",
    "brainstate.random.seed(0)\n",
    "spikes = brainstate.random.bernoulli(spike_rate, size=(num_neurons,))\n",
    "spike_array = brainevent.BinaryArray(spikes)\n",
    "\n",
    "print(f\"Neuron spike pattern (firing rate={spike_rate}):\")\n",
    "print(spike_array)\n",
    "print(f\"Number of firing neurons: {spike_array.sum()}\")"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Neuron spike pattern (firing rate=0.3):\n",
      "BinaryArray(value=Array([ True,  True, False, False,  True,  True,  True, False,  True,\n",
      "                         False]),\n",
      "            dtype=bool)\n",
      "Number of firing neurons: 6\n"
     ]
    }
   ],
   "execution_count": 57
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. Basic Operations of BinaryArray\n",
    "\n",
    "### 4.1 Access and Indexing"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-21T06:25:41.084271Z",
     "start_time": "2025-10-21T06:25:41.079252Z"
    }
   },
   "source": [
    "# Create a 2D BinaryArray\n",
    "events_2d = brainevent.BinaryArray([\n",
    "    [1, 0, 1, 0],\n",
    "    [0, 1, 1, 0],\n",
    "    [1, 1, 0, 1]\n",
    "])\n",
    "\n",
    "print(\"Original BinaryArray:\")\n",
    "print(events_2d)\n",
    "print()\n",
    "\n",
    "# Indexing operations\n",
    "print(\"First row:\", events_2d[0])\n",
    "print(\"Second column:\", events_2d[:, 1])\n",
    "print(\"Submatrix:\", events_2d[1:, :2])"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Original BinaryArray:\n",
      "BinaryArray(value=Array([[1, 0, 1, 0],\n",
      "                         [0, 1, 1, 0],\n",
      "                         [1, 1, 0, 1]]),\n",
      "            dtype=int32)\n",
      "\n",
      "First row: [1 0 1 0]\n",
      "Second column: [0 1 1]\n",
      "Submatrix: [[0 1]\n",
      " [1 1]]\n"
     ]
    }
   ],
   "execution_count": 58
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.2 Mathematical Operations"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-21T06:25:44.220392Z",
     "start_time": "2025-10-21T06:25:44.214425Z"
    }
   },
   "source": [
    "# Basic statistics\n",
    "print(\"Total spikes:\", events_2d.sum())\n",
    "print(\"Spikes per row:\", events_2d.sum(axis=1))\n",
    "print(\"Spikes per column:\", events_2d.sum(axis=0))\n",
    "print()\n",
    "\n",
    "# Logical operations\n",
    "event_a = brainevent.BinaryArray([1, 0, 1, 0])\n",
    "event_b = brainevent.BinaryArray([1, 1, 0, 0])\n",
    "\n",
    "print(\"Event A:\", event_a)\n",
    "print(\"Event B:\", event_b)\n",
    "print(\"A AND B:\", event_a & event_b)\n",
    "print(\"A OR B:\", event_a | event_b)"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total spikes: 7\n",
      "Spikes per row: [2 2 3]\n",
      "Spikes per column: [2 2 2 1]\n",
      "\n",
      "Event A: BinaryArray(value=Array([1, 0, 1, 0]), dtype=int32)\n",
      "Event B: BinaryArray(value=Array([1, 1, 0, 0]), dtype=int32)\n",
      "A AND B: [1 0 0 0]\n",
      "A OR B: [1 1 1 0]\n"
     ]
    }
   ],
   "execution_count": 59
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. Event-driven matrix multiplication\n",
    "\n",
    "This is `BinaryArray`'s most powerful feature! When `BinaryArray` is multiplied with a matrix, BrainEvent automatically leverages event sparsity for optimization.\n",
    "\n",
    "### 5.1 Multiplication with Dense Matrix"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-21T06:25:47.659812Z",
     "start_time": "2025-10-21T06:25:47.654165Z"
    }
   },
   "source": [
    "# Create BinaryArray (simulating presynaptic neuron spikes)\n",
    "pre_spikes = brainevent.BinaryArray([1, 0, 1, 0, 1])  # 5 neurons\n",
    "\n",
    "# Create weight matrix (5 presynaptic -> 3 postsynaptic)\n",
    "weights = jnp.array([\n",
    "    [0.5, 0.2, 0.1],  # neuron 0 -> post neurons\n",
    "    [0.3, 0.4, 0.2],  # neuron 1 -> post neurons\n",
    "    [0.1, 0.5, 0.3],  # neuron 2 -> post neurons\n",
    "    [0.2, 0.1, 0.4],  # neuron 3 -> post neurons\n",
    "    [0.4, 0.3, 0.5],  # neuron 4 -> post neurons\n",
    "])\n",
    "\n",
    "# Event-driven matrix multiplication\n",
    "# Only neurons that fired spikes(索引0, 2, 4)corresponding rows participate in computation\n",
    "post_input = pre_spikes @ weights\n",
    "\n",
    "print(\"Presynaptic spikes:\", pre_spikes)\n",
    "print(\"Weight matrix shape:\", weights.shape)\n",
    "print(\"Postsynaptic input:\", post_input)\n",
    "print()\n",
    "print(\"Explanation: Only neurons0, 2, 4fired spikes, so only these 3rows are computed for weighted sum\")"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Presynaptic spikes: BinaryArray(value=Array([1, 0, 1, 0, 1]), dtype=int32)\n",
      "Weight matrix shape: (5, 3)\n",
      "Postsynaptic input: [1.  1.  0.9]\n",
      "\n",
      "Explanation: Only neurons0, 2, 4fired spikes, so only these 3rows are computed for weighted sum\n"
     ]
    }
   ],
   "execution_count": 60
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.2 Performance Comparison: BinaryArray vs Regular Array\n",
    "\n",
    "Let's compare using `BinaryArray` and regular arrays in performance:"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-21T06:25:51.173316Z",
     "start_time": "2025-10-21T06:25:50.435912Z"
    }
   },
   "source": [
    "import time\n",
    "\n",
    "# Large-scale network parameters\n",
    "n_pre = 10000   # Number of presynaptic neurons\n",
    "n_post = 5000   # Number of postsynaptic neurons\n",
    "spike_rate = 0.05  # 5% firing rate (sparse spikes)\n",
    "\n",
    "# Set random seed for reproducibility\n",
    "brainstate.random.seed(42)\n",
    "\n",
    "# Generate sparse spikes\n",
    "spikes_bool = brainstate.random.bernoulli(spike_rate, size=(n_pre,))\n",
    "\n",
    "# Generate weight matrix\n",
    "weights = brainstate.random.normal(size=(n_pre, n_post)) * 0.1\n",
    "\n",
    "# Method 1: Using BinaryArray (event-driven)\n",
    "event_spikes = brainevent.BinaryArray(spikes_bool)\n",
    "\n",
    "# Method 2: Using regular array\n",
    "normal_spikes = spikes_bool.astype(jnp.float32)\n",
    "\n",
    "# Warmup and compilation\n",
    "_ = jax.block_until_ready(event_spikes @ weights)\n",
    "_ = jax.block_until_ready(normal_spikes @ weights)\n",
    "\n",
    "# Performance test\n",
    "n_trials = 100\n",
    "\n",
    "# BinaryArray method\n",
    "start = time.time()\n",
    "for _ in range(n_trials):\n",
    "    result_event = jax.block_until_ready(event_spikes @ weights)\n",
    "event_time = (time.time() - start) / n_trials\n",
    "\n",
    "# Regular array method\n",
    "start = time.time()\n",
    "for _ in range(n_trials):\n",
    "    result_normal = jax.block_until_ready(normal_spikes @ weights)\n",
    "normal_time = (time.time() - start) / n_trials\n",
    "\n",
    "print(f\"Network size: {n_pre} -> {n_post} neurons\")\n",
    "print(f\"Spike firing rate: {spike_rate*100}%\")\n",
    "print(f\"Actual firing count: {spikes_bool.sum()}\")\n",
    "print()\n",
    "print(f\"BinaryArray time: {event_time*1000:.3f} ms\")\n",
    "print(f\"Regular array time: {normal_time*1000:.3f} ms\")\n",
    "print(f\"Speedup: {normal_time/event_time:.2f}x\")\n",
    "print()\n",
    "print(\"Result consistency check:\", jnp.allclose(result_event, result_normal))"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Network size: 10000 -> 5000 neurons\n",
      "Spike firing rate: 5.0%\n",
      "Actual firing count: 516\n",
      "\n",
      "BinaryArray time: 0.685 ms\n",
      "Regular array time: 3.768 ms\n",
      "Speedup: 5.50x\n",
      "\n",
      "Result consistency check: True\n"
     ]
    }
   ],
   "execution_count": 61
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6. Practical Application Example: Feedforward Network\n",
    "\n",
    "Let's build a simple two-layer feedforward spiking neural network:"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-21T06:25:57.544760Z",
     "start_time": "2025-10-21T06:25:57.525234Z"
    }
   },
   "source": [
    "class SimpleSpikingNetwork:\n",
    "    \"\"\"Simple two-layer spiking neural network\"\"\"\n",
    "    \n",
    "    def __init__(self, n_input, n_hidden, n_output, seed=0):\n",
    "        self.n_input = n_input\n",
    "        self.n_hidden = n_hidden\n",
    "        self.n_output = n_output\n",
    "        \n",
    "        # Initialize weights - using brainstate.random\n",
    "        brainstate.random.seed(seed)\n",
    "        self.w1 = brainstate.random.normal(size=(n_input, n_hidden)) * 0.1\n",
    "        self.w2 = brainstate.random.normal(size=(n_hidden, n_output)) * 0.1\n",
    "    \n",
    "    def forward(self, input_spikes):\n",
    "        \"\"\"Forward propagation\n",
    "        \n",
    "        Args:\n",
    "            input_spikes: BinaryArray，Input layer spikes\n",
    "        \n",
    "        Returns:\n",
    "            Output layer activity values\n",
    "        \"\"\"\n",
    "        # First layer: Input -> Hidden\n",
    "        hidden_input = input_spikes @ self.w1\n",
    "        \n",
    "        # Hidden layer neurons: simple threshold activation\n",
    "        threshold = 0.5\n",
    "        hidden_spikes = brainevent.BinaryArray(hidden_input > threshold)\n",
    "        \n",
    "        # Second layer: Hidden -> Output\n",
    "        output = hidden_spikes @ self.w2\n",
    "        \n",
    "        return output, hidden_spikes\n",
    "\n",
    "# Create network\n",
    "network = SimpleSpikingNetwork(n_input=20, n_hidden=10, n_output=5, seed=123)\n",
    "\n",
    "# Generate input spikes\n",
    "brainstate.random.seed(456)\n",
    "input_pattern = brainstate.random.bernoulli(0.3, size=(20,))\n",
    "input_events = brainevent.BinaryArray(input_pattern)\n",
    "\n",
    "# Forward propagation\n",
    "output, hidden_spikes = network.forward(input_events)\n",
    "\n",
    "print(\"Input layer spikes:\", input_events)\n",
    "print(f\"Input layer firing count: {input_events.sum()}/20\")\n",
    "print()\n",
    "print(\"Hidden layer spikes:\", hidden_spikes)\n",
    "print(f\"Hidden layer firing count: {hidden_spikes.sum()}/10\")\n",
    "print()\n",
    "print(\"Output layer activity:\")\n",
    "print(output)\n",
    "print(f\"Output neuron with strongest response: {jnp.argmax(output)}\")"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input layer spikes: BinaryArray(value=Array([False, False,  True, False, False,  True, False, False, False,\n",
      "                         False,  True, False, False,  True,  True, False, False,  True,\n",
      "                         False,  True]),\n",
      "            dtype=bool)\n",
      "Input layer firing count: 7/20\n",
      "\n",
      "Hidden layer spikes: BinaryArray(value=Array([False, False, False, False, False, False, False, False, False,\n",
      "                         False]),\n",
      "            dtype=bool)\n",
      "Hidden layer firing count: 0/10\n",
      "\n",
      "Output layer activity:\n",
      "[0. 0. 0. 0. 0.]\n",
      "Output neuron with strongest response: 0\n"
     ]
    }
   ],
   "execution_count": 62
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7. Time Series Spike Patterns\n",
    "\n",
    "In actual neural network simulations, we typically need to process time series spike data:"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-21T06:26:04.408157Z",
     "start_time": "2025-10-21T06:26:04.348900Z"
    }
   },
   "source": [
    "# Simulate 100 time steps, each step has 50 neurons\n",
    "n_steps = 100\n",
    "n_neurons = 50\n",
    "spike_rate = 0.1\n",
    "\n",
    "# Generate time series spikes\n",
    "brainstate.random.seed(999)\n",
    "spike_trains = brainstate.random.bernoulli(spike_rate, size=(n_steps, n_neurons))\n",
    "\n",
    "# Create BinaryArray for each time step and compute network output\n",
    "brainstate.random.seed(0)\n",
    "weights = brainstate.random.normal(size=(n_neurons, 10)) * 0.1\n",
    "\n",
    "outputs = []\n",
    "for t in range(n_steps):\n",
    "    spikes_t = brainevent.BinaryArray(spike_trains[t])\n",
    "    output_t = spikes_t @ weights\n",
    "    outputs.append(output_t)\n",
    "\n",
    "outputs = jnp.stack(outputs)\n",
    "\n",
    "print(f\"Time series length: {n_steps} steps\")\n",
    "print(f\"Neurons per step: {n_neurons}\")\n",
    "print(f\"Output shape: {outputs.shape}\")\n",
    "print()\n",
    "print(f\"Total spikes: {spike_trains.sum()}\")\n",
    "print(f\"Average spikes per step: {spike_trains.sum(axis=1).mean():.1f}\")\n",
    "print(f\"Average firing rate per neuron: {spike_trains.mean():.3f}\")"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Time series length: 100 steps\n",
      "Neurons per step: 50\n",
      "Output shape: (100, 10)\n",
      "\n",
      "Total spikes: 503\n",
      "Average spikes per step: 5.0\n",
      "Average firing rate per neuron: 0.101\n"
     ]
    }
   ],
   "execution_count": 63
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 8. Visualizing Spike Patterns\n",
    "\n",
    "Let's use `braintools` to visualize neuron spike firing patterns (raster plot):"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-21T06:26:16.559107Z",
     "start_time": "2025-10-21T06:26:16.482934Z"
    }
   },
   "source": [
    "import braintools\n",
    "\n",
    "# Generate spike patterns\n",
    "n_steps = 200\n",
    "n_neurons = 30\n",
    "\n",
    "brainstate.random.seed(456)\n",
    "spike_trains = brainstate.random.bernoulli(0.15, size=(n_steps, n_neurons))\n",
    "\n",
    "# Use braintools.visualize.raster_plot to draw raster plot\n",
    "ts = np.arange(n_steps)\n",
    "\n",
    "braintools.visualize.raster_plot(\n",
    "    ts,\n",
    "    spike_trains,\n",
    "    markersize=4,\n",
    "    xlabel='Time Step',\n",
    "    ylabel='Neuron ID',\n",
    "    title='Spike Raster Plot - 30 Neurons over 200 Time Steps',\n",
    "    xlim=(0, n_steps),\n",
    "    ylim=(-1, n_neurons),\n",
    "    show=True\n",
    ")\n",
    "\n",
    "# Statistics\n",
    "print(f\"Total spikes: {spike_trains.sum()}\")\n",
    "print(f\"Average firing rate: {spike_trains.mean():.3f}\")\n",
    "print(f\"Most active neuron: {spike_trains.sum(axis=0).argmax()}, fired {spike_trains.sum(axis=0).max()} times\")"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ],
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkAAAAHHCAYAAABXx+fLAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAVBZJREFUeJzt3Qe8FNX9//9DVVAsKIhGuDbsHVGxYAFFY1RsKJpgC4oi9oKJgiURS4IlMZdEUWNUYomIRsUoChgFe40VFEUFOyIgfX6P9/l/Z/97l713Z3fPtJ3X8/HYx4XdnZkz50z57Jkz82nmeZ5nAAAAMqR53AUAAACIGgEQAADIHAIgAACQOQRAAAAgcwiAAABA5hAAAQCAzCEAAgAAmUMABAAAMocACAAAZA4BUMrdcccdplmzZmbGjBm59zbYYAPzi1/8ItZyZZHa4bLLLou7GEDmnXDCCfY4CDSFAChib731ljnyyCNNXV2dWXnllc3PfvYzs99++5k//elPJi0UbOlk77+aN29u2rdvbw488EAzZcqU0Ja7YMECG2BMnDjRxLWuLVq0MF26dDGHHXaYef31150s45133rHrlR/EuvK///3PHHXUUWajjTYybdu2NWuvvbbp2bOneeSRR4p+/9133zUHHHCAWXXVVW2b/upXvzJff/11oGX5dfTHP/6x0UD95ZdfrnqdEI333nvPXHjhhWb77bc37dq1M+uuu6456KCDGm3Dzz//3PTr18+sscYaZrXVVjOHHnqo+eijj4p+d/To0WaLLbawx8CuXbsGOv7l74dNvaI8PpRD+/eJJ55oNt54Y7venTp1svvi8OHDG3zvL3/5i91fEL5m5AKLzvPPP2/22WcfewI9/vjj7Q4wc+ZMM3XqVDN9+nQzbdq0sue5bNkys2TJErPSSivZnV/0y2frrbc2//73v0PbkTfccEPTv39/8/Of/9yW4YMPPrA77k8//WReeukls8022zhf7jfffGM6dOhgDxhR9bQUW1cFCfX19WbRokW27XSCENV/JWV74IEHbJDyzDPPmL333ttp+R977DFz0003mR49epj11lvPBpH/+te/zLPPPmv++te/mlNOOSX33c8++8zssMMOZvXVVzdnnnmmmTdvnvnDH/5gt9cXX3zRtG7dusll+dvfOuusY098Crh8OqDr4K9tY6eddnK6jgjH+eefbwOVI444wuy8887mhx9+sNuM9onx48eb3r17576rbWXHHXe03znvvPNMq1atzPXXX290etEPhbXWWiv3Xc1j0KBBdr59+vSx2+I//vEPc/XVV5uLLrqo0fLcddddDf5/5513mieffNJOm08/KBW8L1++3B4Xk0DH9u7du5s2bdqYk046yR6jZ82aZV599VXz+OOPm4ULF+a+q2O3fqgkNZCrKQqAEI2f//znXocOHbzvv/9+hc++/PJLZ8upq6vzDjroIC8sH3/8sYJm77rrrmvw/uOPP27fP+2000JZ7tdff23nP3z4cKfznTdvXtnr+vDDD9v3TznllNx7lZbt/vvvt9M+88wzXhSWLl3qbbfddt5mm23W4H21W5s2bbxPPvkk996TTz5py/bXv/615Hz1ve23397+/eMf/9jgs9tvv92+/9JLL3lRWr58ubdgwYJIl5kmTW37L7/8svfjjz82eO+bb76xx7Ddd9+9wfvXXHONbd8XX3wx9967777rtWjRwrv44otz76kt1lprrRWOT8cdd5y3yiqreN99913gsg8ePNguMw1OP/10r2XLlt6MGTNKHvu32morb6+99oqwdNnFJbAIqZdnq622sl3EhTp27LjCr+kzzjjD3H333WazzTazXabdunUzkydPLjkGqJi///3vpmXLluaCCy7IvffCCy/Yyx36xa9f63vttZd57rnnKl6/PffcM7ee+W6//Xaz77772nXUL7Itt9zS9qAUUte6fhHq149+KannRb+WROun3h+5/PLLc93d+b0t6rLX5UX9+lN9qafh4YcfLlpfkyZNMqeffrot0/rrr1/2ump95OOPP27ye6+99pq9NKhLArqs1KtXL9trlF8e9f6Iegej6MbXZbzOnTubOXPmNHhfPUMaO6YeH59+5W+66abmvvvuCzTv3Xff3dbNtddea3sDSwnSZmpjv3cp6Pi3J554ws5L25F6HES9UqprLUvb+6677moeffTRBvNUvWueWt/f//73dttQudRuhT20H374oe3FUE+uvqPvHnPMMbYXpJT777/f7s8qn7b3X/7yl/YSkk89byrHJ598ssK0F198se2N+/7778val/161CXXY4891qy55ppmjz32aLSMKp+22XzqydF+rl7Qwl5M9XDo5dt8881tveVvO+rl/Pbbb+2+l2/w4MFm/vz5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/RByKozEZ4N3RYO2g9ATrevq6uzfSj5PK9frVcn8klK3SSp7sfn67/Xr1y/QMpO0PlGKer2bmjYt9RnW+oc5j3L3B1fLdTGty3kggtvg04bb4AEASJ+5SboNXu68884mPx8wYEA15QEAAEheD9Caa67Z4P9LliwxCxYsMK1btzZt27Y13333nUkSeoAAAEifuSGfv8seA/T99983eClp6fvvv2/22GMPM2bMGOcFBAAAiD0AKqZr167m6quvNmeddZaL2QEAACQ/APKfEu3nBgMAAEiysgdBP/zwww3+ryFEs2bNsg9G3H333V2WDQAAIBkBUN++fRv8X9nfO3ToYPbdd1+bIR4AAKDmAqDly5eHUxIAAICkjwFavHixvftr6dKlJg2C5gJLY66guHPwhJGLqlpBc7pVkz+okuW6nG/S89aFlV/OdZuVu/xy6jvuNqiF/TbMenV97Aw7H1zQZVUyv6CSuE1XrNzcGfPnz/dOPPFEm3dFr+nTp9v3zzjjDG/EiBFe2nOBpTFXUNw5eMLIRVWtoDndqskfVMlyXc436Xnrwsov57rNyl1+OfUddxvUwn4bZr26PnaGnQ8u6LIqmV9QUW4XYecCKzsAOvPMM71u3bp5zz77rLfKKqvkAqCHHnrI23777b2k8Stw5MiRTuZXX19vG15/XU4b1nzDnEfQaVyUr1yllul/3r9/f6dlC2tdi8036DpGWe9hLj+sNit3+eXUd9xtUAv7bZj16vrYGdYxvtxlVTK/oKLcLsIOgMp+ErQStt17771m1113Ne3atTNvvPGG2Wijjcy0adPMjjvuaJ/cmCQ8CRoAgPSZm7QnQX/99demY8eOK7w/f/58e0cYAABA0pUdAO20007m0Ucfzf3fD3puvfVW06NHD7elAwAASMJt8FdddZU58MADzTvvvGPvALvxxhvtv59//nkzadKkMMoIAAAQbw+Qkp6+/vrrNvjZZpttzH/+8x97SWzKlCmmW7dubksHAAAQgrIHQacNg6ABAEifuUkbBA0AAJCZMUDNmzcveZeXPk/Lk6EBAEB2BQ6Axo4d2+hnGv9z0003kScMAACkQuBLYIceeugKr80339zccccd5g9/+IM56qijbG6wNHCdUygpuVHiKkepXD1x5KyJoi5c5ShKyvaTz0VOo7StVxrLFcX2FmWduT5uNDbvtIkr71cc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AABIMAIgAACQOTUfAK200kpm+PDh9m+WsN6sdxaw3qx3FrDeK4Uy/5rPBQYAAJC5HiAAAIBCBEAAACBzCIAAAEDmEAABAIDMqekA6OabbzYbbLCBWXnllc0uu+xiXnzxRVNLRowYYbp3727atWtnOnbsaPr27Wvef//9Bt/Ze++9TbNmzRq8Bg0aZNLssssuW2GdNt9889znCxcuNIMHDzZrrbWWWXXVVc0RRxxhvvzyS5N22pYL11svrWsttfXkyZPNwQcfbNZbbz27Dg899FCDz3XfxrBhw8y6665r2rRpY3r37m0+/PDDBt/57rvvzHHHHWcfGrfGGmuYk08+2cybN8+kdb2XLFliLrroIrPNNtuYVVZZxX5nwIAB5osvvii5jVx99dUmze19wgknrLBOBxxwQE23txTb1/W67rrrUt3eIwKct4Icw5Xd4aCDDjJt27a187ngggvM0qVLyypLzQZA9957rzn33HPtLXSvvvqq2W677UyfPn3MV199ZWrFpEmT7EYydepU8+STT9qD5P7772/mz5/f4HsDBw40s2bNyr2uvfZak3ZbbbVVg3X673//m/vsnHPOMY888oi5//77bR3pJHH44YebtHvppZcarLPaXI466qiaamttv9pf9QOmGK3TTTfdZEaNGmVeeOEFGxBo39ZB06eT4f/+9z9bR//+97/tyeaUU04xaV3vBQsW2OPYpZdeav8++OCD9qRxyCGHrPDdK664osE2MGTIEJPm9hYFPPnrNGbMmAaf11p7S/766nXbbbfZAEfBQJrbe1KA81apY/iyZcts8LN48WLz/PPPm7///e/mjjvusD+MyuLVqJ133tkbPHhw7v/Lli3z1ltvPW/EiBFerfrqq6/0SANv0qRJuff22msv76yzzvJqyfDhw73tttuu6Gdz5szxWrVq5d1///259959911bL1OmTPFqidp144039pYvX16zba12Gzt2bO7/WtdOnTp51113XYM2X2mllbwxY8bY/7/zzjt2updeein3nccff9xr1qyZ9/nnn3tpXO9iXnzxRfu9Tz75JPdeXV2dd/3113tpVWy9jz/+eO/QQw9tdJqstLfqYN99923wXtrbu9h5K8gx/LHHHvOaN2/uzZ49O/ed+vp6b7XVVvMWLVrkBVWTPUCKCl955RXbNZ6fS0T/nzJliqnlxHFSmDju7rvvNmuvvbbZeuutzcUXX2x/TaadLnmo63ijjTayv/7UHSpqd/2iyG97XR5TQr1aantt43fddZc56aST7K/CWm7rfB9//LGZPXt2g/ZVnj9d4vbbV391GWSnnXbKfUff1zFAPUa1tL+r7bWu+XQJRJcOdthhB3u5pNzLAkk0ceJEe5ljs802M6eddprNDu7LQnvr8s+jjz5qL+0VSnt7/1Bw3gpyDNdfXQ5eZ511ct9RL7CSxqonMNPJUL/55hvbRZZfOaL/v/fee6YWLV++3Jx99tlm9913tyc/37HHHmvq6upssPDmm2/acQTqOlcXelrpZKfuTh0M1eV7+eWXmz333NO8/fbb9uTYunXrFU4Kant9Vis0XmDOnDl2fEQtt3Uhvw2L7dv+Z/qrk2W+li1b2gNsrWwDutyn9u3fv3+D5Jhnnnmm2XHHHe266tKAgmDtIyNHjjRppctfuvyx4YYbmunTp5vf/OY35sADD7QnwRYtWmSivXWJR2NmCi/lp729lxc5bwU5hutvsWOA/1mmA6As0jVVBQD5Y2Ek/zq4ImYNHO3Vq5c9kGy88cYmjXTw82277bY2INKJ/7777rODYrNg9OjRth4U7NRyW2NF+nXcr18/Oxi8vr6+wWca95i/b+hEcuqpp9qBp2lNo3DMMcc02K61Xtqe1Suk7TsLNP5HPd26oaeW2ntwI+etqNTkJTBdAtAvg8JR4/p/p06dTK0544wz7MC/Z555xqy//vpNflfBgkybNs3UCv1S2HTTTe06qX11eUi9I7Xa9p988ol56qmnzK9//evMtbXfhk3t2/pbeLODLgvoTqG0bwN+8KNtQANI83t/GtsGtO4zZswwtUKXvXWM97frWm5vefbZZ21Pbqn9PW3tfUYj560gx3D9LXYM8D/LdACkKLhbt25mwoQJDbra9P8ePXqYWqFfgNqIxo4da55++mnbRVzK66+/bv+qd6BW6HZX9XJondTurVq1atD2OnhojFCttP3tt99uu/x1F0TW2lrbuA5w+e2r6/4a6+G3r/7q4KmxBD7tHzoG+EFhmoMfjX9TAKxxH6VoG9BYmMJLRGn22Wef2TFA/nZdq+2d39ur45ruGKuF9vZKnLeCHMP196233moQ+Po/CLbccsuyClOT/vnPf9o7Q+644w57l8App5zirbHGGg1Gjafdaaed5q2++urexIkTvVmzZuVeCxYssJ9PmzbNu+KKK7yXX37Z+/jjj71x48Z5G220kdezZ08vzc477zy7zlqn5557zuvdu7e39tpr27sJZNCgQV6XLl28p59+2q57jx497KsW6G5GrdtFF13U4P1aausff/zRe+211+xLh6iRI0faf/t3O1199dV2X9Y6vvnmm/bumA033ND76aefcvM44IADvB122MF74YUXvP/+979e165dvf79+3tpXe/Fixd7hxxyiLf++ut7r7/+eoP93b/r5fnnn7d3BOnz6dOne3fddZfXoUMHb8CAAV5a11ufnX/++fbuH23XTz31lLfjjjva9ly4cGHNtrfvhx9+8Nq2bWvvcCqU1vY+rcR5K8gxfOnSpd7WW2/t7b///nb9x48fb9f94osvLqssNRsAyZ/+9Cdbia1bt7a3xU+dOtWrJdppir1uv/12+/mnn35qT4Dt27e3weAmm2ziXXDBBXanSrOjjz7aW3fddW27/uxnP7P/VwDg04nw9NNP99Zcc0178DjssMPsDlYLnnjiCdvG77//foP3a6mtn3nmmaLbtW6H9m+Fv/TSS7111lnHrmuvXr1WqI9vv/3WngBXXXVVe2vsiSeeaE84aV1vnfwb2981nbzyyiveLrvsYk8uK6+8srfFFlt4V111VYNAIW3rrZOiTnI6uenWaN32PXDgwBV+yNZae/v++te/em3atLG3hhdKa3ubEuetoMfwGTNmeAceeKCtH/0A1g/jJUuWlFWWZv9XIAAAgMyoyTFAAAAATSEAAgAAmUMABAAAMocACAAAZA4BEAAAyBwCIAAAkDkEQAAAIHMIgACEQpnq+/btG3cxAKAoAiAAZWvWrFmTr8suu8zceOON5o477oilfLfccovNnbTqqqvaZLk77LCDzZDtIzgD0DLuAgBIn1mzZuX+fe+995phw4bZhIU+BR56xeG2224zZ599trnpppvMXnvtZRYtWmTefPNN8/bbb8dSHgDJRA8QgLIpI7v/Wn311W2vT/57Cn4Ke1n23ntvM2TIEBucrLnmmmadddaxPTXz5883J554omnXrp3ZZJNNzOOPP95gWQpcDjzwQDtPTfOrX/3KfPPNN42W7eGHH7ZZ008++WQ7v6222sr079/f/P73v7efq3fq73//uxk3blyux2rixIn2s5kzZ9pp1WvUvn17c+ihh5oZM2bk5u2v0+WXX246dOhgs08PGjTILF68OIRaBhAmAiAAkVHgsfbaa5sXX3zRBkOnnXaaOeqoo8xuu+1mXn31VbP//vvbAGfBggX2+3PmzDH77ruvvYT18ssvm/Hjx5svv/zSBimNUQA2depU88knnxT9/Pzzz7fTH3DAAbYnSy8tf8mSJaZPnz42EHv22WfNc889Z4MufS8/wJkwYYJ59913bdA0ZswY8+CDD9qACEDKuMrwCiCblMVZGakLKav1oYcemvv/Xnvt5e2xxx65/y9dutRbZZVVvF/96le595TxWYelKVOm2P9feeWVNht4vpkzZ9rvFGaA933xxRferrvuar+z6aab2nLce++93rJlyxotm/zjH//wNttsM5tt3rdo0SKbbfqJJ57ITde+fXtv/vz5ue/U19fbLOT58weQfPQAAYjMtttum/t3ixYtzFprrWW22Wab3Hu6xCVfffWV/fvGG2+YZ555JjemSK/NN9/cfjZ9+vSiy1h33XXNlClTzFtvvWXOOusss3TpUnP88cfbnpzly5c3WjYta9q0abYHyF+WLoMtXLiwwbI0uLpt27a5//fo0cPMmzfPXj4DkB4MggYQmVatWjX4v8bf5L+n/4sfqCiwOPjgg80111xTNNBpytZbb21fp59+uh2ns+eee5pJkyaZffbZp+j3taxu3bqZu+++e4XPNN4HQG0hAAKQWDvuuKP517/+ZTbYYAPTsmXlh6stt9zS/tWAa2ndurVZtmzZCsvSHW0dO3a0g5ub6in66aefTJs2bez/Nd5IvUWdO3euuHwAosclMACJNXjwYPPdd9/Zu7heeukleynqiSeesHeNFQYwPg2svvLKK+0gZg2EVoAyYMAA24ujy1WigEq3xuvWfd1RpgHQxx13nB2grTu/NAj6448/tgOdzzzzTPPZZ5/l5q8B0brD7J133jGPPfaYGT58uDnjjDNM8+YcToE0YY8FkFjrrbeeDWQU7OgOMY0X0m30uk29sYCjd+/eNujR3WWbbrqpOeKII8zKK69s797SmCMZOHCg2WyzzcxOO+1kAyMtQ+N6Jk+ebLp06WIOP/xws8UWW9hAR2OA8nuEevXqZbp27Wp69uxpjj76aHPIIYfYW+sBpEszjYSOuxAAkAZ6DpBuzX/ooYfiLgqAKtEDBAAAMocACAAAZA6XwAAAQObQAwQAADKHAAgAAGQOARAAAMgcAiAAAJA5BEAAACBzCIAAAEDmEAABAIDMIQACAACZQwAEAAAy5/8BnXsyXEUTOz0AAAAASUVORK5CYII="
     },
     "metadata": {},
     "output_type": "display_data",
     "jetTransient": {
      "display_id": null
     }
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total spikes: 904\n",
      "Average firing rate: 0.151\n",
      "Most active neuron: 20, fired 39 times\n"
     ]
    }
   ],
   "execution_count": 64
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 9. Summary\n",
    "\n",
    "In this tutorial, we learned:\n",
    "\n",
    "1. ✅ **BinaryArray Concept**: Core data structure for representing binary spike events\n",
    "2. ✅ **Creating BinaryArray**: Creating from lists, NumPy arrays, JAX arrays\n",
    "3. ✅ **Basic Operations**: Indexing, statistics, logical operations\n",
    "4. ✅ **Event-driven Computation**: Leveraging sparsity to optimize matrix multiplication\n",
    "5. ✅ **Performance Advantages**: BinaryArray's acceleration effect in sparse spike scenarios\n",
    "6. ✅ **Practical Applications**: Building simple spiking neural networks\n",
    "7. ✅ **Time Series Processing**: Processing multi-step time series spike data\n",
    "8. ✅ **Visualization**: Using braintools to draw spike raster plots\n",
    "\n",
    "## Next steps\n",
    "\n",
    "In the next tutorial, we will dive deeper into:\n",
    "- 📚 **Tutorial 2**: Sparse Data Structures - CSR, COO, CSC\n",
    "- Learn how to use different sparse matrix formats to represent neural network connections\n",
    "- Learn how to choose appropriate data structures for optimal performance\n",
    "\n",
    "## References\n",
    "\n",
    "- 💻 [GitHubRepository](https://github.com/chaobrain/brainevent)\n",
    "- 🌐 [Brain modeling ecosystem](https://brainx.chaobrain.com/)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.10.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
