From 47d5c0b19855d6c38af8516fce2f1414977d42eb Mon Sep 17 00:00:00 2001 From: Willian-Girao Date: Fri, 5 Apr 2024 18:47:02 +0200 Subject: [PATCH 01/15] comp graph from nir not working --- tests/test_residual/NIR_graph_nonseq.ipynb | 210 +++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 tests/test_residual/NIR_graph_nonseq.ipynb diff --git a/tests/test_residual/NIR_graph_nonseq.ipynb b/tests/test_residual/NIR_graph_nonseq.ipynb new file mode 100644 index 00000000..0335cd8c --- /dev/null +++ b/tests/test_residual/NIR_graph_nonseq.ipynb @@ -0,0 +1,210 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import torch\n", + "import torch.nn as nn\n", + "from sinabs import to_nir\n", + "import networkx as nx\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "class NonSeq(nn.Module):\n", + " def __init__(self, *args, **kwargs) -> None:\n", + " super().__init__(*args, **kwargs)\n", + "\n", + " self.fc1 = nn.Linear(100, 50)\n", + " self.fc2 = nn.Linear(50, 50)\n", + " self.fc3 = nn.Linear(50, 50)\n", + " self.fc4 = nn.Linear(50, 50)\n", + " self.fc5 = nn.Linear(50, 2)\n", + "\n", + " def forward(self, x):\n", + "\n", + " out1 = self.fc1(x)\n", + " out2 = self.fc2(out1)\n", + " out3 = self.fc3(out2)\n", + " out4 = self.fc4(out3 + out2)\n", + " out5 = self.fc5(out4)\n", + "\n", + " return out5\n", + "\n", + "ann = NonSeq()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "7\n" + ] + } + ], + "source": [ + "ann_graph = [\n", + " ('input', 'fc1'),\n", + " ('fc1', 'fc2'),\n", + " ('fc2', 'fc3'),\n", + " ('fc2', 'fc4'),\n", + " ('fc3', 'fc4'),\n", + " ('fc4', 'fc5'),\n", + " ('fc5', 'output'),\n", + "]\n", + "\n", + "print(len(ann_graph))" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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gbKKKEsnae/evbVixYpSn3344Yc1bdq0yioVXsrhcOjHH390PQu5ZcsWmUwm11if2NhY3XTTTR4x1gcAULlYtqoiggOsatng4qs9N9xwg1avXq2ioiJJksViUZ06dTRq1KjKKBFeqHisT2JiopYtW6a0tDTXWJ9HHnmkSoz1AQBUPAKlF7nuuutcYVKSatSooe3bt6tOnToGVoWq5OyxPomJidqwYYNsNptuuOEGjR49WrGxserYsSOPUAAASuBPBS9y3XXXSZICAwP12GOP6dVXX9W0adM0depUYwuDR7vYWJ9//etf6tu3L2N9AAAXRaD0Irfeeqv+3//7f7rvvvt0zTXXqFq1anr++efVs2dPderUyejy4EEOHTrk6sg+d6xPbGysunbtylgfAECZ0ZTjxWw2m7p3766UlBT9/PPPqlWrltElwSCXGusTGxura665hrE+AIArQqD0cocPH9aNN96omJgYzZs3j8DgQ4rH+iQmJiopKUknT54sMdanV69eCg0NNbpMAIAXIFD6gC+++EKDBw/W9OnTNWbMGKPLQQU5e6xPYmKitm7dKpPJpFtvvdU1XJyxPgCAikCg9BHx8fH6/PPP9dNPP6lFixZGl4NycuLECa1YsaLEWJ+aNWsqJiZGsbGxiomJYawPAKDCESh9xKlTp3TzzTcrNDRUGzdulL+/v9El4Qo4nU798ssvrmch169fL7vdrhtuuMH1LORtt93GWB8AQKUiUPqQH3/8Ubfddpsee+wxvfbaa0aXgzLKy8srMdbn8OHDrrE+xbeyIyMjjS4TAODDCJQ+5vXXX9eTTz6pFStWqGfPnkaXgws4dOiQ61nI5ORk11if4lXIbt26KTAw0OgyAQCQRKD0OQ6HQzExMdq1a5e2bdvGW3Q8RPFYn+IQuXv3blmtVnXp0sXVlc1YHwCApyJQ+qBjx46pdevWuu2227Ro0SJCikHON9anXr166tevn/r168dYHwBAlUGg9FHffPON4uLi9O677+qvf/2r0eX4BIfDoa1bt7oaahjrAwDwFgRKH/bII4/o448/1tatW3XDDTcYXY5XOnHihJKSkrR06dLzjvXp06cPjx0AAKo8AqUPy8/P16233ipJ+uGHH3h3czkoHutT3JFdPNanVatWrmchGesDAPA2BEoft3PnTrVt21b333+//vWvfxldTpV0vrE+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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "g_ideal = nx.DiGraph(ann_graph)\n", + "nx.draw(g_ideal, with_labels = True)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=================================\n", + "source_node: Tensor_0(1, 100) (inp)\n", + "destination_node: fc1 (inp)\n", + "\n", + "source_node: fc1 (out)\n", + "destination_node: Tensor_1(1, 50) (out)\n", + "\n", + "source_node: Tensor_1(1, 50) (inp)\n", + "destination_node: fc2 (inp)\n", + "\n", + "source_node: fc2 (out)\n", + "destination_node: Tensor_2(1, 50) (out)\n", + "\n", + "source_node: Tensor_2(1, 50) (inp)\n", + "destination_node: fc3 (inp)\n", + "\n", + "source_node: fc3 (out)\n", + "destination_node: Tensor_3(1, 50) (out)\n", + "\n", + "source_node: Tensor_4(1, 50) (inp)\n", + "destination_node: fc4 (inp)\n", + "\n", + "source_node: fc4 (out)\n", + "destination_node: Tensor_5(1, 50) (out)\n", + "\n", + "source_node: Tensor_5(1, 50) (inp)\n", + "destination_node: fc5 (inp)\n", + "\n", + "source_node: fc5 (out)\n", + "destination_node: Tensor_6(1, 2) (out)\n", + "\n", + "***************************\n", + "source_node: fc1 ()\n", + "destination_node: fc2 ()\n", + "\n", + "source_node: fc2 ()\n", + "destination_node: fc3 ()\n", + "\n", + "source_node: fc4 ()\n", + "destination_node: fc5 ()\n", + "\n", + "-------------extract_nir_graph_v2(extract_torch_graph()) (wrong)---------------\n", + "```mermaid\n", + "graph TD;\n", + "fc1 --> fc2;\n", + "fc2 --> fc3;\n", + "fc3;\n", + "fc4 --> fc5;\n", + "fc5;\n", + "\n", + "```\n", + " \n", + "--------------------------------------------------------\n", + "0: 1\n", + "1: 6 [('input', 'fc1'), ('fc1', 'fc2'), ('fc2', 'fc3'), ('fc3', 'output'), ('fc4', 'fc5'), ('fc5', 'output')]\n", + "2: 6\n" + ] + } + ], + "source": [ + "nir_graph = to_nir(ann, torch.randn(1, 100))" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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zSq/a7wF7me1KNBm1Rtd6+mpyBoFVCkA5A2zvAGgEBmGoX/+85+aOXOmvvjiC3Xt2kb9ktM1ck68Vu9LlJ/VcsXJOn/ub1o9TK92qcdtbgAegVneAFCIXnnlFY0aNUoff/yxHn/88Qv27T2eoikbErRizwklJKXr/A9ni35ftDzy5nD1ahKhGuElCrVuALgSAiUAFJJPPvlEAwYM0EsvvaRRo0Zd8bVpWU4dSEpTttOlAJtV1cJCeAIOAI9FoASAQjB79mw98sgjGjhwoN577z1ZLO5dNggAzESgBIACtnLlSrVr105dunTR1KlTZbUyHxKAbyFQAkAB2rJli1q2bKm77rpL8+fPV2BgoNklAYDbESgBoID88ssvatasmapWrarly5erRAkm0gDwTQRKACgAx44dU7NmzWSz2bRmzRqVK1fO7JIAoMAwZRAA3OzMmTNq3769MjMz9f333xMmAfg8AiUAuFFmZqYeeughHThwQN99952qVatmdkkAUOAIlADgJrm5uYqKitKGDRu0ZMkS1atXz+ySAKBQECgBwA0Mw9DAgQM1d+5czZkzR82bNze7JAAoNARKAHCDF198UZ988ok+//xzdezY0exyAKBQsbouAOTT+++/r1deeUVvvPGGYmJizC4HAAodywYBQD5Mnz5dPXv21JAhQ/TWW2/xSEUARRKBEgCu05IlS9ShQwf16NFDsbGxPFIRQJFFoASA67Bp0yZFRkaqZcuW+uqrr+Tv7292SQBgGgIlAFyjn3/+Wc2aNdPNN9+sJUuWKCQkxOySAMBUBEoAuAaHDx9W06ZNVbx4ca1evVqhoaFmlwQApmPADwDkUXJystq1ayfDMLRo0SLCJAD8gXUoASAP0tPT1bFjRx07dkxr1qxRlSpVzC4JADwGgRIAriInJ0fdu3fXli1btHz5ctWqVcvskgDAoxAoAeAKDMNQ//79tXDhQs2fP1+NGzc2uyQA8DgESgC4gueee04Oh0NTpkxRu3btzC4HADwSk3IA4DLGjRunt956S++884569uxpdjkA4LFYNggALiEuLk52u10jRozQq6++anY5AODRCJQA8DfffPONHnroIcXExGjixIk8nxsAroJACQDnWbt2rVq3bq127drpiy++kM3GUHMAuBoCJQD8YceOHWrRooXq1q2rRYsWqVixYmaXBABegUAJAJIS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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "g_nir = nx.DiGraph(nir_graph.edges)\n", + "nx.draw(g_nir, with_labels = True)\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "speck-rescnn", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 276eda3c75a75f3dcc190a2db11c6f18f67c4f50 Mon Sep 17 00:00:00 2001 From: Willian-Girao Date: Sat, 6 Apr 2024 18:45:57 +0200 Subject: [PATCH 02/15] using jit trace --- tests/test_residual/NIR_graph_nonseq.ipynb | 122 +++++------ tests/test_residual/test_jit_tracer_ann.ipynb | 204 ++++++++++++++++++ tests/test_residual/test_jit_tracer_cnn.ipynb | 149 +++++++++++++ 3 files changed, 411 insertions(+), 64 deletions(-) create mode 100644 tests/test_residual/test_jit_tracer_ann.ipynb create mode 100644 tests/test_residual/test_jit_tracer_cnn.ipynb diff --git a/tests/test_residual/NIR_graph_nonseq.ipynb b/tests/test_residual/NIR_graph_nonseq.ipynb index 0335cd8c..1cd8dfae 100644 --- a/tests/test_residual/NIR_graph_nonseq.ipynb +++ b/tests/test_residual/NIR_graph_nonseq.ipynb @@ -22,12 +22,12 @@ "class NonSeq(nn.Module):\n", " def __init__(self, *args, **kwargs) -> None:\n", " super().__init__(*args, **kwargs)\n", - "\n", - " self.fc1 = nn.Linear(100, 50)\n", - " self.fc2 = nn.Linear(50, 50)\n", - " self.fc3 = nn.Linear(50, 50)\n", - " self.fc4 = nn.Linear(50, 50)\n", - " self.fc5 = nn.Linear(50, 2)\n", + " # input tensor / output tensor\n", + " self.fc1 = nn.Linear(100, 50) # Size([1, 100]) / Size([1, 50])\n", + " self.fc2 = nn.Linear(50, 50) # Size([1, 50]) / Size([1, 50])\n", + " self.fc3 = nn.Linear(50, 50) # Size([1, 50]) / Size([1, 50])\n", + " self.fc4 = nn.Linear(50, 30) # Size([1, 50]) / Size([1, 30])\n", + " self.fc5 = nn.Linear(30, 2) # Size([1, 30]) / Size([1, 2])\n", "\n", " def forward(self, x):\n", "\n", @@ -73,22 +73,11 @@ "cell_type": "code", "execution_count": 4, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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gbKKKEsnae/evbVixYpSn3344Yc1bdq0yioVXsrhcOjHH390PQu5ZcsWmUwm11if2NhY3XTTTR4x1gcAULlYtqoiggOsatng4qs9N9xwg1avXq2ioiJJksViUZ06dTRq1KjKKBFeqHisT2JiopYtW6a0tDTXWJ9HHnmkSoz1AQBUPAKlF7nuuutcYVKSatSooe3bt6tOnToGVoWq5OyxPomJidqwYYNsNptuuOEGjR49WrGxserYsSOPUAAASuBPBS9y3XXXSZICAwP12GOP6dVXX9W0adM0depUYwuDR7vYWJ9//etf6tu3L2N9AAAXRaD0Irfeeqv+3//7f7rvvvt0zTXXqFq1anr++efVs2dPderUyejy4EEOHTrk6sg+d6xPbGysunbtylgfAECZ0ZTjxWw2m7p3766UlBT9/PPPqlWrltElwSCXGusTGxura665hrE+AIArQqD0cocPH9aNN96omJgYzZs3j8DgQ4rH+iQmJiopKUknT54sMdanV69eCg0NNbpMAIAXIFD6gC+++EKDBw/W9OnTNWbMGKPLQQU5e6xPYmKitm7dKpPJpFtvvdU1XJyxPgCAikCg9BHx8fH6/PPP9dNPP6lFixZGl4NycuLECa1YsaLEWJ+aNWsqJiZGsbGxiomJYawPAKDCESh9xKlTp3TzzTcrNDRUGzdulL+/v9El4Qo4nU798ssvrmch169fL7vdrhtuuMH1LORtt93GWB8AQKUiUPqQH3/8Ubfddpsee+wxvfbaa0aXgzLKy8srMdbn8OHDrrE+xbeyIyMjjS4TAODDCJQ+5vXXX9eTTz6pFStWqGfPnkaXgws4dOiQ61nI5ORk11if4lXIbt26KTAw0OgyAQCQRKD0OQ6HQzExMdq1a5e2bdvGW3Q8RPFYn+IQuXv3blmtVnXp0sXVlc1YHwCApyJQ+qBjx46pdevWuu2227Ro0SJCikHON9anXr166tevn/r168dYHwBAlUGg9FHffPON4uLi9O677+qvf/2r0eX4BIfDoa1bt7oaahjrAwDwFgRKH/bII4/o448/1tatW3XDDTcYXY5XOnHihJKSkrR06dLzjvXp06cPjx0AAKo8AqUPy8/P16233ipJ+uGHH3h3czkoHutT3JFdPNanVatWrmchGesDAPA2BEoft3PnTrVt21b333+//vWvfxldTpV0vrE+QUFBio6Odj0PyVgfAIA3I1BC06ZN0yOPPKLFixerf//+RpdTJfz222+uZyGLx/o0bdrU9SwkY30AAL6EQAk5nU7FxcVp06ZN2r59uxo0aGB0SR6nqKhI69evd4XIs8f6FM+GbNGiBR3zAACfRKCEJCk9PV033nijWrZsqW+//ZZOY0nHjx/XsmXLtHTp0lJjfWJjY9WzZ0/G+gAAIAIlzrJy5Ur16tVLr7/+uiZNmmR0OZWueKxP8bOQ5471iY2NVZs2bQjbAACcg0CJEp588km9/fbb2rRpk2655Rajy6lwxWN9EhMTtWzZMqWnp6tmzZrq06eP+vXrx1gfAADKgECJEgoLC3XbbbcpJydHP/30k6pXr250SeXq7LE+iYmJ2rBhg2usT3FDDWN9AAC4PARKlLJv3z7dfPPNGjx4sBISEowux20XG+sTGxurvn37MtYHAAA3EChxXjNmzNDYsWP1+eef69577y21P7fApkOZuSq0OeRvNSsqPFjBAZ6zqvfbb7+5AuS5Y31iY2PVtWtXxvoAAFBOCJQ4L6fTqSFDhujbb7/Vtm3b1KRJE+1PzdGczSlK3pumlKw8nf0LxyQpMixI3a+J0LD2kbq6bkil1ls81qc4RO7evVt+fn7q0qWLqyubsT4AAFQMAiUu6M8//1SbNm3U7Mb2ajjgca07kCGL2SS748K/ZIr3d25eWy8NaqXGYUEVVl/xWJ/ExEStWLFCJ0+eVP369V1vp2GsDwAAlYNAiYt67av1+mDrn5LZctEgeS6L2SSr2aTn41pqSLvyeT7x7LE+iYmJ+vHHH2UymdS+fXtXQw1jfQAAqHwESlzQu8n79UbSPrfPM6l3Cz3S/eor+uzFxvrExsYqJiaGsT4AABiMQInzmrclRZMX7Ci38716ZysNLsNKpdPp1K5du1yvOCwe69O6dWvXs5AdOnRgrA8AAB6EQIlSfs/KU/txLyhj3VzZThyX01ao+mPekX/dphf9XP6hbcrdlayCI3tkz0mXOSBY/vWuVo1OQxXaqIVWTuh63mcq8/Ly9N1337lCZEpKSomxPv369VPjxo0r6usCAAA3EShRyj1vL9f8x/urWtObFXrrIJksfvKLiJLZ7+JjdtIXvix7fo6Cr+0kv9qNZc/L1skfFqrw+AHVH/Kionv00Cfx7SUx1gcAAG9CoEQJ+1Nz1HnS+0r99EnVHvCUgq/rXObP2nNPyBJcs8Q2R2G+jnxwv/xrN1HdoX9X76LNWpf4pfbs2cNYHwAAvASBEiW0iR6obd8tKrEtoPENqjfsFRUc3avsDfNUcGSPHEWnZQ0JV7Xmtyqs5wMXPefxz6bIfipTDe9/T/Y9yYqpfVKxsbGKjo5mrA8AAF6AzgaU4N/uboVZGygr6T3V7DpSgZGtZQoIUv7BH5U2/0X5hTdSrehxsobWkS07Vfm//eei53OczlVh6q8KbNJaMpnVtFN/fTypeyV9GwAAUBkIlHA5VWBTuqmW/MLPNMBYazVQQMNrJUlH5r8oa2gd1R/1pkxWf9dnqrfuddFzZq14T86i06rRcbAkKSUzT7kFNo96TSMAAHAPE6DhcjgzV+d7/qEo64hsJ46p+o29SoTJSzmx9hPl7lqtWtHjFFCvuSTJKelQZm75FAwAADwCgRIuhTbHebfb87IlSZaQ2mU+14n1nyl74+eq2WWkQm/pX6brAACAqolACRd/6/l/OViCakiS7DkZZTrPifWfKXv9Z6rR6T7V6Hhvma8DAACqJv5kh0tUeLDON7THL6yhrDXr69T2FXLaii56jhMb5p4Jkx0Hq2an+0rtN/3fdQAAgPcgUMIlOMCqyPO8yUaSwno/KFt2uo7NflyndqzS6cPbdWrHKqUvft11zMnNC5S9bo4Cm96ias3aqeDInhL/k6TI8CAacgAA8DL8yY4Sul8Tof3bSq9TVmt6i+oNe0UnNsxV1soP5bQVyhpSW9WuvtV1TN6BHyRJpw/+qOMHfyx1jqZTEtW9RUTFFQ8AAAzBYHOUsD81R73eXlth5185oYuaR4RU2PkBAEDl45Y3Sri6bog6N68ti7l8X4FoMZvUuXltwiQAAF6IQIlSXhrUStZyDpRWs0kvDWpVrucEAACegUCJEvLy8pS8ZL7qpCSX63lfiGupxhdo+AEAAFUbTTlQWlqalixZooULFyopKUmFhYWSpJcX36f3Nx5x+/xP9L5Gg9tFun0eAADgmQiUPiw/P1+xsbFavXq1nE6nLBaL7Ha7JOn222/X5P5tFFUvTM8t3iWbwym7o+z9WxazSVazSS/EtSRMAgDg5bjl7cP8/Px09OhRFTf6F4dJSXrooYckSUPaRWrlhK7q2DRcki7ZrFO8v2PTcK2c0JUwCQCAD2BskI87duyY2rZtq6NHj7q2BQYGKiMjQ8HBJd9osz81R3M2pyh5X5pSMvN09i8ck84MLe/eIkLDO0TSzQ0AgA/hlrePO3r0qPLy8mS1Wl0rlXfddVepMCmdGSk0Na6lpqqlcgtsOpSZq0KbQ/5Ws6LCg3kDDgAAPopb3j5sw4YN6tGjh6655hp9//33CgsLk91u14gRIy752eAAq1o2qKGbImupZYMahEkAAHwYKcBHfffdd+rfv7/atm2rJUuWKCQkRGvWrNHcuXMVHR1tdHkAAKAK4RlKH5SYmKi77rpL3bp104IFCxQUxHxIAABw5bjl7WPmz5+vQYMGqW/fvlq0aBFhEgAAuI1A6UNmz56twYMH65577tEXX3yhgIAAo0sCAABegEDpI95//32NGjVKY8eO1ezZs+Xn52d0SQAAwEsQKH3Am2++qYceekjjx4/Xhx9+KIvFYnRJAADAixAovZjT6dQLL7ygxx9/XFOmTNFbb70lk+nib7oBAAC4XIwN8lJOp1OTJ0/Wa6+9pr///e+aMmWK0SUBAAAvRaD0Qg6HQ48++qimTZumt99+W+PHjze6JAAA4MUIlF7Gbrdr3LhxmjVrlj788EPdf//9RpcEAAC8HIHSixQVFWn48OH66quv9Mknn2jYsGFGlwQAAHwAgdJLnD59Wvfee6+WL1+uL7/8UoMGDTK6JAAA4CMIlF4gNzdXAwcO1Pr167V48WL16dPH6JIAAIAPIVBWcdnZ2YqNjdW2bdu0fPlyde3a1eiSAACAjyFQVmFZWVmKiYnRgQMHtGLFCnXo0MHokgAAgA8iUFZRqamp6tWrl44dO6bk5GS1adPG6JIAAICPIlBWQX/88Yeio6OVk5OjtWvX6rrrrjO6JAAA4MMIlFXMwYMHFR0dLafTqXXr1qlZs2ZGlwQAAHwc7/KuQvbs2aPOnTvLz89Pa9euJUwCAACPQKCsIrZt26YuXbooLCxMa9euVWRkpNElAQAASCJQVgmbN29Wt27dFBkZqdWrV6tevXpGlwQAAOBCoPRwa9euVc+ePXX99ddr1apVCg8PN7okAACAEgiUHuzbb79Vnz591L59eyUlJalGjRpGlwQAAFAKgdJDff3114qLi1OPHj20ZMkSBQcHG10SAADAeREoPdDcuXN19913a8CAAVqwYIECAwONLgkAAOCCCJQeJiEhQcOGDdPw4cP12Wefyd/f3+iSAAAALopA6UHeeecdjRs3Tg8++KCmT58uq5W58wAAwPMRKD3EK6+8ovHjx2vSpEmaNm2azGb+rwEAAFUDqcVgTqdTzzzzjJ5++mk999xzeu2112QymYwuCwAAoMy4p2ogp9OpiRMn6u2339Zrr72mJ554wuiSAAAALhuB0iB2u10PPfSQPvroI02bNk0PP/yw0SUBAABcEQKlAWw2m0aPHq25c+dq5syZGjVqlNElAQAAXDECZSUrKCjQ0KFD9c0332ju3Lm69957jS4JAADALQTKSpSfn68777xTycnJWrhwoe644w6jSwIAAHAbgbKS5OTkKC4uTj/88IOWLFminj17Gl0SAABAuSBQVoI///xT/fr10y+//KJvv/1WnTp1MrokAACAckOgrGDp6enq3bu3UlJStGrVKrVt29bokgAAAMoVgbICHT16VD179lRWVpZWr16tVq1aGV0SAABAuSNQVpDDhw8rOjpaBQUFWrt2rVq0aGF0SQAAABWCVy9WgP3796tz585yOp1at24dYRIAAHg1AmU527lzpzp37qzg4GCtXbtWUVFRRpcEAABQoQiU5ejHH39Ut27dVK9ePa1Zs0YNGzY0uiQAAIAKR6AsJxs2bFCPHj3UvHlzJScnKyIiwuiSAAAAKgWBshysWrVKvXv3Vps2bbRixQrVqlXL6JIAAAAqDYHSTYmJiYqNjVXnzp21bNkyhYSEGF0SAABApSJQuuHLL7/UwIED1bdvXy1atEhBQUFGlwQAAFDpCJRXaPbs2RoyZIjuvfdeffHFFwoICDC6JAAAAEP4/GDz3AKbDmXmqtDmkL/VrKjwYAUHXPyn5b333tPDDz+scePG6f3335fFYqmkagEAADyPTwbK/ak5mrM5Rcl705SSlSfnWftMkiLDgtT9mggNax+pq+uWfCbyH//4hyZNmqTx48frrbfekslkqtTaAQAAPI3J6XQ6L32Yd/g9K09TFu7QugMZsphNsjsu/NWL93duXlsvDWqlRrWq6cUXX9Rzzz2nKVOm6G9/+xthEgAAQD4UKOdtSdFzi3fJ5nBeNEiey2I2yWo2qVXhHn312kT9/e9/15QpUyqwUgAAgKrFJwLlu8n79UbSPjfO4JRkUsfgDH32zKjyKgsAAMAreH2X97wtKW6GSenMk5XSxtza+nxLivtFAQAAeBGvXqGcljBbEyc/q6ITx+W0Far+mHfkX7fpRT/jKMhT9sZ5Kkz9TYWpv8qRf1I1bh+qmp2HSZICrGatnNBVjcOYOQkAACB58Qplenq6Hv1LvKy16ini3udVb8QbsoY1uOTnHPk5yvn5WzntRQpq0aHUfpvDqSkLd1REyQAAAFWS144NWvX9z3LYbQq6vrsCI1uV+XOWGhFq/Ng8mUwm2fOydWpbUon9dodT6w5k6EBajppH8JpFAAAArwyUo0eP1qxZsyRJGYteVcaiVxXQ+AbVG/aKCo7uVfaGeSo4skeOotOyhoSrWvNbFdbzAUkq0yggi9mkT79P0dS4lhX6PQAAAKoCrwyUzz77rDZk19SBr/+pml1HKjCytUwBQco/+KPS5r8ov/BGqhU9TtbQOrJlpyr/t/9c1vntDqeS96VpqgiUAAAAXhko6zZqopxqdSVJ1loNFNDwWknSkfkvyhpaR/VHvSmT1d91fPXWvS77GimZecotsF3yNY0AAADeziubcg5n5pbaVpR1RLYTx1T9xl4lwuSVcko6dJ7rAAAA+BqvDJSFNkepbfa8bEmSJaR2hV4HAADA13hloPS3lv5alqAakiR7TkaFXgcAAMDXeGUiigoPLrXNL6yhrDXr69T2FXLaity+hukC1wEAAPA1XtlREhxgVd3QQKWesz2s94NKm/+ijs1+XKHtBpzp8j6ZrvzfflKduCdcx+X/ulWOotNyFuZLkooyf1funvWSpGrN2srsF6jI8CAFB1iVk5OjY8eO6dixYzp+/LgCAwM1YMCAyvqqAAAAhvPKQClJNzWuqe3nbKvW9BbVG/aKTmyYq6yVH8ppK5Q1pLaqXX1rieMyv/237CfTXP+et2e98v4vUDZ8MEGWmgHavWq+/KfEqKio5Gqnn5+f8vPzZbFYKuR7AQAAeBqvfZf3/tQc9Xp7bYWd/9jHD6swI6XENovFogEDBuirr76qsOsCAAB4Gq98hlKSrq4bos7Na8tivvSbby6HxWxS5+a1teSzj0utQtrtdtWqVUupqefebAcAAPBeXhsoJemlQa1kLedAaTWb9NKgVurVq5fmzJlT4lWNgYGB+uSTT9SoUSMNGjRI33zzjWw2W7leHwAAwNN4daBsHBak58v5fdsvxLVU47AgSdLgwYP1zjvvuPZNmjRJx44d01tvvaVDhw4pLi5OjRs31uTJk7Vv375yrQMAAMBTeO0zlGd7N3m/3khyP9A90fsa/bV781Lbn3vuOb322mvatWuXmjZt6tr+n//8RwkJCZozZ45OnDihTp06KT4+XnfffbeqV6/udj0AAACewCcCpSTN25Ki5xbvks3hlN1R9q9sMZtkNZv0QlxLDW4XecHjcnNzFRx8/rmUp0+f1sKFCzV9+nStXLlS1atX15AhQzR27Fh16NChxG1zAACAqsZnAqUk/Z6VpykLd2jdgQxZzKaLBsvi/Z2b19ZLg1q5bnO769ChQ5o5c6ZmzJihlJQUXXfddYqPj9eIESMUERFRLtcAAACoTD4VKIvtT83RnM0pSt6XppTMPJ39E2CSFBkepO4tIjS8Q6SaR4RUSA12u13fffedEhIStHDhQjkcDvXv319jx45Vnz59ZLV67YhQAADgZXwyUJ4tt8CmQ5m5KrQ55G81Kyo8WMEBlRvmsrKyNGfOHCUkJGjbtm2qX7++Ro0apbFjx+rqq6+u1FoAAAAul88HSk/z008/afr06a5Gns6dO7saeS70jCYAAICRCJQeKj8/X19//bUSEhK0atUqhYSEaMiQIYqPj9ett95KIw8AAPAYBMoq4LfffnM18vz++++6/vrrFR8fr+HDh9PIAwAADEegrELsdrtWrVqlhIQEff3113I4HIqLi9PYsWMVExNDIw8AADAEgbKKyszMdDXybN++XQ0aNHA18jRvXnr4OgAAQEUhUFZxTqdTP/30kxISEvTZZ58pOztbXbt21dixY3X33XcrKKh85mcCAABcCIHSi+Tn52vBggWaPn26vvvuO4WEhGjo0KGKj49Xu3btaOQBAAAVgkDppQ4ePOhq5Pnjjz/UsmVLVyNPnTp1jC4PAAB4EQKll7Pb7Vq5cqWrkUdSiUYei8VibIEAAKDKI1D6kIyMDFcjz44dO9SwYUONHj1aY8aMUbNmzYwuDwAAVFEESh/kdDr1448/uhp5Tp48qW7dumns2LG66667aOQBAACXhUDp4/Ly8lyNPMnJyQoNDXU18rRt25ZGHgAAcEkESrj8+uuvrkaeI0eOqFWrVho7dqyGDx+u2rVrG10eAADwUARKlGK325WUlKTp06dr0aJFkqQBAwYoPj5evXr1opEHAACUQKDERaWnp7saeXbu3KlGjRq5GnmaNm1qdHkAAMADEChRJk6nU1u3blVCQoLmzp2rkydPqnv37q5GnmrVqhldIgAAMAiBEpctLy9PX331laZPn67Vq1erRo0arkaeW265hUYeAAB8DIESbjlw4IBmzpypmTNn6siRI2rdurWrkSc8PNzo8gAAQCUgUKJc2O12ffvtt5o+fboWL14sk8nkauTp2bMnjTwAAHgxAiXKXXp6uj799FMlJCRo165daty4sauR56qrrjK6PAAAUM4IlKgwTqdTW7ZscTXy5OTkqEePHoqPj9egQYNo5AEAwEsQKFEpcnNz9dVXXykhIUFr165VzZo1dd9992ns2LG6+eabaeQBAKAKI1Ci0u3fv18zZszQzJkzdezYMd14440aO3ashg0bRiMPAABVEIEShrHZbCUaecxmswYOHKj4+HhFR0fTyAMAQBVBoIRHSEtLczXy/PLLL4qMjHQ18kRFRRldHgAAuAgCJTyK0+nU5s2bNX36dM2bN085OTmKjo52NfIEBgYaXSIAADgHgRIeKzc3V/Pnz1dCQoLWrVunmjVratiwYa5GHgAA4BkIlKgS9u3bpxkzZmjWrFk6duyY2rRp42rkCQsLM7o8AAB8GoESVYrNZtPy5cuVkJCgJUuWyGKxaNCgQRo7dqyio6NlNpuNLhEAAJ9DoESVlZqaqk8++UQJCQnas2ePIiMjNWbMGI0ZM0ZNmjQxujwAAHwGgRJVntPp1Pfff+9q5MnNzXU18gwcOJBGHgAAKhiBEl7l1KlTrkae9evXq1atWq5Gnptuusno8gAA8EoESnitvXv3uhp5jh8/rptuuknx8fG67777VKtWLaPLAwDAaxAo4fVsNpuWLVvmauSxWq268847NXbsWPXo0YNGHgAA3ESghE85fvy4q5Fn7969atKkiauRJzIy0ujyAACokgiU8ElOp1ObNm1yNfLk5eWpZ8+erkaegIAAo0sEAKDKIFDC5506dUpffPGFpk+frg0bNigsLEzDhg1TfHy8brzxxkqtJbfApkOZuSq0OeRvNSsqPFjBAdZKrQEAgMtFoATOsmfPHlcjT2pqqm6++WbFx8dr6NChFdbIsz81R3M2pyh5b5pSsvJ09m9Ik6TIsCB1vyZCw9pH6uq6IRVSAwAA7iBQAudRVFSkpUuXavr06UpMTJSfn5+rkad79+7l0sjze1aepizcoXUHMmQxm2R3XPi3YvH+zs1r66VBrdQ4LMjt6wMAUF4IlMAlHDt2zNXIs2/fPkVFRWnMmDEaPXr0FTfyzNuSoucW75LN4bxokDyXxWyS1WzS83EtNaQdTUQAAM9AoATKyOl0auPGjUpISNAXX3yhvLw89e7dW2PHjtWAAQPK3MjzbvJ+vZG0z+16JvVuoUe6X+32eQAAcBeBErgCOTk5+uKLL5SQkKBNmzYpLCxMw4cPV3x8vFq3bn3Bz83bkqLJC3aUWx2v3tlKg1mpBAAYjEAJuGn37t2aPn26Zs+erbS0NN1yyy2uRp6aNWu6jvs9K08931qjApuj3K4dYDVr5YSuPFMJADAUrwgB3HTdddepZcuWSktL0wcffKAGDRrof/7nf1S/fn0NHz5cycnJcjgcmrJwh2yX8bxkWdgcTk1ZWHLFMy8vT1OnTtXq1avL9VoAAFwIK5RAOUhPT9evv/6qm266SQEBATp69KirkWf//v26qk1HOfpMqbDrr5zQRc0jzowUysjIUJ06dfTcc89p6tSpFXZNAACKsUIJlIM6deqoQ4cOrsacBg0a6KmnntLevXu1bt06Ne4+VGZTxVzbYjbp0+9TKubkAACUASuUQDmYOXOmxowZo99++01RUVHq1q2bMjIyNGPGDE2cOFEbN2+ROaimqrfpo9AOd8lkOvN3udOHtyt17hSF3/G4ClMPKPeXNXIW5Mm/fguFRd8v/3rNXNc4PmeyJKnesFdKXDtjyVuyHdmp01nHdejQIV111VWl6hs1apRmzpxZcT8BAACfxgolUEGOHz+uYcOG6Z7BQ1XnrmcV2KytTqyZpdydyaWOPbF2tmwnUhXe91GF9f0f2U9l6vjcp1V04niZrmWzO5VbYFP9+vW1fPlySVJ8fLw2bdqkTZs26dlnny3X7wYAwNl4STBQQTIzM7V06VIFN7pGbx5Zr8CoNipI2aHcX9aoeqvoEsdaqoWqzp3/K5PpzH3xwEYtdeSDB3Ry0xcK7/toma53KDNXLRvU0C233CJJatSokTp06FC+XwoAgPNghRKoIPXq1dOtt96qwrPGBPnViZLtZFqpY4Ov7+oKk5JkrRGhgIbX6vThss+sLCzHcUQAAFwOAiVQQcLDwyVJ/tb//jYzWfzkLCosday5eq1S2yzVa8mRf7LM1zv7OgAAVCb+BAIqWFR4sC7V4O049WepbfZTf8pcLdT17yarv5z2otKf/b/QGRUe7FadAABcKQIlUMGCA6yKvMSbbHJ3r9XZAxds2WkqOLJHgZGtXNusNSJkyzoqp+2/odKef1IFR3bLajEpOODMI9HFo4vy8/PL82sAAHBBNOUAlaD7NRH6ZPPhC+6352UrfcHfVf3GGDkLcnVi/RyZrH4Kve0e1zHBN/TQqZ+XK+ObN1S9TYwc+TnK/v4rmQOCFOhncR0XEhKiJk2aaNGiRYqOjlZYWJhq166tqKioivyKAAAfxgolUAmGtY+U/SKvXazZZaSsoXWUufRtZSz9pyzBYap738vyq1XfdUxgo+sVHjtBRRkpSv/qb8re+Llq3HaPAhq3UvWAkn83TEhIUFBQkOLi4tSuXTvemAMAqFAMNgcqyYiEzdp4MLNEsCwebF574GQFX9vpss9pMZvUsWm4PolvX56lAgBwWVihBCrJS4NayVrO71+0mk16aVCrSx8IAEAFIlAClaRxWJCej2tZrud8Ia6lGl+i4QcAgIrGLW+gkr2bvF9vJO1z+zxP9L5Gf+3evBwqAgDAPQRKwADztqToucW7ZHM4L9qscy6L2SSr2aQX4lpqcLvICqwQAICyI1ACBvk9K09TFu7QugMZsphNFw2Wxfs7N6+tlwa14jY3AMCjECgBg+1PzdGczSlK3pemlMw8nf0b0iQpMjxI3VtEaHiHSDWPCDGqTAAALohACXiQ3AKbDmXmqtDmkL/VrKjwYNcbcAAA8FQESgAAALiFsUEAAABwC4ESAAAAbiFQAgAAwC0ESgAAALiFQAkAAAC3ECgBAADgFgIlAAAA3EKgBAAAgFsIlAAAAHALgRIAAABuIVACAADALQRKAAAAuIVACQAAALcQKAEAAOAWAiUAAADcQqAEAACAWwiUAAAAcAuBEgAAAG4hUAIAAMAtBEoAAAC4hUAJAAAAtxAoAQAA4BYCJQAAANxCoAQAAIBbCJQAAABwC4ESAAAAbiFQAgAAwC0ESgAAALiFQAkAAAC3ECgBAADgFgIlAAAA3EKgBAAAgFsIlAAAAHALgRIAAABuIVACAADALQRKAAAAuIVACQAAALf8f7YZh3DUfLZdAAAAAElFTkSuQmCC", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ - "g_ideal = nx.DiGraph(ann_graph)\n", - "nx.draw(g_ideal, with_labels = True)\n", - "plt.show()" + "# g_ideal = nx.DiGraph(ann_graph)\n", + "# nx.draw(g_ideal, with_labels = True)\n", + "# plt.show()" ] }, { @@ -101,47 +90,63 @@ "output_type": "stream", "text": [ "=================================\n", - "source_node: Tensor_0(1, 100) (inp)\n", - "destination_node: fc1 (inp)\n", + "> input_data: torch.Size([1, 100])\n", + " creating Node: Tensor_0(1, 100) \n", + " returning Node: fc1 \n", + "> output_data: torch.Size([1, 50])\n", + " returning Node: fc1 \n", + " creating Node: Tensor_1(1, 50) \n", "\n", - "source_node: fc1 (out)\n", - "destination_node: Tensor_1(1, 50) (out)\n", "\n", - "source_node: Tensor_1(1, 50) (inp)\n", - "destination_node: fc2 (inp)\n", + "> input_data: torch.Size([1, 50])\n", + " returning Node: Tensor_1(1, 50) \n", + " returning Node: fc2 \n", + "> output_data: torch.Size([1, 50])\n", + " returning Node: fc2 \n", + " creating Node: Tensor_2(1, 50) \n", "\n", - "source_node: fc2 (out)\n", - "destination_node: Tensor_2(1, 50) (out)\n", "\n", - "source_node: Tensor_2(1, 50) (inp)\n", - "destination_node: fc3 (inp)\n", + "> input_data: torch.Size([1, 50])\n", + " returning Node: Tensor_2(1, 50) \n", + " returning Node: fc3 \n", + "> output_data: torch.Size([1, 50])\n", + " returning Node: fc3 \n", + " creating Node: Tensor_3(1, 50) \n", "\n", - "source_node: fc3 (out)\n", - "destination_node: Tensor_3(1, 50) (out)\n", "\n", - "source_node: Tensor_4(1, 50) (inp)\n", - "destination_node: fc4 (inp)\n", + "> input_data: torch.Size([1, 50])\n", + " creating Node: Tensor_4(1, 50) \n", + " returning Node: fc4 \n", + "> output_data: torch.Size([1, 30])\n", + " returning Node: fc4 \n", + " creating Node: Tensor_5(1, 30) \n", "\n", - "source_node: fc4 (out)\n", - "destination_node: Tensor_5(1, 50) (out)\n", "\n", - "source_node: Tensor_5(1, 50) (inp)\n", - "destination_node: fc5 (inp)\n", + "> input_data: torch.Size([1, 30])\n", + " returning Node: Tensor_5(1, 30) \n", + " returning Node: fc5 \n", + "> output_data: torch.Size([1, 2])\n", + " returning Node: fc5 \n", + " creating Node: Tensor_6(1, 2) \n", "\n", - "source_node: fc5 (out)\n", - "destination_node: Tensor_6(1, 2) (out)\n", "\n", - "***************************\n", - "source_node: fc1 ()\n", - "destination_node: fc2 ()\n", + "> input_data: torch.Size([1, 100])\n", + "> output_data: torch.Size([1, 2])\n", "\n", - "source_node: fc2 ()\n", - "destination_node: fc3 ()\n", - "\n", - "source_node: fc4 ()\n", - "destination_node: fc5 ()\n", "\n", + "++++++++++++++++++++++++++++++++\n", + " returning Node: fc1 \n", + " returning Node: fc2 \n", + " returning Node: fc2 \n", + " returning Node: fc3 \n", + " returning Node: fc4 \n", + " returning Node: fc5 \n", "-------------extract_nir_graph_v2(extract_torch_graph()) (wrong)---------------\n", + ">>>> fc1\n", + ">>>> fc2\n", + ">>>> fc3\n", + ">>>> fc4\n", + ">>>> fc5\n", "```mermaid\n", "graph TD;\n", "fc1 --> fc2;\n", @@ -167,22 +172,11 @@ "cell_type": "code", "execution_count": 6, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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zSq/a7wF7me1KNBm1Rtd6+mpyBoFVCkA5A2zvAGgEBmGoX/+85+aOXOmvvjiC3Xt2kb9ktM1ck68Vu9LlJ/VcsXJOn/ub1o9TK92qcdtbgAegVneAFCIXnnlFY0aNUoff/yxHn/88Qv27T2eoikbErRizwklJKXr/A9ni35ftDzy5nD1ahKhGuElCrVuALgSAiUAFJJPPvlEAwYM0EsvvaRRo0Zd8bVpWU4dSEpTttOlAJtV1cJCeAIOAI9FoASAQjB79mw98sgjGjhwoN577z1ZLO5dNggAzESgBIACtnLlSrVr105dunTR1KlTZbUyHxKAbyFQAkAB2rJli1q2bKm77rpL8+fPV2BgoNklAYDbESgBoID88ssvatasmapWrarly5erRAkm0gDwTQRKACgAx44dU7NmzWSz2bRmzRqVK1fO7JIAoMAwZRAA3OzMmTNq3769MjMz9f333xMmAfg8AiUAuFFmZqYeeughHThwQN99952qVatmdkkAUOAIlADgJrm5uYqKitKGDRu0ZMkS1atXz+ySAKBQECgBwA0Mw9DAgQM1d+5czZkzR82bNze7JAAoNARKAHCDF198UZ988ok+//xzdezY0exyAKBQsbouAOTT+++/r1deeUVvvPGGYmJizC4HAAodywYBQD5Mnz5dPXv21JAhQ/TWW2/xSEUARRKBEgCu05IlS9ShQwf16NFDsbGxPFIRQJFFoASA67Bp0yZFRkaqZcuW+uqrr+Tv7292SQBgGgIlAFyjn3/+Wc2aNdPNN9+sJUuWKCQkxOySAMBUBEoAuAaHDx9W06ZNVbx4ca1evVqhoaFmlwQApmPADwDkUXJystq1ayfDMLRo0SLCJAD8gXUoASAP0tPT1bFjRx07dkxr1qxRlSpVzC4JADwGgRIAriInJ0fdu3fXli1btHz5ctWqVcvskgDAoxAoAeAKDMNQ//79tXDhQs2fP1+NGzc2uyQA8DgESgC4gueee04Oh0NTpkxRu3btzC4HADwSk3IA4DLGjRunt956S++884569uxpdjkA4LFYNggALiEuLk52u10jRozQq6++anY5AODRCJQA8DfffPONHnroIcXExGjixIk8nxsAroJACQDnWbt2rVq3bq127drpiy++kM3GUHMAuBoCJQD8YceOHWrRooXq1q2rRYsWqVixYmaXBABegUAJAJIS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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ - "g_nir = nx.DiGraph(nir_graph.edges)\n", - "nx.draw(g_nir, with_labels = True)\n", - "plt.show()" + "# g_nir = nx.DiGraph(nir_graph.edges)\n", + "# nx.draw(g_nir, with_labels = True)\n", + "# plt.show()" ] } ], diff --git a/tests/test_residual/test_jit_tracer_ann.ipynb b/tests/test_residual/test_jit_tracer_ann.ipynb new file mode 100644 index 00000000..629ccec1 --- /dev/null +++ b/tests/test_residual/test_jit_tracer_ann.ipynb @@ -0,0 +1,204 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "import torch\n", + "import torch.nn as nn" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "class NonSeq(nn.Module):\n", + " def __init__(self, *args, **kwargs) -> None:\n", + " super().__init__(*args, **kwargs)\n", + " self.fc1 = nn.Linear(100, 50)\n", + " self.fc2 = nn.Linear(50, 50)\n", + " self.fc3 = nn.Linear(50, 50)\n", + " self.fc4 = nn.Linear(50, 30)\n", + " self.fc5 = nn.Linear(30, 2)\n", + "\n", + " def forward(self, x):\n", + " out1 = self.fc1(x)\n", + " out2 = self.fc2(out1)\n", + " out3 = self.fc3(out2)\n", + " out4 = self.fc4(out3 + out2)\n", + " out5 = self.fc5(out4)\n", + " return out5\n", + "\n", + "model = NonSeq()" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "dummy_input = torch.randn(1, 100)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "trace = torch.jit.trace(model, dummy_input)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "trace_output = trace(dummy_input)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "graph(%self.1 : __torch__.___torch_mangle_15.NonSeq,\n", + " %x : Float(1, 100, strides=[100, 1], requires_grad=0, device=cpu)):\n", + " %fc5 : __torch__.torch.nn.modules.linear.___torch_mangle_14.Linear = prim::GetAttr[name=\"fc5\"](%self.1)\n", + " %fc4 : __torch__.torch.nn.modules.linear.___torch_mangle_13.Linear = prim::GetAttr[name=\"fc4\"](%self.1)\n", + " %fc3 : __torch__.torch.nn.modules.linear.___torch_mangle_12.Linear = prim::GetAttr[name=\"fc3\"](%self.1)\n", + " %fc2 : __torch__.torch.nn.modules.linear.___torch_mangle_11.Linear = prim::GetAttr[name=\"fc2\"](%self.1)\n", + " %fc1 : __torch__.torch.nn.modules.linear.___torch_mangle_10.Linear = prim::GetAttr[name=\"fc1\"](%self.1)\n", + " %61 : Tensor = prim::CallMethod[name=\"forward\"](%fc1, %x)\n", + " %62 : Tensor = prim::CallMethod[name=\"forward\"](%fc2, %61)\n", + " %63 : Tensor = prim::CallMethod[name=\"forward\"](%fc3, %62)\n", + " %32 : int = prim::Constant[value=1]() # /tmp/ipykernel_16069/242438593.py:14:0\n", + " %input.5 : Float(1, 50, strides=[50, 1], requires_grad=1, device=cpu) = aten::add(%63, %62, %32) # /tmp/ipykernel_16069/242438593.py:14:0\n", + " %64 : Tensor = prim::CallMethod[name=\"forward\"](%fc4, %input.5)\n", + " %65 : Tensor = prim::CallMethod[name=\"forward\"](%fc5, %64)\n", + " return (%65)\n", + "\n" + ] + } + ], + "source": [ + "print(trace.graph)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "def forward(self,\n", + " x: Tensor) -> Tensor:\n", + " fc5 = self.fc5\n", + " fc4 = self.fc4\n", + " fc3 = self.fc3\n", + " fc2 = self.fc2\n", + " fc1 = self.fc1\n", + " _0 = (fc2).forward((fc1).forward(x, ), )\n", + " input = torch.add((fc3).forward(_0, ), _0)\n", + " _1 = (fc5).forward((fc4).forward(input, ), )\n", + " return _1\n", + " \n" + ] + } + ], + "source": [ + "print(trace.code, type(trace.code))" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(%fc5 : __torch__.torch.nn.modules.linear.___torch_mangle_14.Linear = prim::GetAttr[name=\"fc5\"](%self.1)\n", + ", fc5 defined in (%fc5 : __torch__.torch.nn.modules.linear.___torch_mangle_14.Linear = prim::GetAttr[name=\"fc5\"](%self.1)\n", + "))\n", + "(%fc4 : __torch__.torch.nn.modules.linear.___torch_mangle_13.Linear = prim::GetAttr[name=\"fc4\"](%self.1)\n", + ", fc4 defined in (%fc4 : __torch__.torch.nn.modules.linear.___torch_mangle_13.Linear = prim::GetAttr[name=\"fc4\"](%self.1)\n", + "))\n", + "(%fc3 : __torch__.torch.nn.modules.linear.___torch_mangle_12.Linear = prim::GetAttr[name=\"fc3\"](%self.1)\n", + ", fc3 defined in (%fc3 : __torch__.torch.nn.modules.linear.___torch_mangle_12.Linear = prim::GetAttr[name=\"fc3\"](%self.1)\n", + "))\n", + "(%fc2 : __torch__.torch.nn.modules.linear.___torch_mangle_11.Linear = prim::GetAttr[name=\"fc2\"](%self.1)\n", + ", fc2 defined in (%fc2 : __torch__.torch.nn.modules.linear.___torch_mangle_11.Linear = prim::GetAttr[name=\"fc2\"](%self.1)\n", + "))\n", + "(%fc1 : __torch__.torch.nn.modules.linear.___torch_mangle_10.Linear = prim::GetAttr[name=\"fc1\"](%self.1)\n", + ", fc1 defined in (%fc1 : __torch__.torch.nn.modules.linear.___torch_mangle_10.Linear = prim::GetAttr[name=\"fc1\"](%self.1)\n", + "))\n", + "(%61 : Tensor = prim::CallMethod[name=\"forward\"](%fc1, %x)\n", + ", 61 defined in (%61 : Tensor = prim::CallMethod[name=\"forward\"](%fc1, %x)\n", + "))\n", + "(%62 : Tensor = prim::CallMethod[name=\"forward\"](%fc2, %61)\n", + ", 62 defined in (%62 : Tensor = prim::CallMethod[name=\"forward\"](%fc2, %61)\n", + "))\n", + "(%63 : Tensor = prim::CallMethod[name=\"forward\"](%fc3, %62)\n", + ", 63 defined in (%63 : Tensor = prim::CallMethod[name=\"forward\"](%fc3, %62)\n", + "))\n", + "(%32 : int = prim::Constant[value=1]() # /tmp/ipykernel_16069/242438593.py:14:0\n", + ", 32 defined in (%32 : int = prim::Constant[value=1]() # /tmp/ipykernel_16069/242438593.py:14:0\n", + "))\n", + "(%input.5 : Float(1, 50, strides=[50, 1], requires_grad=1, device=cpu) = aten::add(%63, %62, %32) # /tmp/ipykernel_16069/242438593.py:14:0\n", + ", input.5 defined in (%input.5 : Float(1, 50, strides=[50, 1], requires_grad=1, device=cpu) = aten::add(%63, %62, %32) # /tmp/ipykernel_16069/242438593.py:14:0\n", + "))\n", + "(%64 : Tensor = prim::CallMethod[name=\"forward\"](%fc4, %input.5)\n", + ", 64 defined in (%64 : Tensor = prim::CallMethod[name=\"forward\"](%fc4, %input.5)\n", + "))\n", + "(%65 : Tensor = prim::CallMethod[name=\"forward\"](%fc5, %64)\n", + ", 65 defined in (%65 : Tensor = prim::CallMethod[name=\"forward\"](%fc5, %64)\n", + "))\n" + ] + } + ], + "source": [ + "edges = []\n", + "for node in trace.graph.nodes():\n", + " for next_node in node.outputs():\n", + " edges.append((node, next_node))\n", + " print(edges[-1])" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "speck-rescnn", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tests/test_residual/test_jit_tracer_cnn.ipynb b/tests/test_residual/test_jit_tracer_cnn.ipynb new file mode 100644 index 00000000..62b05573 --- /dev/null +++ b/tests/test_residual/test_jit_tracer_cnn.ipynb @@ -0,0 +1,149 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "import torch\n", + "import torch.nn as nn" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "class NonSeqCNN(nn.Module):\n", + " def __init__(self) -> None:\n", + " super().__init__()\n", + " self.conv1 = nn.Conv2d(3, 32, kernel_size=3, padding=1)\n", + " self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)\n", + " self.conv3 = nn.Conv2d(64, 64, kernel_size=3, padding=1)\n", + " self.conv4 = nn.Conv2d(64, 128, kernel_size=3, padding=1)\n", + " self.conv5 = nn.Conv2d(128, 128, kernel_size=3, padding=1)\n", + "\n", + " def forward(self, x):\n", + " out1 = torch.relu(self.conv1(x))\n", + " out2 = torch.relu(self.conv2(out1))\n", + " out3 = torch.relu(self.conv3(out2))\n", + " out4 = torch.relu(self.conv4(out3))\n", + " # Apply a 1x1 convolution to match dimensions for the skip connection\n", + " out2_skip = torch.relu(nn.Conv2d(64, 128, kernel_size=1)(out2))\n", + " # Introduce skip connection by adding outputs of conv2 and conv3\n", + " out4_skip = out4 + out2_skip\n", + " out5 = torch.relu(self.conv5(out4_skip))\n", + " return out5\n", + "\n", + "model = NonSeqCNN()" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "dummy_input = torch.randn(1, 3, 32, 32)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/willian/.local/lib/python3.11/site-packages/torch/jit/_trace.py:1102: TracerWarning: Trace had nondeterministic nodes. Did you forget call .eval() on your model? Nodes:\n", + "\t%tensor.3 : Float(128, 64, 1, 1, strides=[64, 1, 1, 1], requires_grad=1, device=cpu) = aten::uniform_(%tensor.1, %173, %174, %175) # /home/willian/.local/lib/python3.11/site-packages/torch/nn/init.py:459:0\n", + "\t%bias.9 : Float(128, strides=[1], requires_grad=1, device=cpu) = aten::uniform_(%tensor, %202, %203, %204) # /home/willian/.local/lib/python3.11/site-packages/torch/nn/init.py:15:0\n", + "This may cause errors in trace checking. To disable trace checking, pass check_trace=False to torch.jit.trace()\n", + " _check_trace(\n", + "/home/willian/.local/lib/python3.11/site-packages/torch/jit/_trace.py:1102: TracerWarning: Output nr 1. of the traced function does not match the corresponding output of the Python function. Detailed error:\n", + "Tensor-likes are not close!\n", + "\n", + "Mismatched elements: 87964 / 131072 (67.1%)\n", + "Greatest absolute difference: 0.22646202147006989 at index (0, 69, 20, 16) (up to 1e-05 allowed)\n", + "Greatest relative difference: inf at index (0, 1, 0, 1) (up to 1e-05 allowed)\n", + " _check_trace(\n" + ] + } + ], + "source": [ + "trace = torch.jit.trace(model, dummy_input)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "trace_output = trace(dummy_input)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "def forward(self,\n", + " x: Tensor) -> Tensor:\n", + " conv5 = self.conv5\n", + " conv4 = self.conv4\n", + " conv3 = self.conv3\n", + " conv2 = self.conv2\n", + " conv1 = self.conv1\n", + " input = torch.relu((conv1).forward(x, ))\n", + " input0 = torch.relu((conv2).forward(input, ))\n", + " input1 = torch.relu((conv3).forward(input0, ))\n", + " out4 = torch.relu((conv4).forward(input1, ))\n", + " data = torch.empty([128, 64, 1, 1], dtype=None, layout=None, device=torch.device(\"cpu\"), pin_memory=False)\n", + " tensor = torch.detach(data)\n", + " data0 = torch.empty([128], dtype=None, layout=None, device=torch.device(\"cpu\"), pin_memory=False)\n", + " tensor0 = torch.detach(data0)\n", + " tensor1 = torch.uniform_(tensor, -0.12499999999999999, 0.12499999999999999)\n", + " bias = torch.uniform_(tensor0, -0.125, 0.125)\n", + " _0 = torch._convolution(input0, tensor1, bias, [1, 1], [0, 0], [1, 1], False, [0, 0], 1, False, False, True, True)\n", + " out2_skip = torch.relu(_0)\n", + " input2 = torch.add(out4, out2_skip)\n", + " return torch.relu((conv5).forward(input2, ))\n", + " \n" + ] + } + ], + "source": [ + "print(trace.code, type(trace.code))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "speck-rescnn", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 1eda33c87be39cdeafc2ec22d5ea6fe47792eece Mon Sep 17 00:00:00 2001 From: Willian-Girao Date: Tue, 9 Apr 2024 16:17:58 +0200 Subject: [PATCH 03/15] Revert "using jit trace" This reverts commit 276eda3c75a75f3dcc190a2db11c6f18f67c4f50. --- tests/test_residual/NIR_graph_nonseq.ipynb | 122 ++++++----- tests/test_residual/test_jit_tracer_ann.ipynb | 204 ------------------ tests/test_residual/test_jit_tracer_cnn.ipynb | 149 ------------- 3 files changed, 64 insertions(+), 411 deletions(-) delete mode 100644 tests/test_residual/test_jit_tracer_ann.ipynb delete mode 100644 tests/test_residual/test_jit_tracer_cnn.ipynb diff --git a/tests/test_residual/NIR_graph_nonseq.ipynb b/tests/test_residual/NIR_graph_nonseq.ipynb index 1cd8dfae..0335cd8c 100644 --- a/tests/test_residual/NIR_graph_nonseq.ipynb +++ b/tests/test_residual/NIR_graph_nonseq.ipynb @@ -22,12 +22,12 @@ "class NonSeq(nn.Module):\n", " def __init__(self, *args, **kwargs) -> None:\n", " super().__init__(*args, **kwargs)\n", - " # input tensor / output tensor\n", - " self.fc1 = nn.Linear(100, 50) # Size([1, 100]) / Size([1, 50])\n", - " self.fc2 = nn.Linear(50, 50) # Size([1, 50]) / Size([1, 50])\n", - " self.fc3 = nn.Linear(50, 50) # Size([1, 50]) / Size([1, 50])\n", - " self.fc4 = nn.Linear(50, 30) # Size([1, 50]) / Size([1, 30])\n", - " self.fc5 = nn.Linear(30, 2) # Size([1, 30]) / Size([1, 2])\n", + "\n", + " self.fc1 = nn.Linear(100, 50)\n", + " self.fc2 = nn.Linear(50, 50)\n", + " self.fc3 = nn.Linear(50, 50)\n", + " self.fc4 = nn.Linear(50, 50)\n", + " self.fc5 = nn.Linear(50, 2)\n", "\n", " def forward(self, x):\n", "\n", @@ -73,11 +73,22 @@ "cell_type": "code", "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAApQAAAHzCAYAAACe1o1DAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAABMa0lEQVR4nO3deVxWdfr/8fe9sAiCCoo7kpotpllpmrmjopKobWruYk01fTPNyvzWL6uZ9qmmyWnFrUwr0zRRQw13M63JLddMKRfWRAQE7uX3h1/uEXFBb+Dc3Pfr+XjMIzvn3Odct6P59nPOdR2T0+l0CgAAALhCZqMLAAAAQNVGoAQAAIBbCJQAAABwC4ESAAAAbiFQAgAAwC0ESgAAALiFQAkAAAC3ECgBAADgFgIlAAAA3EKgBAAAgFsIlAAAAHALgRIAAABuIVACAADALQRKAAAAuIVACQAAALcQKAEAAOAWAiUAAADcQqAEAACAWwiUAAAAcAuBEgAAAG4hUAIAAMAtBEoAAAC4hUAJAAAAtxAoAQAA4BYCJQAAANxCoAQAAIBbCJQAAABwC4ESAAAAbiFQAgAAwC0ESgAAALiFQAkAAAC3ECgBAADgFgIlAAAA3EKgBAAAgFsIlAAAAHALgRIAAABuIVACAADALVajCwAAAKhqcgtsOpSZq0KbQ/5Ws6LCgxUc4Luxyne/OQAAwGXYn5qjOZtTlLw3TSlZeXKetc8kKTIsSN2vidCw9pG6um6IUWUawuR0Op2XPgwAAMA3/Z6VpykLd2jdgQxZzCbZHReOTsX7OzevrZcGtVLjsKBKrNQ4BEoAAIALmLclRc8t3iWbw3nRIHkui9kkq9mk5+Naaki7yAqs0DMQKAEAAM7j3eT9eiNpn9vnmdS7hR7pfnU5VOS56PIGAAA4x7wtKeUSJiXpjaR9+nxLSrmcy1MRKAEAAM7ye1aenlu8q9T2nJ8SdWr7yis65/9bvEu/Z+WV+fi8vDxNnTpVq1evvqLrVTYCJQAAwFmmLNwh23mel8z5KVGndlxZoLQ5nJqycEeZj8/Ly9Pzzz9PoAQAAKhq9qfmaN2BjMtqwCkLu8OpdQcydCAtp1zP6ykIlAAAwGutX79e0dHRCgkJUVBQkDp27KjExETX/qlTp8pkMrn+fc7mFFnMJp3avlKHX7lDthOpkqQ//j1WRRkpKvh9pw6/cocOv3KH/vj3WEnS6cPbdfiVO3RqZ7KyVn2k3/81XClv3Knjcyar8PivrnNbzCb1jO6hbt26lapz9OjRioqKkiQdOnRIderUkSQ9//zzMplMMplMGj16dDn/7JQfAiUAAPBKa9asUY8ePZSdna2EhATNnTtXISEh6t+/vz7//PPzfiZ5b9p5Vycj7vxfWWvWk3/dZqo34g3VG/GGIu783xLHnFg7W7YTqQrv+6jC+v6P7KcydXzu0yo6cVzSmVXKE3lFl6y7fv36Wr58uSQpPj5emzZt0qZNm/Tss89e7k9BpeFNOQAAwCtNnjxZtWrV0urVq1W9enVJ0h133KE2bdpo0qRJuvfee0scf6rAppQLNM7412smk9VfJv9qCmh47XmPsVQLVZ07/9e14hnYqKWOfPCATm76QuF9H5UknS6yX/J2ekBAgG655RZJUqNGjdShQ4eyf2mDsEIJAAC8Tm5urjZv3qy7777bFSYlyWKxaMSIEfrjjz+0d+/eEp85nJkrd56cDL6+a4nb59YaEQpoeK1OHy7ZjHPaZnfjKp6JQAkAALzOn3/+KafTqfr165fa16BBA0lSZmZmie2FNodb1zRXr1Vqm6V6LTnyT5bY5o2vlCFQAgAAr1OrVi2ZzWYdO3as1L6jR49KkmrXrq3AwEBJUkFBgfyt/41F9nNCYFk4Tv1Zapv91J8yVwt1/bvJ6i9bYWGp4zIyMi77ep6EQAkAALxOcHCw2rdvrwULFig/P9+13eFw6NNPP1WjRo3UokULV2f19u3bFRUerOIb1vkHfih1TpPFT05b6TBYLHf3Wp39RmtbdpoKjuxRYGQr1zZrjQj9fuhXFRQUuLZlZmZq48aNJc4VEBBwpo6zavdkBEoAAOCVXn75ZWVmZqp79+6aP3++Fi9erH79+mnnzp164403ZDKZ1K9fP4WFhSk+Pl4rli1R8PFtSl/4kuwn00udz69OlArTflPu7rUqOLZPhWmHSuy352UrfcHflXdgi3J3rVbqvP+Vyeqn0NvucR3TrGM/ZWVlafjw4UpKStLcuXPVs2dPhYaGljhXSEiImjRpokWLFikpKUlbt27VoUMlr+dJCJQAAMArde3aVd99952Cg4M1evRoDRkyRNnZ2Vq8eLEGDx4sSQoNDdXy5csVEhKi4cOHK+Wbd+RfJ0qhHQeXOl/NzsMUGHmDMpf9S8dnTVTa/BdK7u8yUtbQOspc+rYylv5TluAw1b3vZfnVOvMcp8Vs0sCYHpo1a5Z27dqlAQMG6G9/+5uefvrp886mTEhIUFBQkOLi4tSuXTtNnTq13H+OyovJ6fTGR0MBAAAu3/7UHPV6e+1lfeb04e1KnTtFtQdOVvC1nS567MoJXdQ8IsSdEj0SK5QAAACSsrKydGT3j7q2hlMWs+nSH7gMFrNJnZvX9sowKTHYHAAA+KCjR4/qo48+0v79+7Vnzx7t379fJ0+e6ewOCG+gJg99XK7v87aaTXppUKtLH1hFccsbAAD4nA8++EAPPvigzGazHI6S8ycff/xxtR38qCYv2HGBT1++V+9spcHtIsvtfJ6GQAkAAHxOQUGBbr75Zu3du1d2+3/fXFO3bl0dPHhQQUFBejd5v95I2uf2tZ7ofY3+2r252+fxZDxDCQAAfI7ValVMTEyJMCmdGTUUFBQkSXqk+9V65c5WCrCaL/uZSovZpACrWa/e2crrw6TECiUAAPAxhw4d0siRI7V+/Xp16dJFa9askclk0rXXXqsdO3bIYrGUOP73rDxNWbhD6w5kyGI2XfTZyuL9nZvX1kuDWqlxWFBFfx2PQKAEAAA+wel0aubMmRo/frzCwsI0a9YsderUSV27dtWGDRu0bNky9enT54Kf35+aozmbU5S8L00pmXk6O0CZJEWGB6l7iwgN7xDptd3cF0KgBAAAXi8tLU0PPPCAFi1apNGjR+uf//yn6+00x48f16pVq3TffffJZCrbre3cApsOZeaq0OaQv9WsqPBgBQf47vAcAiUAAPBqixcv1v333y+Hw6EPP/xQgwYNMrokr0NTDgAA8Eo5OTmKj4/XgAED1L59e+3cuZMwWUF8d20WAAB4rXXr1mnUqFFKT0/XRx99pPj4+DLfzsblY4USAAB4jYKCAj311FPq2rWrGjRooG3btmncuHGEyQrGCiUAAPAK27dv14gRI7R79269/PLLmjRpUqkRQKgYrFACAIAqzW636/XXX1e7du3kdDq1ZcsWPfXUU4TJSkSgBAAAVdZvv/2m7t2766mnntL48eO1ZcsW3XjjjUaX5XO45Q0AAKocp9OpGTNmaPz48QoPD1dycrK6du1qdFk+ixVKAABQpaSlpWnQoEGKj4/XPffco+3btxMmDcYKJQAAqDIWLVqk+++/X06nUwsXLtTAgQONLglihRIAAFQBxUPKBw4cqA4dOmjnzp2ESQ/CCiUAAPBoZw8p//jjjzV27FjmSnoYVigBAIBHOntIecOGDbVt2zbeeOOhWKEEAAAe5+wh5a+88ooef/xx5kp6MFYoAQCAx7Db7XrttddKDCl/8sknCZMejkAJAAA8QvGQ8smTJzOkvIrhljcAADDUuUPKV69erS5duhhdFi4DK5QAAMAw5xtSTpiselihBAAAhigeUi5JX3/9tQYMGGBwRbhSrFACAIBKdfLkSdeQ8ttuu007d+4kTFZxrFACAIBKs27dOo0cOVIZGRlKSEjQmDFjmCvpBVihBAAAFa6goEBPPvmkunbtqkaNGmn79u288caLsEIJAAAq1Pbt2zV8+HDt2bOHIeVeihVKAABQIc4eUi6JIeVejEAJAADKHUPKfQu3vAEAQLk5e0h57dq1GVLuI1ihBAAA5SItLU0DBw5UfHy87r33Xm3bto0w6SNYoQQAAG5jSLlvY4USAABcMYaUQ2KFEgAAXCGGlKMYK5QAAOCynD2kvHHjxgwpByuUAACg7IqHlO/du5ch5XBhhRIAAFyS3W7Xq6++qrZt20piSDlKIlACAICLKh5S/vTTT2vChAnasmWLWrdubXRZ8CDc8gYAAOfldDo1ffp0PfbYY6pdu7bWrFmjzp07G10WPBArlAAAoJTU1FQNGDBA48aN0+DBg7V9+3bCJC6IFUoAAFDC2UPKFy1apLi4OIMrgqdjhRIAAEg6M6R87NixGjhwoDp27KidO3cSJlEmrFACAACtXbtWo0aNUkZGhqZPn67Ro0czVxJlxgolAAA+rKCgQE888YS6devmGlLOG29wuVihBADAR23btk0jRozQ3r179eqrr2rixInMlcQVYYUSAAAfUzykvF27djKZTNqyZYueeOIJwiSuGIESAAAfcvDgQXXr1k1PP/20Jk6cqB9++IEh5XAbt7wBAPABDClHRWKFEgAAL8eQclQ0VigBAPBiX3/9tR544AFJDClHxWGFEgAAL3Ty5EmNGTNGgwYNYkg5KhwrlAAAeJk1a9Zo1KhRysrKYkg5KgUrlAAAeInTp0/riSeeUPfu3dWkSROGlKPSsEIJAIAX2LZtm4YPH659+/bptdde04QJE5griUrDCiUAAFXY2UPKzWaztm7dqkmTJhEmUakIlAAAVFHnG1LeqlUro8uCD+KWNwAAVYzT6VRCQoImTJigOnXqaO3aterUqZPRZcGHsUIJAEAVUjyk/P7779fgwYO1bds2wiQMxwolAABVxMKFC/XAAw/IbDYzpBwehRVKAAA8XPGQ8jvvvFOdOnViSDk8DiuUAAB4sLOHlM+YMUOjRo1iriQ8DiuUAAB4oPMNKeeNN/BUrFACAOBhGFKOqoYVSgAAPITdbtcrr7yidu3ayWKxMKQcVQaBEgAAD3Dw4EF17dpVU6ZM0cSJE7V582aGlKPK4JY3AAAGcjqd+vjjjzVhwgRFREQwpBxVEiuUAAAYJDU1VXFxcXrggQc0dOhQhpSjymKFEgAAA5w9pHzx4sXq37+/0SUBV4wVSgAAKtH5hpQTJlHVsUIJAEAlYUg5vBUrlAAAVLDTp09r0qRJDCmH12KFEgCACvTzzz9rxIgR2rdvn15//XVNmDBBZjPrOfAu/IoGAKAC2O12vfzyy7r11ltdQ8off/xxwiS8EiuUAACcJbfApkOZuSq0OeRvNSsqPFjBAZf3x+XBgwc1cuRIbdq0SU8++aSmTp2qgICACqoYMB6BEgDg8/an5mjO5hQl701TSlaenGftM0mKDAtS92siNKx9pK6uG3LB85w9pLxu3bpau3atbr/99gqvHzCayel0Oi99GAAA3uf3rDxNWbhD6w5kyGI2ye648B+Jxfs7N6+tlwa1UuOwoBL7U1NTNW7cOC1ZskTjxo3Tm2++qZCQC4dPwJsQKAEAPmnelhQ9t3iXbA7nRYPkuSxmk6xmk56Pa6kh7SIllRxS/vHHHzNXEj6HQAkA8DnvJu/XG0n73D7PI12aaOfnb2jWrFkaNGiQPvjgA9WpU6ccKgSqFgIlAMCnzNuSoskLdpTb+U5994H+8fBdGjlyJHMl4bMIlAAAnzEtYbYmTn5WRSeOy2krVP0x78i/btOLfub04e1KnTvlvPvqjXhdIZHXa9XEbqWeqQR8CYESAOAT0tPTVa9+AwU2vVkh7QbJZPGTX0SUzH6BF/1ccaCs2XWkAiNbl9jnV6eJ/AKD1LFpuD6Jb1+R5QMejbFBAACfsOr7n+Ww2xR0fXcFRra67M9bazVQQMNrS223O5xadyBDB9Jy1DyCrm74JgIlAMDrjR49WrNmzZIkZSx6VRmLXlVA4xtUb9grKji6V9kb5qngyB45ik7LGhKuas1vVVjPB8p8fovZpE+/T9HUuJYV9RUAj0agBAB4vWeffVYbsmvqwNf/dN26NgUEKf/gj0qb/6L8whupVvQ4WUPryJadqvzf/lPqHFlJ7ytj0Wsy+QUooOG1qtFxiAIbnwmQdodTyfvSNFUESvgmAiUAwOvVbdREOdXqSip56/rI/BdlDa2j+qPelMnq7zq+euterh+bA4IV0jZOgZGtZK4WItufx3Ry8wKlfva0Iu55TtWa3iJJSsnMU26B7bJf0wh4A37VAwC83uHM3FLbirKOyHbimGp2HVkiTJ7Lv14zhdVr9t8NjW9QUIvbdDThEf2ZPMMVKJ2SDmXmqmWDGuVdPuDxzEYXAABARSu0OUpts+dlS5IsIbUv+3zmwOqq1ryditIPyVFUcNHrAL6AQAkA8Hr+1tJ/3FmCzqwk2nMyruyk/zd17+xh5ue7DuAL+JUPAPB6UeHBpbb5hTWUtWZ9ndq+Qk5b0WWdz376lPJ/3SK/iKau2+WmC1wH8AU8QwkA8HrBAVbVDQ1U6jnbw3o/qLT5L+rY7McV2m7AmS7vk+nK/+0n1Yl7QpKUvvh1WUPryL9ec1mqharoz6M6+cPXsueeUHjsBNe5IsODaMiBz+JXPgDAJ9zUuKa2n7OtWtNbVG/YKzqxYa6yVn4op61Q1pDaqnb1ra5j/OtEKXf3OuX8Z5mchfkyVwtRQKPrVbv/RAXUbyHpzBzK7i0iKvHbAJ6FVy8CAHzC/tQc9Xp7bYWdf+WELrwpBz6LZygBAD7h6roh6ty8tixm06UPvgwWs0mdm9cmTMKnESgBAD7jpUGtZC3nQGk1m/TSoMt/NzjgTQiUAACvlp6ers8++0z9+/fX1Q3C1K/uqXI9/wtxLdU4LKhczwlUNTTlAAC8zvbt2/XFF19oyZIl2r59u85uF7ivfZSanaqpN5L2uX2dJ3pfo8HtIt0+D1DV0ZQDAPA6UVFROnz4cKnt7du31/fffy9JmrclRc8t3iWbwym7o+x/FFrMJlnNJr0Q15IwCfwfbnkDALzOe++9J7O59B9xf/3rX10/HtIuUisndFXHpuGSdMlmneL9HZuGa+WEroRJ4CysUAIAvI7T6dTdd9+tBQsWuLZVr15dqampCgoq/bzj/tQczdmcouR9aUrJzNPZfzCadGZoefcWERreIZJubuA8CJQAAK9it9v16KOP6t///rdiYmL07bffymQy6cEHH9S///3vS34+t8CmQ5m5KrQ55G81Kyo8mDfgAJfALW8AgNc4ffq0hgwZovfff18fffSRli1bpscee0wmk0kPPPBAmc4RHGBVywY1dFNkLbVsUIMwCZQBK5QAAK+QnZ2tgQMH6vvvv9fnn3+uuLg4SWduf//xxx9q3LixwRUC3otACQCo8o4dO6a+ffvq8OHDWrJkiW6//XajSwJ8Cuv4AIAqbf/+/erdu7eKioq0fv16tWzZ0uiSAJ/DM5QAgCpr69atuv3221WtWjVt3LiRMAkYhEAJAKiSkpKS1K1bNzVr1kzr1q1TZCRzIQGjECgBAFXOnDlzFBsbq27dumnVqlUKDw83uiTApxEoAQBVyptvvqnhw4dr+PDhWrhw4XkHlQOoXARKAECV4HA49OSTT+rxxx/X008/renTp8vPz8/osgCILm8AQBVQVFSk+Ph4ffrpp/rnP/+pRx991OiSAJyFQAkA8Gi5ubm65557tHLlSs2dO1eDBw82uiQA5yBQAgA8VkZGhmJjY/XLL79o6dKl6tmzp9ElATgPAiUAwCMdOnRIMTExOnHihNasWaObb77Z6JIAXABNOQAAj7N9+3Z17NhRNptNGzduJEwCHo5ACQDwKGvWrFGXLl1Ur149bdy4Uc2aNTO6JACXQKAEAHiMBQsWKCYmRm3bttXq1atVt25do0sCUAYESgCAR3j//fd1zz33aMCAAUpMTFRoaKjRJQEoIwIlAMBQTqdTU6dO1UMPPaRHHnlEc+fOVUBAgNFlAbgMdHkDAAxjt9v18MMP68MPP9TLL7+sp556SiaTyeiyAFwmAiUAwBCnT5/Wfffdp8WLF2v69OkaM2aM0SUBuEIESgBApTtx4oTi4uK0detWff3117rjjjuMLgmAGwiUAIBKdfToUfXp00d//PGHVq1apdtuu83okgC4iUAJAKg0e/fuVUxMjOx2u9avX6/rr7/e6JIAlAO6vAEAlWLz5s26/fbbFRwcrE2bNhEmAS9CoAQAVLhly5apR48euvbaa7Vu3To1atTI6JIAlCMCJQCgQs2ePVv9+/dXdHS0kpKSFBYWZnRJAMoZgRIAUCGcTqdef/11jRo1SqNHj9aCBQsUFBRkdFkAKgCBEgBQ7hwOhx5//HE9+eSTeuaZZ/TRRx/JaqUPFPBW/O4GAJSrwsJCjRkzRnPnztW7776rv/71r0aXBKCCESgBAOUmJydHd999t1avXq3PP/9c99xzj9ElAagEBEoAQLlIS0tTbGys9u7dq+XLl6t79+5GlwSgkhAoAQBuO3jwoGJiYpSTk6M1a9bopptuMrokAJWIphwAgFt+/vlndezYUZK0ceNGwiTggwiUAIArlpycrC5duqhRo0basGGDmjZtanRJAAxAoAQAXJEvv/xSffr0UYcOHZScnKyIiAijSwJgEAIlAOCyTZs2TYMHD9Zdd92lJUuWKCQkxOiSABiIQAkAKDOn06lnnnlGjzzyiB577DF9+umn8vf3N7osAAajyxsAUCY2m00PPvigEhIS9Nprr2nSpEkymUxGlwXAAxAoAQCXlJeXp6FDhyoxMVGzZs3SyJEjjS4JgAchUAIALiorK0txcXH6z3/+o8WLF6tfv35GlwTAwxAoAQAX9McffygmJkbHjx/XqlWr1KFDB6NLAuCBCJQAgPPavXu3YmJiZDKZtGHDBl177bVGlwTAQ9HlDQAoZdOmTerUqZNq1KihjRs3EiYBXBSBEgBQwpIlSxQdHa2WLVtq7dq1atiwodElAfBwBEoAgMuMGTM0cOBAxcTE6Ntvv1WtWrWMLglAFUCgBADI6XTq5Zdf1tixYxUfH6/58+erWrVqRpcFoIogUAKAj3M4HHrsscc0ZcoUPffcc3r//fdlsViMLgtAFUKXNwD4sIKCAo0aNUpffPGF3nvvPT344INGlwSgCiJQAoCPOnnypO68806tW7dOX375pe666y6jSwJQRREoAcAHpaamqm/fvvr111+VlJSkrl27Gl0SgCqMQAkAPubXX39VTEyM8vLytG7dOrVu3drokgBUcTTlAIAP+emnn9SxY0dZLBZt3LiRMAmgXBAoAcBHrFy5Ul27dlVUVJTWr1+vqKgoo0sC4CUIlADgA+bNm6d+/frp9ttv16pVq1SnTh2jSwLgRQiUAODl3nnnHQ0dOlSDBw/WN998o+rVqxtdEgAvQ6AEAC/ldDr19NNPa/z48Zo0aZJmzZolPz8/o8sC4IXo8gYAL2Sz2fTAAw9oxowZ+sc//qGJEycaXRIAL0agBAAvk5eXp8GDB2v58uX69NNPNWzYMKNLAuDlCJQA4EUyMzPVv39/bd++XUuWLFFMTIzRJQHwAQRKAPASKSkp6tOnj9LT05WcnKx27doZXRIAH0GgBAAvsHPnTvXp00d+fn7asGGDWrRoYXRJAHwIXd4AUMWtX79enTt3Vnh4uDZu3EiYBFDpCJQAUIUtXrxYvXr1Ups2bbR27VrVr1/f6JIA+CACJQBUUR9//LEGDRqkO+64Q8uWLVONGjWMLgmAjyJQAkAV43Q69be//U3333+/HnzwQc2bN0+BgYFGlwXAh9GUAwBViN1u1/jx4zVt2jS98MILeuaZZ2QymYwuC4CPI1ACQBVx+vRpjRgxQgsWLNCHH36o+++/3+iSAEASgRIAqoTs7GwNHDhQ33//vb766isNHDjQ6JIAwIVACQAe7tixY+rbt68OHz6sFStWqFOnTkaXBAAlECgBwIPt379fvXv3VlFRkdatW6cbbrjB6JIAoBS6vAHAQ23dulW33367AgMDtXHjRsIkAI9FoAQAD5SUlKRu3bqpWbNmWr9+vSIjI40uCQAuiEAJAB7ms88+U2xsrLp166aVK1cqPDzc6JIA4KIIlADgQd566y0NGzZMw4YN08KFCxUcHGx0SQBwSQRKAPAADodDTz75pCZOnKjJkydrxowZ8vPzM7osACgTurwBwGBFRUWKj4/XJ598orffflvjx483uiQAuCwESgAwUG5uru655x6tXLlSc+fO1ZAhQ4wuCQAuG4ESAAySkZGh2NhY/fLLL1q6dKl69uxpdEkAcEUIlADghtwCmw5l5qrQ5pC/1ayo8GAFB1z6P62HDx9WTEyM/vzzT61evVq33HJLJVQLABWDQAkAl2l/ao7mbE5R8t40pWTlyXnWPpOkyLAgdb8mQsPaR+rquiGlPr9jxw716dNHgYGB2rBhg5o3b15ptQNARTA5nU7npQ8DAPyelacpC3do3YEMWcwm2R0X/s9n8f7OzWvrpUGt1DgsSJK0du1axcXF6aqrrtKyZctUr169yiofACoMgRIAymDelhQ9t3iXbA7nRYPkuSxmk6xmk56Pa6mAP37U0KFDdfvtt2vhwoUKDQ2twIoBoPIQKAHgEt5N3q83kva5fZ4Taz9RTCOnZs+erYCAgHKoDAA8A89QAsBFzNuSUi5hUpJqdhmhgYNuIEwC8DoEyiriSjtJAVy537PyNPHlfytj3VzZThyX01ao+mPekX/dppd1npxt3ypr2b9k8gvUVOsCdWpex/VMJQB4AxKJB3O3kxSAeybOXqtji/6hak1vVljvh2Sy+Mka1uCyzmHLydCf302XpXqYHAV5sjmcmrJwhz6Jb19BVQNA5SNQeqCydJI6JR3OytMnmw9r5qZDpTpJAbhnf2qO1v24Q3LYFNyyuwIjW13RebKWT1Ng45YyB4Yob+8G2R1OrTuQoQNpOWoewV8EAXgHmnI8THl0kg5pF1mBFQLeweFw6PTp0woKOv9fwtpED9S27xaV2BbQ+AbVG/aKCo7uVfaGeSo4skeOotOyhoSrWvNbFdbzgRLHn9qZrKykf6vBuPd0Yu0nytu7QZGPz5fFbNKI9k00Na5lhX0/AKhMZqMLwH+9m7xfkxfsUIHNcVlhUpLsDqcKbA5NXrBD7ybvr6AKAe8xc+ZMhYaGavTo0dq9e3ep/f7t7lZY74ckSTW7jlS9EW8oLOZh5R/8Ucc/fUq2k+mqFT1Ode99XjU6DpY990SJz9tzT+jPVR+pVrfRsobWLrnP4VTyvrQK+24AUNkIlB6iPDtJ30jap8+3pJTLuQBvdeLECdntdn366ae6/vrrNWDAAG3evFmSdKrApnRTLfmFN5YkWWs1UEDDa+VfO1JZSe/LGlpH9Ue9qeqtohXYpLWqt+6lOgOeLHH+rKR/yy+soarf1O+810/JzFNuga1ivyQAVBKeofQA0xJma+LkZ1V0GV2khakHdWLtbBWmH5YjL1smq7+sYQ0VcvMdqn5Dd/2/xbvUsVltnqmET7DZbCosLFRBQYHrn2f/+Hz7duzYIZPJJLvdLklasmSJFi9erMjISD3/ToLOd4+gKOuIbCeOqWbXkTJZ/S9YT+6eDco78IPqj3lHJpPpvMc4JR3KzFXLBjXK4WcAAIxFoDRYenq6Hv1LvAKb3qxal9FF6jh9SpaQ2qp5XVdZQ8LlKDqt3F2rlbnkH7Jlpyq881A6SVGu7HZ7mYKaEfscDofb36/4HCkpKdrw/WZJbUr/HORlS5IsIbVL7XOdpzBfWSveU+gt/WWtHibH6VOSJKfjzGqk4/QpyWyV2T9QhTb36wYAT0CgNNiq73+Ww25T0PWX10Ua2KS1Apu0LrEtqPmtOpadqlPbvlXN24fQSVoFFYc2TwlqZ//TndBmMpkUEBAgf39/BQQElPjxuf8s/nFISMgF913sc2U95ssvv9Rf/vIXSZLFYpHdbtcdd9yhl19+Waawxlrxr/Wlvocl6Mxqoj0n44Lf1ZF3Uo7cEzr5w0Kd/GFhqf2/vz1E1a7uoIi7npG/laeOAHgHAqWBRo8erVmzZkmSMha9qoxFr152F+m5LNVC5fi/5gCL2aRPv0+hk/Qcdrvdo4La2fvcXWkrDk1lDVXVq1cvl8B2qW0Wi+WCt36NsnfvXtePAwMD1bNnT/Xu3VvZ2dlqVq+hzletX1hDWWvW16ntKxTabpBMVr9Sx1iq11LdoS+V2p79/XwV/L5TEfdMlTkoVCZJUeHB5fiNAMA4BEoDPfvss9qQXVMHvv6nanYdqcDI1jIFBCn/4I9Km/+i/MIbqVb0OFlD68iWnar83/5T6hxOp0NyOuU4fUp5e9Yr/7efFNbrQUn/7SSdqsoPlGeHtooMYVfy+eJn5q5UWQLU2f8MDg4u16B2oX1Wq9XjQpsni4mJ0ZtvvilJys3N1ZIlS7Ro0ZkxQWazWbe/skL7Dpf+XFjvB5U2/0Udm/24QtsNOPP782S68n/7SXXinpDJ6l/q7oEkndqxSjKZXfsiw4N42xUAr8F/zQxUt1ET5VSrK+m/XaSSdGT+i64u0rMf/K/eulepc2R9+2+d+nn5mX+xWBXW8y8Kuamva//hzDwtWb5CJnthpa7AVVRou1CYCg4OrpBVtnP3Edq8x4033qhatWrpzz//lKQSv2ZHjhypJtfU1YFtpf+/rtb0FtUb9opObJirrJUfymkrlDWktqpdfWuZr20xm9S9RYT7XwIAPASB0kCHM3NLbStrF2mxGrfdq+o3xsiRd0J5B35Q1or35Sg6rRrt73Qdc+eov6go7bdSn73cUHV2aKuoVbaAgABCGyqEw+HQTz/9pKVLlyoxMVFbtmzRue91MJlMmjZtmh566CHtT83RzE2t1GTyklLnCmh4rere+/xlXb/2HROkOyZIOnP3YHgHXkAAwHsQKA10vg7PsnSRns1aI0LWGmdWOqo1aydJOrFmlqq3inY1EHy9eIluiqxVIsQR2uALsrOztWLFCiUmJmrZsmVKTU1VjRo1FBMTo4cfflj+/v667777ZDKZZDab9cknn2jo0KGSpKvrhqhz89raeDDzsl80cDEWs0kdm4bTLAfAqxAoDXS+Ds+ydJFeTED9Fjr1n2WynTjuOleTxg1Vvz6z7uD9nE6n9uzZo8TERCUmJmr9+vWy2Wxq2bKlRo0apX79+qljx47y8zvTTJOfn6+AgABJ0oIFC9SvX8kh5C8NaqWeb60p10BpNZv00qArey84AHgqAqWBztfhWZYu0os5fXi7ZDLLWrOeJNFJCq+Xn5+v1atXu0LkoUOHVK1aNfXo0UPvvPOO+vXrpyZNmpz3s9WqVdO8efPUqFEjtW3bttT+xmFBej6upSYv2FFu9b4Q15IXDgDwOgRKAwUHWFU3NFCp52y/VBepJGUu+5fMAUHyr99CluCasuedVN7e9crbvU6h7e90rU7SSQpvdPjwYdezkN99953y8/MVFRWl2NhYxcbGqlu3bqpWrVqZzjVw4MCL7h/SLlIZpwrK5dWoT/S+RoPb8ewkAO9D0jDYTY1ravs528rSRRrQ8Fqd2r5Sp3askqMgV2a/QPlFXKXwOx5X9Ru6S6KTFN6jqKhIGzduVGJiopYuXapdu3bJarWqU6dOeuGFFxQbG6trr722wp4LfqT71apdPUDPLd4lm8N5WbfALWaTrGaTXohrSZgE4LVMznPbHFGp9qfmqNfbayvs/CsndOHhf1RJaWlpWrZsmRITE5WUlKTs7GzVrVtX/fr1U79+/dSrVy/VqFG5zwb/npWnKQt3aN2BDFnMposGy+L9nZvX1kuDWnGbG4BXI1B6gBEJmyusk5R3eaOqKB7rU/ws5NatWyVJ7dq1U2xsrPr166ebb75ZZrPxryvcn5qjOZtTlLwvTSmZeTr7d65JZx416d4iQsM7RPIXOgA+gUDpAX7PylPPt9ao4DxjhK5UgNWslRO6sioCj5adna2kpCQtXbq01Fif2NhY9enTRxERnv3YRm6BTYcyc1Voc8jfalZUeDDPLQPwOQRKDzFvS0q5dpK+emcrnteCx3E6ndq9e7frWcizx/oUN9TcdtttrrE+AICqgUDpQd5N3l9unaR/7d68HCoC3Jefn6/k5GRXiCwe6xMdHe16HvJCY30AAFUD92U8iFudpCbJVlSo1oV7NKpd1wqsEri0w4cPu56F/O6773T69GldddVVuuOOO9SvX7/LGusDAPB8rFB6oCvpJL0tqqbmP3WXbNmpqlWrlqZOnaq//OUvrreAABWpqKhIGzZscM2G/OWXX2S1WtW5c2dXQ01FjvUBABiLQOnBLreTNCIiQunp6Wf2m0yqX7++/va3v2nEiBGyWlmMRvlKTU3VsmXLtHTp0lJjfWJjY9WzZ89KH+sDADAGgbKKKEsnae/evbVixYpSn3344Yc1bdq0yioVXsrhcOjHH390PQu5ZcsWmUwm11if2NhY3XTTTR4x1gcAULlYtqoiggOsatng4qs9N9xwg1avXq2ioiJJksViUZ06dTRq1KjKKBFeqHisT2JiopYtW6a0tDTXWJ9HHnmkSoz1AQBUPAKlF7nuuutcYVKSatSooe3bt6tOnToGVoWq5OyxPomJidqwYYNsNptuuOEGjR49WrGxserYsSOPUAAASuBPBS9y3XXXSZICAwP12GOP6dVXX9W0adM0depUYwuDR7vYWJ9//etf6tu3L2N9AAAXRaD0Irfeeqv+3//7f7rvvvt0zTXXqFq1anr++efVs2dPderUyejy4EEOHTrk6sg+d6xPbGysunbtylgfAECZ0ZTjxWw2m7p3766UlBT9/PPPqlWrltElwSCXGusTGxura665hrE+AIArQqD0cocPH9aNN96omJgYzZs3j8DgQ4rH+iQmJiopKUknT54sMdanV69eCg0NNbpMAIAXIFD6gC+++EKDBw/W9OnTNWbMGKPLQQU5e6xPYmKitm7dKpPJpFtvvdU1XJyxPgCAikCg9BHx8fH6/PPP9dNPP6lFixZGl4NycuLECa1YsaLEWJ+aNWsqJiZGsbGxiomJYawPAKDCESh9xKlTp3TzzTcrNDRUGzdulL+/v9El4Qo4nU798ssvrmch169fL7vdrhtuuMH1LORtt93GWB8AQKUiUPqQH3/8Ubfddpsee+wxvfbaa0aXgzLKy8srMdbn8OHDrrE+xbeyIyMjjS4TAODDCJQ+5vXXX9eTTz6pFStWqGfPnkaXgws4dOiQ61nI5ORk11if4lXIbt26KTAw0OgyAQCQRKD0OQ6HQzExMdq1a5e2bdvGW3Q8RPFYn+IQuXv3blmtVnXp0sXVlc1YHwCApyJQ+qBjx46pdevWuu2227Ro0SJCikHON9anXr166tevn/r168dYHwBAlUGg9FHffPON4uLi9O677+qvf/2r0eX4BIfDoa1bt7oaahjrAwDwFgRKH/bII4/o448/1tatW3XDDTcYXY5XOnHihJKSkrR06dLzjvXp06cPjx0AAKo8AqUPy8/P16233ipJ+uGHH3h3czkoHutT3JFdPNanVatWrmchGesDAPA2BEoft3PnTrVt21b333+//vWvfxldTpV0vrE+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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "# g_ideal = nx.DiGraph(ann_graph)\n", - "# nx.draw(g_ideal, with_labels = True)\n", - "# plt.show()" + "g_ideal = nx.DiGraph(ann_graph)\n", + "nx.draw(g_ideal, with_labels = True)\n", + "plt.show()" ] }, { @@ -90,63 +101,47 @@ "output_type": "stream", "text": [ "=================================\n", - "> input_data: torch.Size([1, 100])\n", - " creating Node: Tensor_0(1, 100) \n", - " returning Node: fc1 \n", - "> output_data: torch.Size([1, 50])\n", - " returning Node: fc1 \n", - " creating Node: Tensor_1(1, 50) \n", + "source_node: Tensor_0(1, 100) (inp)\n", + "destination_node: fc1 (inp)\n", "\n", + "source_node: fc1 (out)\n", + "destination_node: Tensor_1(1, 50) (out)\n", "\n", - "> input_data: torch.Size([1, 50])\n", - " returning Node: Tensor_1(1, 50) \n", - " returning Node: fc2 \n", - "> output_data: torch.Size([1, 50])\n", - " returning Node: fc2 \n", - " creating Node: Tensor_2(1, 50) \n", + "source_node: Tensor_1(1, 50) (inp)\n", + "destination_node: fc2 (inp)\n", "\n", + "source_node: fc2 (out)\n", + "destination_node: Tensor_2(1, 50) (out)\n", "\n", - "> input_data: torch.Size([1, 50])\n", - " returning Node: Tensor_2(1, 50) \n", - " returning Node: fc3 \n", - "> output_data: torch.Size([1, 50])\n", - " returning Node: fc3 \n", - " creating Node: Tensor_3(1, 50) \n", + "source_node: Tensor_2(1, 50) (inp)\n", + "destination_node: fc3 (inp)\n", "\n", + "source_node: fc3 (out)\n", + "destination_node: Tensor_3(1, 50) (out)\n", "\n", - "> input_data: torch.Size([1, 50])\n", - " creating Node: Tensor_4(1, 50) \n", - " returning Node: fc4 \n", - "> output_data: torch.Size([1, 30])\n", - " returning Node: fc4 \n", - " creating Node: Tensor_5(1, 30) \n", + "source_node: Tensor_4(1, 50) (inp)\n", + "destination_node: fc4 (inp)\n", "\n", + "source_node: fc4 (out)\n", + "destination_node: Tensor_5(1, 50) (out)\n", "\n", - "> input_data: torch.Size([1, 30])\n", - " returning Node: Tensor_5(1, 30) \n", - " returning Node: fc5 \n", - "> output_data: torch.Size([1, 2])\n", - " returning Node: fc5 \n", - " creating Node: Tensor_6(1, 2) \n", + "source_node: Tensor_5(1, 50) (inp)\n", + "destination_node: fc5 (inp)\n", "\n", + "source_node: fc5 (out)\n", + "destination_node: Tensor_6(1, 2) (out)\n", "\n", - "> input_data: torch.Size([1, 100])\n", - "> output_data: torch.Size([1, 2])\n", + "***************************\n", + "source_node: fc1 ()\n", + "destination_node: fc2 ()\n", "\n", + "source_node: fc2 ()\n", + "destination_node: fc3 ()\n", + "\n", + "source_node: fc4 ()\n", + "destination_node: fc5 ()\n", "\n", - "++++++++++++++++++++++++++++++++\n", - " returning Node: fc1 \n", - " returning Node: fc2 \n", - " returning Node: fc2 \n", - " returning Node: fc3 \n", - " returning Node: fc4 \n", - " returning Node: fc5 \n", "-------------extract_nir_graph_v2(extract_torch_graph()) (wrong)---------------\n", - ">>>> fc1\n", - ">>>> fc2\n", - ">>>> fc3\n", - ">>>> fc4\n", - ">>>> fc5\n", "```mermaid\n", "graph TD;\n", "fc1 --> fc2;\n", @@ -172,11 +167,22 @@ "cell_type": "code", "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "image/png": "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zSq/a7wF7me1KNBm1Rtd6+mpyBoFVCkA5A2zvAGgEBmGoX/+85+aOXOmvvjiC3Xt2kb9ktM1ck68Vu9LlJ/VcsXJOn/ub1o9TK92qcdtbgAegVneAFCIXnnlFY0aNUoff/yxHn/88Qv27T2eoikbErRizwklJKXr/A9ni35ftDzy5nD1ahKhGuElCrVuALgSAiUAFJJPPvlEAwYM0EsvvaRRo0Zd8bVpWU4dSEpTttOlAJtV1cJCeAIOAI9FoASAQjB79mw98sgjGjhwoN577z1ZLO5dNggAzESgBIACtnLlSrVr105dunTR1KlTZbUyHxKAbyFQAkAB2rJli1q2bKm77rpL8+fPV2BgoNklAYDbESgBoID88ssvatasmapWrarly5erRAkm0gDwTQRKACgAx44dU7NmzWSz2bRmzRqVK1fO7JIAoMAwZRAA3OzMmTNq3769MjMz9f333xMmAfg8AiUAuFFmZqYeeughHThwQN99952qVatmdkkAUOAIlADgJrm5uYqKitKGDRu0ZMkS1atXz+ySAKBQECgBwA0Mw9DAgQM1d+5czZkzR82bNze7JAAoNARKAHCDF198UZ988ok+//xzdezY0exyAKBQsbouAOTT+++/r1deeUVvvPGGYmJizC4HAAodywYBQD5Mnz5dPXv21JAhQ/TWW2/xSEUARRKBEgCu05IlS9ShQwf16NFDsbGxPFIRQJFFoASA67Bp0yZFRkaqZcuW+uqrr+Tv7292SQBgGgIlAFyjn3/+Wc2aNdPNN9+sJUuWKCQkxOySAMBUBEoAuAaHDx9W06ZNVbx4ca1evVqhoaFmlwQApmPADwDkUXJystq1ayfDMLRo0SLCJAD8gXUoASAP0tPT1bFjRx07dkxr1qxRlSpVzC4JADwGgRIAriInJ0fdu3fXli1btHz5ctWqVcvskgDAoxAoAeAKDMNQ//79tXDhQs2fP1+NGzc2uyQA8DgESgC4gueee04Oh0NTpkxRu3btzC4HADwSk3IA4DLGjRunt956S++884569uxpdjkA4LFYNggALiEuLk52u10jRozQq6++anY5AODRCJQA8DfffPONHnroIcXExGjixIk8nxsAroJACQDnWbt2rVq3bq127drpiy++kM3GUHMAuBoCJQD8YceOHWrRooXq1q2rRYsWqVixYmaXBABegUAJAJIS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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "# g_nir = nx.DiGraph(nir_graph.edges)\n", - "# nx.draw(g_nir, with_labels = True)\n", - "# plt.show()" + "g_nir = nx.DiGraph(nir_graph.edges)\n", + "nx.draw(g_nir, with_labels = True)\n", + "plt.show()" ] } ], diff --git a/tests/test_residual/test_jit_tracer_ann.ipynb b/tests/test_residual/test_jit_tracer_ann.ipynb deleted file mode 100644 index 629ccec1..00000000 --- a/tests/test_residual/test_jit_tracer_ann.ipynb +++ /dev/null @@ -1,204 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "import torch\n", - "import torch.nn as nn" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "class NonSeq(nn.Module):\n", - " def __init__(self, *args, **kwargs) -> None:\n", - " super().__init__(*args, **kwargs)\n", - " self.fc1 = nn.Linear(100, 50)\n", - " self.fc2 = nn.Linear(50, 50)\n", - " self.fc3 = nn.Linear(50, 50)\n", - " self.fc4 = nn.Linear(50, 30)\n", - " self.fc5 = nn.Linear(30, 2)\n", - "\n", - " def forward(self, x):\n", - " out1 = self.fc1(x)\n", - " out2 = self.fc2(out1)\n", - " out3 = self.fc3(out2)\n", - " out4 = self.fc4(out3 + out2)\n", - " out5 = self.fc5(out4)\n", - " return out5\n", - "\n", - "model = NonSeq()" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "dummy_input = torch.randn(1, 100)" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "trace = torch.jit.trace(model, dummy_input)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "trace_output = trace(dummy_input)" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "graph(%self.1 : __torch__.___torch_mangle_15.NonSeq,\n", - " %x : Float(1, 100, strides=[100, 1], requires_grad=0, device=cpu)):\n", - " %fc5 : __torch__.torch.nn.modules.linear.___torch_mangle_14.Linear = prim::GetAttr[name=\"fc5\"](%self.1)\n", - " %fc4 : __torch__.torch.nn.modules.linear.___torch_mangle_13.Linear = prim::GetAttr[name=\"fc4\"](%self.1)\n", - " %fc3 : __torch__.torch.nn.modules.linear.___torch_mangle_12.Linear = prim::GetAttr[name=\"fc3\"](%self.1)\n", - " %fc2 : __torch__.torch.nn.modules.linear.___torch_mangle_11.Linear = prim::GetAttr[name=\"fc2\"](%self.1)\n", - " %fc1 : __torch__.torch.nn.modules.linear.___torch_mangle_10.Linear = prim::GetAttr[name=\"fc1\"](%self.1)\n", - " %61 : Tensor = prim::CallMethod[name=\"forward\"](%fc1, %x)\n", - " %62 : Tensor = prim::CallMethod[name=\"forward\"](%fc2, %61)\n", - " %63 : Tensor = prim::CallMethod[name=\"forward\"](%fc3, %62)\n", - " %32 : int = prim::Constant[value=1]() # /tmp/ipykernel_16069/242438593.py:14:0\n", - " %input.5 : Float(1, 50, strides=[50, 1], requires_grad=1, device=cpu) = aten::add(%63, %62, %32) # /tmp/ipykernel_16069/242438593.py:14:0\n", - " %64 : Tensor = prim::CallMethod[name=\"forward\"](%fc4, %input.5)\n", - " %65 : Tensor = prim::CallMethod[name=\"forward\"](%fc5, %64)\n", - " return (%65)\n", - "\n" - ] - } - ], - "source": [ - "print(trace.graph)" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "def forward(self,\n", - " x: Tensor) -> Tensor:\n", - " fc5 = self.fc5\n", - " fc4 = self.fc4\n", - " fc3 = self.fc3\n", - " fc2 = self.fc2\n", - " fc1 = self.fc1\n", - " _0 = (fc2).forward((fc1).forward(x, ), )\n", - " input = torch.add((fc3).forward(_0, ), _0)\n", - " _1 = (fc5).forward((fc4).forward(input, ), )\n", - " return _1\n", - " \n" - ] - } - ], - "source": [ - "print(trace.code, type(trace.code))" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(%fc5 : __torch__.torch.nn.modules.linear.___torch_mangle_14.Linear = prim::GetAttr[name=\"fc5\"](%self.1)\n", - ", fc5 defined in (%fc5 : __torch__.torch.nn.modules.linear.___torch_mangle_14.Linear = prim::GetAttr[name=\"fc5\"](%self.1)\n", - "))\n", - "(%fc4 : __torch__.torch.nn.modules.linear.___torch_mangle_13.Linear = prim::GetAttr[name=\"fc4\"](%self.1)\n", - ", fc4 defined in (%fc4 : __torch__.torch.nn.modules.linear.___torch_mangle_13.Linear = prim::GetAttr[name=\"fc4\"](%self.1)\n", - "))\n", - "(%fc3 : __torch__.torch.nn.modules.linear.___torch_mangle_12.Linear = prim::GetAttr[name=\"fc3\"](%self.1)\n", - ", fc3 defined in (%fc3 : __torch__.torch.nn.modules.linear.___torch_mangle_12.Linear = prim::GetAttr[name=\"fc3\"](%self.1)\n", - "))\n", - "(%fc2 : __torch__.torch.nn.modules.linear.___torch_mangle_11.Linear = prim::GetAttr[name=\"fc2\"](%self.1)\n", - ", fc2 defined in (%fc2 : __torch__.torch.nn.modules.linear.___torch_mangle_11.Linear = prim::GetAttr[name=\"fc2\"](%self.1)\n", - "))\n", - "(%fc1 : __torch__.torch.nn.modules.linear.___torch_mangle_10.Linear = prim::GetAttr[name=\"fc1\"](%self.1)\n", - ", fc1 defined in (%fc1 : __torch__.torch.nn.modules.linear.___torch_mangle_10.Linear = prim::GetAttr[name=\"fc1\"](%self.1)\n", - "))\n", - "(%61 : Tensor = prim::CallMethod[name=\"forward\"](%fc1, %x)\n", - ", 61 defined in (%61 : Tensor = prim::CallMethod[name=\"forward\"](%fc1, %x)\n", - "))\n", - "(%62 : Tensor = prim::CallMethod[name=\"forward\"](%fc2, %61)\n", - ", 62 defined in (%62 : Tensor = prim::CallMethod[name=\"forward\"](%fc2, %61)\n", - "))\n", - "(%63 : Tensor = prim::CallMethod[name=\"forward\"](%fc3, %62)\n", - ", 63 defined in (%63 : Tensor = prim::CallMethod[name=\"forward\"](%fc3, %62)\n", - "))\n", - "(%32 : int = prim::Constant[value=1]() # /tmp/ipykernel_16069/242438593.py:14:0\n", - ", 32 defined in (%32 : int = prim::Constant[value=1]() # /tmp/ipykernel_16069/242438593.py:14:0\n", - "))\n", - "(%input.5 : Float(1, 50, strides=[50, 1], requires_grad=1, device=cpu) = aten::add(%63, %62, %32) # /tmp/ipykernel_16069/242438593.py:14:0\n", - ", input.5 defined in (%input.5 : Float(1, 50, strides=[50, 1], requires_grad=1, device=cpu) = aten::add(%63, %62, %32) # /tmp/ipykernel_16069/242438593.py:14:0\n", - "))\n", - "(%64 : Tensor = prim::CallMethod[name=\"forward\"](%fc4, %input.5)\n", - ", 64 defined in (%64 : Tensor = prim::CallMethod[name=\"forward\"](%fc4, %input.5)\n", - "))\n", - "(%65 : Tensor = prim::CallMethod[name=\"forward\"](%fc5, %64)\n", - ", 65 defined in (%65 : Tensor = prim::CallMethod[name=\"forward\"](%fc5, %64)\n", - "))\n" - ] - } - ], - "source": [ - "edges = []\n", - "for node in trace.graph.nodes():\n", - " for next_node in node.outputs():\n", - " edges.append((node, next_node))\n", - " print(edges[-1])" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "speck-rescnn", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/tests/test_residual/test_jit_tracer_cnn.ipynb b/tests/test_residual/test_jit_tracer_cnn.ipynb deleted file mode 100644 index 62b05573..00000000 --- a/tests/test_residual/test_jit_tracer_cnn.ipynb +++ /dev/null @@ -1,149 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "import torch\n", - "import torch.nn as nn" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "class NonSeqCNN(nn.Module):\n", - " def __init__(self) -> None:\n", - " super().__init__()\n", - " self.conv1 = nn.Conv2d(3, 32, kernel_size=3, padding=1)\n", - " self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)\n", - " self.conv3 = nn.Conv2d(64, 64, kernel_size=3, padding=1)\n", - " self.conv4 = nn.Conv2d(64, 128, kernel_size=3, padding=1)\n", - " self.conv5 = nn.Conv2d(128, 128, kernel_size=3, padding=1)\n", - "\n", - " def forward(self, x):\n", - " out1 = torch.relu(self.conv1(x))\n", - " out2 = torch.relu(self.conv2(out1))\n", - " out3 = torch.relu(self.conv3(out2))\n", - " out4 = torch.relu(self.conv4(out3))\n", - " # Apply a 1x1 convolution to match dimensions for the skip connection\n", - " out2_skip = torch.relu(nn.Conv2d(64, 128, kernel_size=1)(out2))\n", - " # Introduce skip connection by adding outputs of conv2 and conv3\n", - " out4_skip = out4 + out2_skip\n", - " out5 = torch.relu(self.conv5(out4_skip))\n", - " return out5\n", - "\n", - "model = NonSeqCNN()" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "dummy_input = torch.randn(1, 3, 32, 32)" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/willian/.local/lib/python3.11/site-packages/torch/jit/_trace.py:1102: TracerWarning: Trace had nondeterministic nodes. Did you forget call .eval() on your model? Nodes:\n", - "\t%tensor.3 : Float(128, 64, 1, 1, strides=[64, 1, 1, 1], requires_grad=1, device=cpu) = aten::uniform_(%tensor.1, %173, %174, %175) # /home/willian/.local/lib/python3.11/site-packages/torch/nn/init.py:459:0\n", - "\t%bias.9 : Float(128, strides=[1], requires_grad=1, device=cpu) = aten::uniform_(%tensor, %202, %203, %204) # /home/willian/.local/lib/python3.11/site-packages/torch/nn/init.py:15:0\n", - "This may cause errors in trace checking. To disable trace checking, pass check_trace=False to torch.jit.trace()\n", - " _check_trace(\n", - "/home/willian/.local/lib/python3.11/site-packages/torch/jit/_trace.py:1102: TracerWarning: Output nr 1. of the traced function does not match the corresponding output of the Python function. Detailed error:\n", - "Tensor-likes are not close!\n", - "\n", - "Mismatched elements: 87964 / 131072 (67.1%)\n", - "Greatest absolute difference: 0.22646202147006989 at index (0, 69, 20, 16) (up to 1e-05 allowed)\n", - "Greatest relative difference: inf at index (0, 1, 0, 1) (up to 1e-05 allowed)\n", - " _check_trace(\n" - ] - } - ], - "source": [ - "trace = torch.jit.trace(model, dummy_input)" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "trace_output = trace(dummy_input)" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "def forward(self,\n", - " x: Tensor) -> Tensor:\n", - " conv5 = self.conv5\n", - " conv4 = self.conv4\n", - " conv3 = self.conv3\n", - " conv2 = self.conv2\n", - " conv1 = self.conv1\n", - " input = torch.relu((conv1).forward(x, ))\n", - " input0 = torch.relu((conv2).forward(input, ))\n", - " input1 = torch.relu((conv3).forward(input0, ))\n", - " out4 = torch.relu((conv4).forward(input1, ))\n", - " data = torch.empty([128, 64, 1, 1], dtype=None, layout=None, device=torch.device(\"cpu\"), pin_memory=False)\n", - " tensor = torch.detach(data)\n", - " data0 = torch.empty([128], dtype=None, layout=None, device=torch.device(\"cpu\"), pin_memory=False)\n", - " tensor0 = torch.detach(data0)\n", - " tensor1 = torch.uniform_(tensor, -0.12499999999999999, 0.12499999999999999)\n", - " bias = torch.uniform_(tensor0, -0.125, 0.125)\n", - " _0 = torch._convolution(input0, tensor1, bias, [1, 1], [0, 0], [1, 1], False, [0, 0], 1, False, False, True, True)\n", - " out2_skip = torch.relu(_0)\n", - " input2 = torch.add(out4, out2_skip)\n", - " return torch.relu((conv5).forward(input2, ))\n", - " \n" - ] - } - ], - "source": [ - "print(trace.code, type(trace.code))" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "speck-rescnn", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} From f38ca6faf5ecf77ca61442911e43f1132392e11e Mon Sep 17 00:00:00 2001 From: Willian-Girao Date: Tue, 9 Apr 2024 16:19:50 +0200 Subject: [PATCH 04/15] Revert "comp graph from nir not working" This reverts commit 47d5c0b19855d6c38af8516fce2f1414977d42eb. --- tests/test_residual/NIR_graph_nonseq.ipynb | 210 --------------------- 1 file changed, 210 deletions(-) delete mode 100644 tests/test_residual/NIR_graph_nonseq.ipynb diff --git a/tests/test_residual/NIR_graph_nonseq.ipynb b/tests/test_residual/NIR_graph_nonseq.ipynb deleted file mode 100644 index 0335cd8c..00000000 --- a/tests/test_residual/NIR_graph_nonseq.ipynb +++ /dev/null @@ -1,210 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import torch\n", - "import torch.nn as nn\n", - "from sinabs import to_nir\n", - "import networkx as nx\n", - "import matplotlib.pyplot as plt" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "class NonSeq(nn.Module):\n", - " def __init__(self, *args, **kwargs) -> None:\n", - " super().__init__(*args, **kwargs)\n", - "\n", - " self.fc1 = nn.Linear(100, 50)\n", - " self.fc2 = nn.Linear(50, 50)\n", - " self.fc3 = nn.Linear(50, 50)\n", - " self.fc4 = nn.Linear(50, 50)\n", - " self.fc5 = nn.Linear(50, 2)\n", - "\n", - " def forward(self, x):\n", - "\n", - " out1 = self.fc1(x)\n", - " out2 = self.fc2(out1)\n", - " out3 = self.fc3(out2)\n", - " out4 = self.fc4(out3 + out2)\n", - " out5 = self.fc5(out4)\n", - "\n", - " return out5\n", - "\n", - "ann = NonSeq()\n" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "7\n" - ] - } - ], - "source": [ - "ann_graph = [\n", - " ('input', 'fc1'),\n", - " ('fc1', 'fc2'),\n", - " ('fc2', 'fc3'),\n", - " ('fc2', 'fc4'),\n", - " ('fc3', 'fc4'),\n", - " ('fc4', 'fc5'),\n", - " ('fc5', 'output'),\n", - "]\n", - "\n", - "print(len(ann_graph))" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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gbKKKEsnae/evbVixYpSn3344Yc1bdq0yioVXsrhcOjHH390PQu5ZcsWmUwm11if2NhY3XTTTR4x1gcAULlYtqoiggOsatng4qs9N9xwg1avXq2ioiJJksViUZ06dTRq1KjKKBFeqHisT2JiopYtW6a0tDTXWJ9HHnmkSoz1AQBUPAKlF7nuuutcYVKSatSooe3bt6tOnToGVoWq5OyxPomJidqwYYNsNptuuOEGjR49WrGxserYsSOPUAAASuBPBS9y3XXXSZICAwP12GOP6dVXX9W0adM0depUYwuDR7vYWJ9//etf6tu3L2N9AAAXRaD0Irfeeqv+3//7f7rvvvt0zTXXqFq1anr++efVs2dPderUyejy4EEOHTrk6sg+d6xPbGysunbtylgfAECZ0ZTjxWw2m7p3766UlBT9/PPPqlWrltElwSCXGusTGxura665hrE+AIArQqD0cocPH9aNN96omJgYzZs3j8DgQ4rH+iQmJiopKUknT54sMdanV69eCg0NNbpMAIAXIFD6gC+++EKDBw/W9OnTNWbMGKPLQQU5e6xPYmKitm7dKpPJpFtvvdU1XJyxPgCAikCg9BHx8fH6/PPP9dNPP6lFixZGl4NycuLECa1YsaLEWJ+aNWsqJiZGsbGxiomJYawPAKDCESh9xKlTp3TzzTcrNDRUGzdulL+/v9El4Qo4nU798ssvrmch169fL7vdrhtuuMH1LORtt93GWB8AQKUiUPqQH3/8Ubfddpsee+wxvfbaa0aXgzLKy8srMdbn8OHDrrE+xbeyIyMjjS4TAODDCJQ+5vXXX9eTTz6pFStWqGfPnkaXgws4dOiQ61nI5ORk11if4lXIbt26KTAw0OgyAQCQRKD0OQ6HQzExMdq1a5e2bdvGW3Q8RPFYn+IQuXv3blmtVnXp0sXVlc1YHwCApyJQ+qBjx46pdevWuu2227Ro0SJCikHON9anXr166tevn/r168dYHwBAlUGg9FHffPON4uLi9O677+qvf/2r0eX4BIfDoa1bt7oaahjrAwDwFgRKH/bII4/o448/1tatW3XDDTcYXY5XOnHihJKSkrR06dLzjvXp06cPjx0AAKo8AqUPy8/P16233ipJ+uGHH3h3czkoHutT3JFdPNanVatWrmchGesDAPA2BEoft3PnTrVt21b333+//vWvfxldTpV0vrE+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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "g_ideal = nx.DiGraph(ann_graph)\n", - "nx.draw(g_ideal, with_labels = True)\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=================================\n", - "source_node: Tensor_0(1, 100) (inp)\n", - "destination_node: fc1 (inp)\n", - "\n", - "source_node: fc1 (out)\n", - "destination_node: Tensor_1(1, 50) (out)\n", - "\n", - "source_node: Tensor_1(1, 50) (inp)\n", - "destination_node: fc2 (inp)\n", - "\n", - "source_node: fc2 (out)\n", - "destination_node: Tensor_2(1, 50) (out)\n", - "\n", - "source_node: Tensor_2(1, 50) (inp)\n", - "destination_node: fc3 (inp)\n", - "\n", - "source_node: fc3 (out)\n", - "destination_node: Tensor_3(1, 50) (out)\n", - "\n", - "source_node: Tensor_4(1, 50) (inp)\n", - "destination_node: fc4 (inp)\n", - "\n", - "source_node: fc4 (out)\n", - "destination_node: Tensor_5(1, 50) (out)\n", - "\n", - "source_node: Tensor_5(1, 50) (inp)\n", - "destination_node: fc5 (inp)\n", - "\n", - "source_node: fc5 (out)\n", - "destination_node: Tensor_6(1, 2) (out)\n", - "\n", - "***************************\n", - "source_node: fc1 ()\n", - "destination_node: fc2 ()\n", - "\n", - "source_node: fc2 ()\n", - "destination_node: fc3 ()\n", - "\n", - "source_node: fc4 ()\n", - "destination_node: fc5 ()\n", - "\n", - "-------------extract_nir_graph_v2(extract_torch_graph()) (wrong)---------------\n", - "```mermaid\n", - "graph TD;\n", - "fc1 --> fc2;\n", - "fc2 --> fc3;\n", - "fc3;\n", - "fc4 --> fc5;\n", - "fc5;\n", - "\n", - "```\n", - " \n", - "--------------------------------------------------------\n", - "0: 1\n", - "1: 6 [('input', 'fc1'), ('fc1', 'fc2'), ('fc2', 'fc3'), ('fc3', 'output'), ('fc4', 'fc5'), ('fc5', 'output')]\n", - "2: 6\n" - ] - } - ], - "source": [ - "nir_graph = to_nir(ann, torch.randn(1, 100))" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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zSq/a7wF7me1KNBm1Rtd6+mpyBoFVCkA5A2zvAGgEBmGoX/+85+aOXOmvvjiC3Xt2kb9ktM1ck68Vu9LlJ/VcsXJOn/ub1o9TK92qcdtbgAegVneAFCIXnnlFY0aNUoff/yxHn/88Qv27T2eoikbErRizwklJKXr/A9ni35ftDzy5nD1ahKhGuElCrVuALgSAiUAFJJPPvlEAwYM0EsvvaRRo0Zd8bVpWU4dSEpTttOlAJtV1cJCeAIOAI9FoASAQjB79mw98sgjGjhwoN577z1ZLO5dNggAzESgBIACtnLlSrVr105dunTR1KlTZbUyHxKAbyFQAkAB2rJli1q2bKm77rpL8+fPV2BgoNklAYDbESgBoID88ssvatasmapWrarly5erRAkm0gDwTQRKACgAx44dU7NmzWSz2bRmzRqVK1fO7JIAoMAwZRAA3OzMmTNq3769MjMz9f333xMmAfg8AiUAuFFmZqYeeughHThwQN99952qVatmdkkAUOAIlADgJrm5uYqKitKGDRu0ZMkS1atXz+ySAKBQECgBwA0Mw9DAgQM1d+5czZkzR82bNze7JAAoNARKAHCDF198UZ988ok+//xzdezY0exyAKBQsbouAOTT+++/r1deeUVvvPGGYmJizC4HAAodywYBQD5Mnz5dPXv21JAhQ/TWW2/xSEUARRKBEgCu05IlS9ShQwf16NFDsbGxPFIRQJFFoASA67Bp0yZFRkaqZcuW+uqrr+Tv7292SQBgGgIlAFyjn3/+Wc2aNdPNN9+sJUuWKCQkxOySAMBUBEoAuAaHDx9W06ZNVbx4ca1evVqhoaFmlwQApmPADwDkUXJystq1ayfDMLRo0SLCJAD8gXUoASAP0tPT1bFjRx07dkxr1qxRlSpVzC4JADwGgRIAriInJ0fdu3fXli1btHz5ctWqVcvskgDAoxAoAeAKDMNQ//79tXDhQs2fP1+NGzc2uyQA8DgESgC4gueee04Oh0NTpkxRu3btzC4HADwSk3IA4DLGjRunt956S++884569uxpdjkA4LFYNggALiEuLk52u10jRozQq6++anY5AODRCJQA8DfffPONHnroIcXExGjixIk8nxsAroJACQDnWbt2rVq3bq127drpiy++kM3GUHMAuBoCJQD8YceOHWrRooXq1q2rRYsWqVixYmaXBABegUAJAJIS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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "g_nir = nx.DiGraph(nir_graph.edges)\n", - "nx.draw(g_nir, with_labels = True)\n", - "plt.show()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "speck-rescnn", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} From 5ecb6136fcddf03ddeee7aee43fb2e29683207eb Mon Sep 17 00:00:00 2001 From: Felix Bauer Date: Fri, 14 Jun 2024 12:10:34 +0200 Subject: [PATCH 05/15] Make spike count plot optional --- .../backend/dynapcnn/dynapcnn_visualizer.py | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/sinabs/backend/dynapcnn/dynapcnn_visualizer.py b/sinabs/backend/dynapcnn/dynapcnn_visualizer.py index d581ac5b..a5bd08c2 100644 --- a/sinabs/backend/dynapcnn/dynapcnn_visualizer.py +++ b/sinabs/backend/dynapcnn/dynapcnn_visualizer.py @@ -15,6 +15,7 @@ class DynapcnnVisualizer: # (tlx, tly, brx, bry) DEFAULT_LAYOUT_DS = [(0, 0, 0.5, 1), (0.5, 0, 1, 1), None, None] DEFAULT_LAYOUT_DSP = [(0, 0, 0.5, 0.66), (0.5, 0, 1, 0.66), None, (0, 0.66, 1, 1)] + DEFAULT_LAYOUT_DRP = [(0, 0, 0.5, 0.66), None, (0.5, 0, 1, 0.66), (0, 0.66, 1, 1)] DEFAULT_LAYOUT_DSR = [(0, 0, 0.33, 1), (0.33, 0, 0.66, 1), (0.66, 0, 1, 1), None] DEFAULT_LAYOUT_DSRP = [ @@ -27,6 +28,7 @@ class DynapcnnVisualizer: LAYOUTS_DICT = { "ds": DEFAULT_LAYOUT_DS, "dsp": DEFAULT_LAYOUT_DSP, + "drp": DEFAULT_LAYOUT_DRP, "dsr": DEFAULT_LAYOUT_DSR, "dsrp": DEFAULT_LAYOUT_DSRP, } @@ -36,6 +38,7 @@ def __init__( window_scale: Tuple[int, int] = (4, 8), dvs_shape: Tuple[int, int] = (128, 128), # height, width add_readout_plot: bool = False, + add_spike_count_plot: bool = True, add_power_monitor_plot: bool = False, spike_collection_interval: int = 500, readout_prediction_threshold: int = 10, @@ -58,6 +61,10 @@ def __init__( Defaults to (128, 128) -- Speck sensor resolution. add_readout_plot: bool (defaults to False) If set true adds a readout plot to the GUI + It displays an icon for the currently predicted class. + add_spike_count_plot: bool (defaults to True) + If set true adds a spike count plot to the GUI. + A line chart indicating the number of spikes over time. add_power_monitor_plot: bool (defaults to False) If set true adds a power monitor plot to the GUI. spike_collection_interval: int (defaults to 500) (in milliseconds) @@ -112,7 +119,9 @@ def __init__( self.dvs_shape = dvs_shape # Modify the GUI type based on the parameters - self.gui_type = "ds" + self.gui_type = "d" + if add_spike_count_plot: + self.gui_type += "s" if add_readout_plot: self.gui_type += "r" if add_power_monitor_plot: @@ -338,13 +347,14 @@ def create_plots(self): plots = [] plots.append(self.add_dvs_plot(shape=self.dvs_shape, layout=layout[0])) - if self.extra_arguments and "spike_count" in self.extra_argument.keys(): - spike_count_plot_args = self.extra_arguments["spike_count"] - else: - spike_count_plot_args = {} - plots.append( - self.add_spike_count_plot(layout=layout[1], **spike_count_plot_args) - ) + if "s" in self.gui_type: + if self.extra_arguments and "spike_count" in self.extra_argument.keys(): + spike_count_plot_args = self.extra_arguments["spike_count"] + else: + spike_count_plot_args = {} + plots.append( + self.add_spike_count_plot(layout=layout[1], **spike_count_plot_args) + ) if "r" in self.gui_type: try: if self.extra_arguments and "readout" in self.extra_arguments.keys(): From 27f5b417b3fbb404bc91f14be5ace43f29913d7f Mon Sep 17 00:00:00 2001 From: Felix Bauer Date: Fri, 14 Jun 2024 12:14:35 +0200 Subject: [PATCH 06/15] Update changelog --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog b/ChangeLog index 6dff00ce..13d60eb3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,8 @@ CHANGES ======= +* Spike count plot in 'DynapcnnVisualizer' is optional + v2.0.0 ------ From 20b3230d402a73f0ea7750ce306dba055f38484a Mon Sep 17 00:00:00 2001 From: Felix Bauer Date: Fri, 14 Jun 2024 12:30:32 +0200 Subject: [PATCH 07/15] Include new dynapcnn visualizer spike count plot argument in docs --- docs/speck/visualizer.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/speck/visualizer.md b/docs/speck/visualizer.md index d2fc2775..a5b1ea8a 100644 --- a/docs/speck/visualizer.md +++ b/docs/speck/visualizer.md @@ -131,6 +131,7 @@ hardware_compatible_model.to( In order to visualize the class outputs as images, we need to get the images. The images should be passed in the same order as the output layer of the network. Important!
- If you want to visualize power measurements during streaming inference, set `add_power_monitor_plot`=`True`. - If you want to visualize readout images as class predictions during streaming you need to pass `add_readout_plot`=`True`. +- If you don't want to visualize spike counts of output classes as line graphs over time during streaming you need to pass `add_spike_count_plot`=`False`. - In order to show a prediction for each `N` milliseconds, set the parameter `spike_collection_interval`=`N`. - In order to show the images, the paths of these images should be passed to `readout_images` parameter. - In order to show a prediction only if there are more than a `threshold` number of events from that output, set the `readout_prediction_threshold`=`threshold`. @@ -172,4 +173,4 @@ The example script that runs the visualizer can be found under `/examples/visual #### MacOS users -Due to the difference in the behaviour of python's multiprocessing library on MacOS, you should run the `examples/visualizer/gesture_viz.py` script with `-i` flag. `python -i /examples/visualizer/gesture_viz.py` . \ No newline at end of file +Due to the difference in the behaviour of python's multiprocessing library on MacOS, you should run the `examples/visualizer/gesture_viz.py` script with `-i` flag. `python -i /examples/visualizer/gesture_viz.py` . From cb975d92de1768c67b3e9a86674e803703c89681 Mon Sep 17 00:00:00 2001 From: Felix Bauer Date: Fri, 14 Jun 2024 12:42:49 +0200 Subject: [PATCH 08/15] Unit test for optional argument to --- tests/test_dynapcnn/test_visualizer.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/test_dynapcnn/test_visualizer.py b/tests/test_dynapcnn/test_visualizer.py index b1c4e650..0873f318 100644 --- a/tests/test_dynapcnn/test_visualizer.py +++ b/tests/test_dynapcnn/test_visualizer.py @@ -1,3 +1,5 @@ +from itertools import product + import pytest import samna from hw_utils import find_open_devices, is_any_samna_device_connected @@ -17,13 +19,16 @@ def X_available() -> bool: True, reason="A window needs to pop. Needs UI. Makes sense to check this test manually", ) -def test_visualizer_initialization(): +@pytest.mark.parametrize("spike_count_plot", (True, False)) +def test_visualizer_initialization(spike_count_plot: bool): dvs_shape = (128, 128) spike_collection_interval = 500 visualizer_id = 3 visualizer = DynapcnnVisualizer( - dvs_shape=dvs_shape, spike_collection_interval=spike_collection_interval + dvs_shape=dvs_shape, + spike_collection_interval=spike_collection_interval, + add_spike_count_plot=spike_count_plot, ) visualizer.create_visualizer_process( f"tcp://0.0.0.0:{visualizer.samna_visualizer_port}" From dae4df12aa3c45e05032d828f4d6b3b9177cf9ef Mon Sep 17 00:00:00 2001 From: Felix Bauer Date: Fri, 14 Jun 2024 12:42:49 +0200 Subject: [PATCH 09/15] Unit test for optional argument to --- .../backend/dynapcnn/dynapcnn_visualizer.py | 46 +++++++++++++------ tests/test_dynapcnn/test_visualizer.py | 22 ++++++++- 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/sinabs/backend/dynapcnn/dynapcnn_visualizer.py b/sinabs/backend/dynapcnn/dynapcnn_visualizer.py index a5bd08c2..5f54abf6 100644 --- a/sinabs/backend/dynapcnn/dynapcnn_visualizer.py +++ b/sinabs/backend/dynapcnn/dynapcnn_visualizer.py @@ -1,6 +1,6 @@ import socket import warnings -from typing import Dict, List, Optional, Tuple +from typing import Callable, Dict, List, Optional, Tuple, Union import samna @@ -49,6 +49,7 @@ def __init__( feature_names: Optional[List[str]] = None, readout_images: Optional[List[str]] = None, feature_count: Optional[int] = None, + readout_node: Union[str, Callable] = "JitMajorityReadout", extra_arguments: Optional[Dict[str, Dict[str, any]]] = None, ): """Quick wrapper around Samna objects to get a basic dynapcnn visualizer. @@ -98,6 +99,9 @@ def __init__( If the `feature_names` and `readout_images` was passed, this is not needed. Otherwise this parameter should be passed, so that the GUI knows how many lines should be drawn on the `Spike Count Plot` and `Readout Layer Plot`. + readout_node: str or Callable + Can either be a string "JitMajorityReadout" or a callable that returns a samna JIT filter + to decide on the readout prediction. Function parameters can be defined freely. extra_arguments: Optional[Dict[str, Dict[str, any]]] (defaults to None) Extra arguments that can be passed to individual plots. Available keys are: - `spike_count`: Arguments that can be passed to `spike_count` plot. @@ -135,6 +139,7 @@ def __init__( self.readout_default_return_value = readout_default_return_value self.readout_default_threshold_low = readout_default_threshold_low self.readout_default_threshold_high = readout_default_threshold_high + self.readout_node = readout_node # Power monitor components if power_monitor_number_of_items != 3 and power_monitor_number_of_items != 5: @@ -518,19 +523,32 @@ def connect( ## Readout node if "r" in self.gui_type: - (_, majority_readout_node, _) = self.streamer_graph.sequential( - [ - spike_collection_node, - samna.graph.JitMajorityReadout(samna.ui.Event), - streamer_node, - ] - ) - majority_readout_node.set_feature_count(self.feature_count) - majority_readout_node.set_default_feature(self.readout_default_return_value) - majority_readout_node.set_threshold_low(self.readout_default_threshold_low) - majority_readout_node.set_threshold_high( - self.readout_default_threshold_high - ) + if self.readout_node == "JitMajorityReadout": + (_, majority_readout_node, _) = self.streamer_graph.sequential( + [ + spike_collection_node, + samna.graph.JitMajorityReadout(samna.ui.Event), + streamer_node, + ] + ) + majority_readout_node.set_feature_count(self.feature_count) + majority_readout_node.set_default_feature( + self.readout_default_return_value + ) + majority_readout_node.set_threshold_low( + self.readout_default_threshold_low + ) + majority_readout_node.set_threshold_high( + self.readout_default_threshold_high + ) + else: + (_, majority_readout_node, _) = self.streamer_graph.sequential( + [ + spike_collection_node, + self.readout_node, + streamer_node, + ] + ) ## Readout layer visualization if "o" in self.gui_type: diff --git a/tests/test_dynapcnn/test_visualizer.py b/tests/test_dynapcnn/test_visualizer.py index b1c4e650..5e3c5136 100644 --- a/tests/test_dynapcnn/test_visualizer.py +++ b/tests/test_dynapcnn/test_visualizer.py @@ -1,5 +1,9 @@ +from itertools import product +from typing import Callable, Union + import pytest import samna +from custom_jit_filters import majority_readout_filter as custom_filter from hw_utils import find_open_devices, is_any_samna_device_connected from sinabs.backend.dynapcnn.dynapcnn_visualizer import DynapcnnVisualizer @@ -13,17 +17,31 @@ def X_available() -> bool: return p.returncode == 0 +vis_init_args = product( + (True, False), + (True, False), + ("JitMajorityReadout", custom_filter), +) + + @pytest.mark.skipif( True, reason="A window needs to pop. Needs UI. Makes sense to check this test manually", ) -def test_visualizer_initialization(): +@pytest.mark.parametrize("spike_count_plot,readout_plot", vis_init_args) +def test_visualizer_initialization( + spike_count_plot: bool, readout_plot: bool, readout_filter: Union[str, Callable] +): dvs_shape = (128, 128) spike_collection_interval = 500 visualizer_id = 3 visualizer = DynapcnnVisualizer( - dvs_shape=dvs_shape, spike_collection_interval=spike_collection_interval + dvs_shape=dvs_shape, + spike_collection_interval=spike_collection_interval, + add_spike_count_plot=spike_count_plot, + add_readout_plot=spike_readout_plot, + readout_filter=readout_filter, ) visualizer.create_visualizer_process( f"tcp://0.0.0.0:{visualizer.samna_visualizer_port}" From 94336e66cb413b36beaa87d7eacaa842f15bfb16 Mon Sep 17 00:00:00 2001 From: Felix Bauer Date: Tue, 6 Aug 2024 16:02:51 +0200 Subject: [PATCH 10/15] Add module for unit test of visualizer --- tests/test_dynapcnn/custom_jit_filters.py | 88 +++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 tests/test_dynapcnn/custom_jit_filters.py diff --git a/tests/test_dynapcnn/custom_jit_filters.py b/tests/test_dynapcnn/custom_jit_filters.py new file mode 100644 index 00000000..6323ccde --- /dev/null +++ b/tests/test_dynapcnn/custom_jit_filters.py @@ -0,0 +1,88 @@ +from typing import Optional + +import samna + + +def majority_readout_filter( + feature_count: int, + default_feature: Optional[int] = None, + detection_threshold: int = 0, + threshold_low: int = 0, + threshold_high: Optional[int] = None, +): + """ + The default reaodut filter of samna's visualizer counts the total + number of events received per timestep to decide whether a detection + should be made or not. + + The filter defined here allows for an additional `detection_threshold` + parameter which is compared to the number of spikes of the most + active class. + In other words, for a class to be detected, there needs to be + a minimum number of spikes for this class. + """ + + jit_src = f""" +using InputT = speck2f::event::Spike; +using OutputT = ui::Event; +using ReadoutT = ui::Readout; + +template +class CustomMajorityReadout : public iris::FilterInterface>, std::shared_ptr>> {{ +private: + int featureCount = {feature_count}; + uint32_t defaultFeature = {default_feature if default_feature is not None else feature_count}; + int detectionThreshold = {detection_threshold}; + int thresholdLow = {threshold_low}; + int thresholdHigh = {threshold_high if threshold_high is not None else "std::numeric_limits::max()"}; + +public: + void apply() override + {{ + while (const auto maybeSpikesPtr = this->receiveInput()) {{ + if (0 == featureCount) {{ + return; + }} + + auto outputCollection = std::make_shared>(); + if ((*maybeSpikesPtr)->size() >= thresholdLow && (*maybeSpikesPtr)->size() <= thresholdHigh) {{ + std::unordered_map sum; // feature -> count + int maxCount = 0; + uint32_t maxCountFeature = 0; + int maxCountNum = 0; + + for (const auto& spike : (**maybeSpikesPtr)) {{ + sum[spike.feature]++; + }} + + for (const auto& [feature, count] : sum) {{ + if (feature >= featureCount) {{ + continue; + }} + + if (count > maxCount) {{ + maxCount = count; + maxCountFeature = feature; + maxCountNum = 1; + }} + else if (count == maxCount) {{ + maxCountNum++; + }} + }} + + if (maxCount > detectionThreshold && 1 == maxCountNum) {{ + outputCollection->emplace_back(ReadoutT{{maxCountFeature}}); + }} + else {{ + outputCollection->emplace_back(ReadoutT{{defaultFeature}}); + }} + }} + else {{ + outputCollection->emplace_back(ReadoutT{{defaultFeature}}); + }} + this->forwardResult(std::move(outputCollection)); + }} + }} +}}; +""" + return samna.graph.JitFilter("CustomMajorityReadout", jit_src) From c30e555b5ad6870ee7df4c881238d3734bf279a2 Mon Sep 17 00:00:00 2001 From: Felix Bauer Date: Tue, 6 Aug 2024 16:15:27 +0200 Subject: [PATCH 11/15] Minor fixes in dynapcnn visualizer and unit test --- sinabs/backend/dynapcnn/dynapcnn_visualizer.py | 2 +- tests/test_dynapcnn/test_visualizer.py | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/sinabs/backend/dynapcnn/dynapcnn_visualizer.py b/sinabs/backend/dynapcnn/dynapcnn_visualizer.py index 5f54abf6..0c7f0799 100644 --- a/sinabs/backend/dynapcnn/dynapcnn_visualizer.py +++ b/sinabs/backend/dynapcnn/dynapcnn_visualizer.py @@ -109,7 +109,7 @@ def __init__( - `power_measurement`: Arguments that can be passed `power_measurement` plot. """ # Checks if the configuration passed is valid - if add_readout_plot and not readout_images: + if add_readout_plot and readout_images is None: raise ValueError( "If a readout plot is to be displayed image paths should be passed as a list." + "The order of the images, should match the model output." diff --git a/tests/test_dynapcnn/test_visualizer.py b/tests/test_dynapcnn/test_visualizer.py index 5e3c5136..2885fee6 100644 --- a/tests/test_dynapcnn/test_visualizer.py +++ b/tests/test_dynapcnn/test_visualizer.py @@ -28,9 +28,9 @@ def X_available() -> bool: True, reason="A window needs to pop. Needs UI. Makes sense to check this test manually", ) -@pytest.mark.parametrize("spike_count_plot,readout_plot", vis_init_args) +@pytest.mark.parametrize("spike_count_plot,readout_plot,readout_node", vis_init_args) def test_visualizer_initialization( - spike_count_plot: bool, readout_plot: bool, readout_filter: Union[str, Callable] + spike_count_plot: bool, readout_plot: bool, readout_node: Union[str, Callable] ): dvs_shape = (128, 128) spike_collection_interval = 500 @@ -40,8 +40,9 @@ def test_visualizer_initialization( dvs_shape=dvs_shape, spike_collection_interval=spike_collection_interval, add_spike_count_plot=spike_count_plot, - add_readout_plot=spike_readout_plot, - readout_filter=readout_filter, + add_readout_plot=readout_plot, + readout_node=readout_node, + readout_images=[], ) visualizer.create_visualizer_process( f"tcp://0.0.0.0:{visualizer.samna_visualizer_port}" From 394778b657b0a8e6f75a6af630978c4c82b613b5 Mon Sep 17 00:00:00 2001 From: Felix Bauer Date: Tue, 6 Aug 2024 16:22:17 +0200 Subject: [PATCH 12/15] Update changelog --- ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog b/ChangeLog index 13d60eb3..8df299a3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,7 @@ CHANGES ======= * Spike count plot in 'DynapcnnVisualizer' is optional +* `DynapcnnVisualizer` allows custom JIT filters to make readout predictions v2.0.0 ------ From f415c8fe3be287183d75f8f83fa826228964f504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dylan=20Perdig=C3=A3o?= <32035460+dylanperdigao@users.noreply.github.com> Date: Thu, 2 Jan 2025 17:54:12 +0100 Subject: [PATCH 13/15] Fix typo covert -> convert --- docs/speck/notebooks/nmnist_quick_start.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/speck/notebooks/nmnist_quick_start.ipynb b/docs/speck/notebooks/nmnist_quick_start.ipynb index 61001a7a..29bcaf60 100644 --- a/docs/speck/notebooks/nmnist_quick_start.ipynb +++ b/docs/speck/notebooks/nmnist_quick_start.ipynb @@ -357,7 +357,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Covert CNN To SNN" + "### Convert CNN To SNN" ] }, { From c29c2448ecebe41d9744bddeca706f3f4e7559d4 Mon Sep 17 00:00:00 2001 From: Vanessa Leite Date: Thu, 23 Jan 2025 11:20:57 +0100 Subject: [PATCH 14/15] Fix bug where events do not have timestamps the forward method can receive other types of events as input and some of them might not have timestamps as attribute I do not see what was the problem that led to the code change added by commit f9f542b3a44611e8408b649020c62705794e2e4b I am removing it now and we can keep an eye to see if this is necessary and in that case, a new solution needs to be designed --- sinabs/backend/dynapcnn/dynapcnn_network.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sinabs/backend/dynapcnn/dynapcnn_network.py b/sinabs/backend/dynapcnn/dynapcnn_network.py index 4a1bb525..770abc42 100644 --- a/sinabs/backend/dynapcnn/dynapcnn_network.py +++ b/sinabs/backend/dynapcnn/dynapcnn_network.py @@ -436,10 +436,6 @@ def forward(self, x): # Send input self.samna_input_buffer.write(x) received_evts = [] - # Record at least until the last event has been replayed - min_duration = max(event.timestamp for event in x) * 1e-6 - time.sleep(min_duration) - # Keep recording if more events are being registered while True: prev_length = len(received_evts) time.sleep(0.1) From 6bf6e70bf81153d72dea3f8ddb5a55c7497e80db Mon Sep 17 00:00:00 2001 From: Vanessa Leite Date: Thu, 23 Jan 2025 16:29:41 +0100 Subject: [PATCH 15/15] Add a minimum sleep time This is necessary to meet the previous requirements (that I am still not sure what problem it is solving) --- sinabs/backend/dynapcnn/dynapcnn_network.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sinabs/backend/dynapcnn/dynapcnn_network.py b/sinabs/backend/dynapcnn/dynapcnn_network.py index 770abc42..007304bd 100644 --- a/sinabs/backend/dynapcnn/dynapcnn_network.py +++ b/sinabs/backend/dynapcnn/dynapcnn_network.py @@ -436,6 +436,11 @@ def forward(self, x): # Send input self.samna_input_buffer.write(x) received_evts = [] + + # Wait a minimum time to guarantee the events were played + time.sleep(1) + + # Keep recording if more events are being registered while True: prev_length = len(received_evts) time.sleep(0.1)