nirviz 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,20 @@
1
+ on:
2
+ release:
3
+ types: [published]
4
+
5
+ jobs:
6
+ pypi-publish:
7
+ name: Upload release to PyPI
8
+ runs-on: ubuntu-latest
9
+ environment:
10
+ name: pypi
11
+ url: https://pypi.org/p/nirviz
12
+ permissions:
13
+ id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
14
+ steps:
15
+ - name: Checkout repository
16
+ uses: actions/checkout@v2
17
+ - name: Build Python package
18
+ run: pip install build && python -m build
19
+ - name: Publish package distributions to PyPI
20
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,6 @@
1
+ /__pycache__/
2
+ /dist/
3
+ /nirviz.egg-info/
4
+ /.ipynb_checkpoints/
5
+ /build/
6
+ */__pycache__
nirviz-0.1.1/LICENSE ADDED
@@ -0,0 +1,27 @@
1
+ Copyright (c) 2025, open-neuromorphic/nirviz
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+
10
+ 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ this list of conditions and the following disclaimer in the documentation
12
+ and/or other materials provided with the distribution.
13
+
14
+ 3. Neither the name of the copyright holder nor the names of its
15
+ contributors may be used to endorse or promote products derived from
16
+ this software without specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
nirviz-0.1.1/PKG-INFO ADDED
@@ -0,0 +1,77 @@
1
+ Metadata-Version: 2.4
2
+ Name: nirviz
3
+ Version: 0.1.1
4
+ Summary: Visualisation tool for the Neuromorphic Intermediate Representation
5
+ Author-email: Jens Egholm Pedersen <jens@jepedersen.dk>, Michail Rontionov <mrontionov@mront.io>
6
+ License-Expression: BSD-3-Clause
7
+ Project-URL: homepage, https://github.com/open-neuromorphic/nirviz
8
+ Classifier: Development Status :: 1 - Planning
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
13
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
14
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
15
+ Classifier: Topic :: Scientific/Engineering :: Physics
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Requires-Python: >=3.9
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: nir
21
+ Requires-Dist: graphviz
22
+ Requires-Dist: cairosvg
23
+ Requires-Dist: pyyaml
24
+ Dynamic: license-file
25
+
26
+ # Neuromorphic Intermediate Representation Visualisation Tool
27
+
28
+ Turn your NIR definitions into a nice graph, the original publication serving as a template.
29
+
30
+ Customise your node colour preferences in [style.yml](./style.yml), and quickly generate graphs from your neuromorphic networks.
31
+
32
+ This work is in progress.
33
+
34
+ ## Running Example (Jupyter Notebook)
35
+ By running the following code (from a notebook),
36
+ ```python
37
+ import nir
38
+ import nirviz
39
+ import numpy as np
40
+
41
+
42
+ a = np.random.randn(2)
43
+ ir = nir.NIRGraph(
44
+ nodes={
45
+ "input": nir.Input(input_type=np.array([2])),
46
+ "affine1": nir.Affine(weight=np.zeros((2,2)), bias=False),
47
+ "cu1": nir.CubaLIF(tau_mem=a, tau_syn=a, r=a, v_leak=a, v_threshold=a, v_reset=a),
48
+ "affine_rec": nir.Affine(weight=np.zeros((2,2)), bias=False),
49
+ "affine2": nir.Affine(weight=np.zeros((2,2)), bias=False),
50
+ "cu2": nir.CubaLIF(tau_mem=a, tau_syn=a, r=a, v_leak=a, v_threshold=a, v_reset=a),
51
+ "output": nir.Output(output_type=np.array([2]))
52
+ },
53
+ edges=[("input", "affine1"), ("affine1", "cu1"), ("affine_rec", "cu1"), ("cu1", "affine_rec"), ("cu1", "affine2"), ("affine2", "cu2"), ("cu2", "output")])
54
+
55
+ viz = nirviz.visualize(ir)
56
+ viz.show()
57
+ ```
58
+
59
+ You would get the following visualisation
60
+
61
+ <picture>
62
+ <img alt="nirviz output" src="https://raw.githubusercontent.com/open-neuromorphic/nirviz/main/img/srnn.png">
63
+ </picture>
64
+
65
+ Similar to Figure 3 of the publication.
66
+
67
+ <picture>
68
+ <img alt="Figure 3 of NIR paper for comparison to output" src="https://raw.githubusercontent.com/open-neuromorphic/nirviz/main/img/fig3.png">
69
+ </picture>
70
+
71
+ ## Running example (CLI)
72
+ To convert a saved NIR graph (e.g. srnn.nir) to a PNG or SVG, you can use one of the following commands:
73
+ ```bash
74
+ python -m nirviz srnn.nir # SVG -> stdout
75
+ python -m nirviz srnn.nir img/srnn.png # PNG -> file
76
+ python -m nirviz srnn.nir img/srnn.svg # SVG -> file
77
+ ```
nirviz-0.1.1/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # Neuromorphic Intermediate Representation Visualisation Tool
2
+
3
+ Turn your NIR definitions into a nice graph, the original publication serving as a template.
4
+
5
+ Customise your node colour preferences in [style.yml](./style.yml), and quickly generate graphs from your neuromorphic networks.
6
+
7
+ This work is in progress.
8
+
9
+ ## Running Example (Jupyter Notebook)
10
+ By running the following code (from a notebook),
11
+ ```python
12
+ import nir
13
+ import nirviz
14
+ import numpy as np
15
+
16
+
17
+ a = np.random.randn(2)
18
+ ir = nir.NIRGraph(
19
+ nodes={
20
+ "input": nir.Input(input_type=np.array([2])),
21
+ "affine1": nir.Affine(weight=np.zeros((2,2)), bias=False),
22
+ "cu1": nir.CubaLIF(tau_mem=a, tau_syn=a, r=a, v_leak=a, v_threshold=a, v_reset=a),
23
+ "affine_rec": nir.Affine(weight=np.zeros((2,2)), bias=False),
24
+ "affine2": nir.Affine(weight=np.zeros((2,2)), bias=False),
25
+ "cu2": nir.CubaLIF(tau_mem=a, tau_syn=a, r=a, v_leak=a, v_threshold=a, v_reset=a),
26
+ "output": nir.Output(output_type=np.array([2]))
27
+ },
28
+ edges=[("input", "affine1"), ("affine1", "cu1"), ("affine_rec", "cu1"), ("cu1", "affine_rec"), ("cu1", "affine2"), ("affine2", "cu2"), ("cu2", "output")])
29
+
30
+ viz = nirviz.visualize(ir)
31
+ viz.show()
32
+ ```
33
+
34
+ You would get the following visualisation
35
+
36
+ <picture>
37
+ <img alt="nirviz output" src="https://raw.githubusercontent.com/open-neuromorphic/nirviz/main/img/srnn.png">
38
+ </picture>
39
+
40
+ Similar to Figure 3 of the publication.
41
+
42
+ <picture>
43
+ <img alt="Figure 3 of NIR paper for comparison to output" src="https://raw.githubusercontent.com/open-neuromorphic/nirviz/main/img/fig3.png">
44
+ </picture>
45
+
46
+ ## Running example (CLI)
47
+ To convert a saved NIR graph (e.g. srnn.nir) to a PNG or SVG, you can use one of the following commands:
48
+ ```bash
49
+ python -m nirviz srnn.nir # SVG -> stdout
50
+ python -m nirviz srnn.nir img/srnn.png # PNG -> file
51
+ python -m nirviz srnn.nir img/srnn.svg # SVG -> file
52
+ ```
@@ -0,0 +1,295 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 1,
6
+ "id": "e6489ed2",
7
+ "metadata": {
8
+ "collapsed": false
9
+ },
10
+ "outputs": [
11
+ {
12
+ "data": {
13
+ "image/svg+xml": [
14
+ "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"362pt\" height=\"44pt\" viewBox=\"0.00 0.00 362.00 44.00\">\n",
15
+ "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 40)\">\n",
16
+ "<title>%3</title>\n",
17
+ "<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-40 358,-40 358,4 -4,4\"/>\n",
18
+ "<!-- input -->\n",
19
+ "<g id=\"node1\" class=\"node\">\n",
20
+ "<title>input</title>\n",
21
+ "<polygon fill=\"none\" stroke=\"black\" stroke-dasharray=\"5,2\" points=\"55,-36 0,-36 0,0 55,0 55,-36\"/>\n",
22
+ "<text text-anchor=\"middle\" x=\"27.5\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">Input</text>\n",
23
+ "</g>\n",
24
+ "<!-- linear -->\n",
25
+ "<g id=\"node2\" class=\"node\">\n",
26
+ "<title>linear</title>\n",
27
+ "<polygon fill=\"#71c8b0\" stroke=\"#71c8b0\" points=\"153,-36 91,-36 91,0 153,0 153,-36\"/>\n",
28
+ "<text text-anchor=\"middle\" x=\"122\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">Linear</text>\n",
29
+ "</g>\n",
30
+ "<!-- input&#45;&gt;linear -->\n",
31
+ "<g id=\"edge1\" class=\"edge\">\n",
32
+ "<title>input-&gt;linear</title>\n",
33
+ "<path fill=\"none\" stroke=\"black\" d=\"M55.25,-18C63.14,-18 71.96,-18 80.51,-18\"/>\n",
34
+ "<polygon fill=\"black\" stroke=\"black\" points=\"80.73,-21.5 90.73,-18 80.73,-14.5 80.73,-21.5\"/>\n",
35
+ "</g>\n",
36
+ "<!-- linear_1 -->\n",
37
+ "<g id=\"node3\" class=\"node\">\n",
38
+ "<title>linear_1</title>\n",
39
+ "<polygon fill=\"#71c8b0\" stroke=\"#71c8b0\" points=\"251,-36 189,-36 189,0 251,0 251,-36\"/>\n",
40
+ "<text text-anchor=\"middle\" x=\"220\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">Linear</text>\n",
41
+ "</g>\n",
42
+ "<!-- linear&#45;&gt;linear_1 -->\n",
43
+ "<g id=\"edge2\" class=\"edge\">\n",
44
+ "<title>linear-&gt;linear_1</title>\n",
45
+ "<path fill=\"none\" stroke=\"black\" d=\"M153.11,-18C161.24,-18 170.17,-18 178.76,-18\"/>\n",
46
+ "<polygon fill=\"black\" stroke=\"black\" points=\"179,-21.5 189,-18 179,-14.5 179,-21.5\"/>\n",
47
+ "</g>\n",
48
+ "<!-- output -->\n",
49
+ "<g id=\"node4\" class=\"node\">\n",
50
+ "<title>output</title>\n",
51
+ "<polygon fill=\"none\" stroke=\"black\" stroke-dasharray=\"5,2\" points=\"354,-36 287,-36 287,0 354,0 354,-36\"/>\n",
52
+ "<text text-anchor=\"middle\" x=\"320.5\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">Output</text>\n",
53
+ "</g>\n",
54
+ "<!-- linear_1&#45;&gt;output -->\n",
55
+ "<g id=\"edge3\" class=\"edge\">\n",
56
+ "<title>linear_1-&gt;output</title>\n",
57
+ "<path fill=\"none\" stroke=\"black\" d=\"M251.09,-18C259.13,-18 267.99,-18 276.58,-18\"/>\n",
58
+ "<polygon fill=\"black\" stroke=\"black\" points=\"276.85,-21.5 286.85,-18 276.85,-14.5 276.85,-21.5\"/>\n",
59
+ "</g>\n",
60
+ "</g>\n",
61
+ "</svg>"
62
+ ],
63
+ "text/plain": [
64
+ "<IPython.core.display.SVG object>"
65
+ ]
66
+ },
67
+ "metadata": {},
68
+ "output_type": "display_data"
69
+ }
70
+ ],
71
+ "source": [
72
+ "import nir\n",
73
+ "import nirviz\n",
74
+ "import importlib\n",
75
+ "importlib.reload(nirviz)\n",
76
+ "import numpy as np\n",
77
+ "\n",
78
+ "w = np.random.randn(2, 2)\n",
79
+ "graph = nir.NIRGraph.from_list(nir.Linear(weight=w), nir.Linear(weight=w))\n",
80
+ "viz = nirviz.visualize(graph)\n",
81
+ "viz.show()"
82
+ ]
83
+ },
84
+ {
85
+ "cell_type": "code",
86
+ "execution_count": 15,
87
+ "id": "dc7287c0-888d-4d1a-a149-a0620866fd1b",
88
+ "metadata": {
89
+ "collapsed": false
90
+ },
91
+ "outputs": [
92
+ {
93
+ "name": "stderr",
94
+ "output_type": "stream",
95
+ "text": [
96
+ "WARNING:root:removing self-connection ('0', '0')\n"
97
+ ]
98
+ }
99
+ ],
100
+ "source": [
101
+ "import snntorch as snn\n",
102
+ "import torch\n",
103
+ "\n",
104
+ "importlib.reload(snn)\n",
105
+ "importlib.reload(nirviz)\n",
106
+ "import numpy as np\n",
107
+ "\n",
108
+ "lif1 = snn.Leaky(beta=0.9, init_hidden=True)\n",
109
+ "lif2 = snn.Leaky(beta=0.9, init_hidden=True, output=True)\n",
110
+ "\n",
111
+ "# Create a network\n",
112
+ "snntorch_network = torch.nn.Sequential(\n",
113
+ " torch.nn.Flatten(),\n",
114
+ " #torch.nn.Linear(784, 500),\n",
115
+ " # lif1,\n",
116
+ " # torch.nn.Linear(500, 10),\n",
117
+ " # lif2\n",
118
+ ")\n",
119
+ "\n",
120
+ "sample_data = torch.randn(1, 784)\n",
121
+ "\n",
122
+ "# Export to nir\n",
123
+ "nir_model = snn.export_to_nir(snntorch_network, sample_data)\n",
124
+ "\n",
125
+ "# viz = nirviz.visualize(nir_model)\n",
126
+ "# viz.show()"
127
+ ]
128
+ },
129
+ {
130
+ "cell_type": "code",
131
+ "execution_count": 18,
132
+ "id": "bef33566-65eb-4c5f-b227-c65f5aec2224",
133
+ "metadata": {
134
+ "collapsed": false
135
+ },
136
+ "outputs": [
137
+ {
138
+ "data": {
139
+ "image/svg+xml": [
140
+ "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"580pt\" height=\"98pt\" viewBox=\"0.00 0.00 580.00 98.00\">\n",
141
+ "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 94)\">\n",
142
+ "<title>%3</title>\n",
143
+ "<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-94 576,-94 576,4 -4,4\"/>\n",
144
+ "<!-- input -->\n",
145
+ "<g id=\"node1\" class=\"node\">\n",
146
+ "<title>input</title>\n",
147
+ "<polygon fill=\"none\" stroke=\"black\" stroke-dasharray=\"5,2\" points=\"55,-90 0,-90 0,-54 55,-54 55,-90\"/>\n",
148
+ "<text text-anchor=\"middle\" x=\"27.5\" y=\"-68.3\" font-family=\"Times,serif\" font-size=\"14.00\">Input</text>\n",
149
+ "</g>\n",
150
+ "<!-- affine1 -->\n",
151
+ "<g id=\"node2\" class=\"node\">\n",
152
+ "<title>affine1</title>\n",
153
+ "<polygon fill=\"#71c8b0\" stroke=\"#71c8b0\" points=\"149,-90 91,-90 91,-54 149,-54 149,-90\"/>\n",
154
+ "<text text-anchor=\"middle\" x=\"120\" y=\"-68.3\" font-family=\"Times,serif\" font-size=\"14.00\">Affine</text>\n",
155
+ "</g>\n",
156
+ "<!-- input&#45;&gt;affine1 -->\n",
157
+ "<g id=\"edge1\" class=\"edge\">\n",
158
+ "<title>input-&gt;affine1</title>\n",
159
+ "<path fill=\"none\" stroke=\"black\" d=\"M55.16,-72C63.17,-72 72.12,-72 80.72,-72\"/>\n",
160
+ "<polygon fill=\"black\" stroke=\"black\" points=\"80.95,-75.5 90.95,-72 80.95,-68.5 80.95,-75.5\"/>\n",
161
+ "</g>\n",
162
+ "<!-- cu1 -->\n",
163
+ "<g id=\"node3\" class=\"node\">\n",
164
+ "<title>cu1</title>\n",
165
+ "<polygon fill=\"#a0d5f1\" stroke=\"#a0d5f1\" points=\"262,-90 185,-90 185,-54 262,-54 262,-90\"/>\n",
166
+ "<text text-anchor=\"middle\" x=\"223.5\" y=\"-68.3\" font-family=\"Times,serif\" font-size=\"14.00\">CubaLIF</text>\n",
167
+ "</g>\n",
168
+ "<!-- affine1&#45;&gt;cu1 -->\n",
169
+ "<g id=\"edge2\" class=\"edge\">\n",
170
+ "<title>affine1-&gt;cu1</title>\n",
171
+ "<path fill=\"none\" stroke=\"black\" d=\"M149,-72C156.93,-72 165.8,-72 174.55,-72\"/>\n",
172
+ "<polygon fill=\"black\" stroke=\"black\" points=\"174.71,-75.5 184.71,-72 174.71,-68.5 174.71,-75.5\"/>\n",
173
+ "</g>\n",
174
+ "<!-- affine_rec -->\n",
175
+ "<g id=\"node4\" class=\"node\">\n",
176
+ "<title>affine_rec</title>\n",
177
+ "<polygon fill=\"#71c8b0\" stroke=\"#71c8b0\" points=\"356,-90 298,-90 298,-54 356,-54 356,-90\"/>\n",
178
+ "<text text-anchor=\"middle\" x=\"327\" y=\"-68.3\" font-family=\"Times,serif\" font-size=\"14.00\">Affine</text>\n",
179
+ "</g>\n",
180
+ "<!-- cu1&#45;&gt;affine_rec -->\n",
181
+ "<g id=\"edge4\" class=\"edge\">\n",
182
+ "<title>cu1-&gt;affine_rec</title>\n",
183
+ "<path fill=\"none\" stroke=\"black\" d=\"M262.01,-65.46C270.44,-65.19 279.39,-65.18 287.85,-65.44\"/>\n",
184
+ "<polygon fill=\"black\" stroke=\"black\" points=\"287.7,-68.94 297.85,-65.88 288.02,-61.94 287.7,-68.94\"/>\n",
185
+ "</g>\n",
186
+ "<!-- affine2 -->\n",
187
+ "<g id=\"node5\" class=\"node\">\n",
188
+ "<title>affine2</title>\n",
189
+ "<polygon fill=\"#71c8b0\" stroke=\"#71c8b0\" points=\"356,-36 298,-36 298,0 356,0 356,-36\"/>\n",
190
+ "<text text-anchor=\"middle\" x=\"327\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">Affine</text>\n",
191
+ "</g>\n",
192
+ "<!-- cu1&#45;&gt;affine2 -->\n",
193
+ "<g id=\"edge5\" class=\"edge\">\n",
194
+ "<title>cu1-&gt;affine2</title>\n",
195
+ "<path fill=\"none\" stroke=\"black\" d=\"M258.58,-53.87C268.37,-48.66 279.09,-42.96 289.04,-37.66\"/>\n",
196
+ "<polygon fill=\"black\" stroke=\"black\" points=\"290.73,-40.73 297.92,-32.94 287.45,-34.55 290.73,-40.73\"/>\n",
197
+ "</g>\n",
198
+ "<!-- affine_rec&#45;&gt;cu1 -->\n",
199
+ "<g id=\"edge3\" class=\"edge\">\n",
200
+ "<title>affine_rec-&gt;cu1</title>\n",
201
+ "<path fill=\"none\" stroke=\"black\" d=\"M297.85,-78.12C289.88,-78.59 280.98,-78.8 272.2,-78.74\"/>\n",
202
+ "<polygon fill=\"black\" stroke=\"black\" points=\"272.07,-75.24 262.01,-78.54 271.94,-82.23 272.07,-75.24\"/>\n",
203
+ "</g>\n",
204
+ "<!-- cu2 -->\n",
205
+ "<g id=\"node6\" class=\"node\">\n",
206
+ "<title>cu2</title>\n",
207
+ "<polygon fill=\"#a0d5f1\" stroke=\"#a0d5f1\" points=\"469,-36 392,-36 392,0 469,0 469,-36\"/>\n",
208
+ "<text text-anchor=\"middle\" x=\"430.5\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">CubaLIF</text>\n",
209
+ "</g>\n",
210
+ "<!-- affine2&#45;&gt;cu2 -->\n",
211
+ "<g id=\"edge6\" class=\"edge\">\n",
212
+ "<title>affine2-&gt;cu2</title>\n",
213
+ "<path fill=\"none\" stroke=\"black\" d=\"M356,-18C363.93,-18 372.8,-18 381.55,-18\"/>\n",
214
+ "<polygon fill=\"black\" stroke=\"black\" points=\"381.71,-21.5 391.71,-18 381.71,-14.5 381.71,-21.5\"/>\n",
215
+ "</g>\n",
216
+ "<!-- output -->\n",
217
+ "<g id=\"node7\" class=\"node\">\n",
218
+ "<title>output</title>\n",
219
+ "<polygon fill=\"none\" stroke=\"black\" stroke-dasharray=\"5,2\" points=\"572,-36 505,-36 505,0 572,0 572,-36\"/>\n",
220
+ "<text text-anchor=\"middle\" x=\"538.5\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">Output</text>\n",
221
+ "</g>\n",
222
+ "<!-- cu2&#45;&gt;output -->\n",
223
+ "<g id=\"edge7\" class=\"edge\">\n",
224
+ "<title>cu2-&gt;output</title>\n",
225
+ "<path fill=\"none\" stroke=\"black\" d=\"M469.17,-18C477.46,-18 486.3,-18 494.78,-18\"/>\n",
226
+ "<polygon fill=\"black\" stroke=\"black\" points=\"494.87,-21.5 504.87,-18 494.87,-14.5 494.87,-21.5\"/>\n",
227
+ "</g>\n",
228
+ "</g>\n",
229
+ "</svg>"
230
+ ],
231
+ "text/plain": [
232
+ "<IPython.core.display.SVG object>"
233
+ ]
234
+ },
235
+ "metadata": {},
236
+ "output_type": "display_data"
237
+ }
238
+ ],
239
+ "source": [
240
+ "a = np.random.randn(2)\n",
241
+ "ir = nir.NIRGraph(\n",
242
+ " nodes={\n",
243
+ " \"input\": nir.Input(input_type=np.array([2])),\n",
244
+ " \"affine1\": nir.Affine(weight=np.zeros((2,2)), bias=False),\n",
245
+ " \"cu1\": nir.CubaLIF(tau_mem=a, tau_syn=a, r=a, v_leak=a, v_threshold=a, v_reset=a),\n",
246
+ " \"affine_rec\": nir.Affine(weight=np.zeros((2,2)), bias=False),\n",
247
+ " \"affine2\": nir.Affine(weight=np.zeros((2,2)), bias=False),\n",
248
+ " \"cu2\": nir.CubaLIF(tau_mem=a, tau_syn=a, r=a, v_leak=a, v_threshold=a, v_reset=a),\n",
249
+ " \"output\": nir.Output(output_type=np.array([2]))\n",
250
+ " },\n",
251
+ " edges=[(\"input\", \"affine1\"), (\"affine1\", \"cu1\"), (\"affine_rec\", \"cu1\"), (\"cu1\", \"affine_rec\"), (\"cu1\", \"affine2\"), (\"affine2\", \"cu2\"), (\"cu2\", \"output\")])\n",
252
+ "\n",
253
+ "nir.write(\"./srnn.nir\", ir)\n",
254
+ "\n",
255
+ "viz = nirviz.visualize(ir)\n",
256
+ "viz.show()\n",
257
+ "viz.to_image().save(\"./img/srnn.png\")"
258
+ ]
259
+ }
260
+ ],
261
+ "metadata": {
262
+ "kernelspec": {
263
+ "argv": [
264
+ "python",
265
+ "-m",
266
+ "ipykernel_launcher",
267
+ "-f",
268
+ "{connection_file}"
269
+ ],
270
+ "display_name": "Python 3 (ipykernel)",
271
+ "env": null,
272
+ "interrupt_mode": "signal",
273
+ "language": "python",
274
+ "metadata": {
275
+ "debugger": true
276
+ },
277
+ "name": "python3"
278
+ },
279
+ "language_info": {
280
+ "codemirror_mode": {
281
+ "name": "ipython",
282
+ "version": 3
283
+ },
284
+ "file_extension": ".py",
285
+ "mimetype": "text/x-python",
286
+ "name": "python",
287
+ "nbconvert_exporter": "python",
288
+ "pygments_lexer": "ipython3",
289
+ "version": "3.12.3"
290
+ },
291
+ "name": "example.ipynb"
292
+ },
293
+ "nbformat": 4,
294
+ "nbformat_minor": 5
295
+ }
Binary file
Binary file
@@ -0,0 +1,13 @@
1
+ from importlib.metadata import version as metadata_version, PackageNotFoundError
2
+ from setuptools_scm.version import ScmVersion
3
+
4
+ # Exposing imports
5
+ from .nirviz import visualize
6
+
7
+ try:
8
+ # For pyproject.toml dynamic versioning
9
+ __version__ = version = metadata_version("nirviz")
10
+ del metadata_version
11
+ except PackageNotFoundError:
12
+ print('nirviz: package not found')
13
+ pass
@@ -0,0 +1,31 @@
1
+ import argparse
2
+ import sys
3
+
4
+ import nirviz
5
+
6
+ if __name__ == "__main__":
7
+ parser = argparse.ArgumentParser(description="CLI tool for visualizing NIR graphs.")
8
+ parser.add_argument("file", type=argparse.FileType('r'), help="The NIR graph file to read from.")
9
+ parser.add_argument("output", type=str, default="stdout", nargs='?', help="The output file to write the graph to. Defaults to stdout.")
10
+ parser.add_argument("yaml", type=argparse.FileType('r'), default="./style.yml", nargs='?', help="Style file defined in yaml. Defaults to ./style.yml.")
11
+ args = parser.parse_args()
12
+
13
+ nir_file = args.file.name
14
+ yaml_file = args.yaml.name
15
+ # Load the NIR graph
16
+ if args.output == "stdout":
17
+ graph = nirviz.visualize(nir_file, style_file=yaml_file)
18
+ graph_svg = str(graph)
19
+ sys.stdout.write(graph_svg)
20
+ elif args.output.endswith(".svg"):
21
+ graph = nirviz.visualize(nir_file, style_file=yaml_file)
22
+ graph_svg = str(graph)
23
+ with open(args.output, "w") as f:
24
+ f.write(graph_svg)
25
+ elif args.output.endswith(".png"):
26
+ graph = nirviz.visualize(nir_file, style_file=yaml_file)
27
+ graph_image = graph.to_image()
28
+ graph_image.save(args.output)
29
+
30
+ else:
31
+ raise ValueError("Output file must be either .svg or .png format.")
@@ -0,0 +1,72 @@
1
+ from dataclasses import dataclass, field
2
+ import yaml
3
+ import re
4
+ import graphviz
5
+ import cairosvg
6
+ import nir
7
+ import typing
8
+ import importlib.util
9
+ import pathlib
10
+ import PIL
11
+ import io
12
+
13
+
14
+ @dataclass
15
+ class visualize:
16
+ nir_graph: typing.Union[nir.NIRGraph, str, pathlib.Path]
17
+ style_file: pathlib.Path = field(default_factory=lambda: pathlib.Path("./style.yml"))
18
+
19
+ def __post_init__(self):
20
+ if isinstance(self.nir_graph, (str, pathlib.Path)):
21
+ self.nir_graph = nir.read(self.nir_graph)
22
+
23
+ self.style_dict = self.__load_style_file()
24
+ self.viz_graph = self.__construct_graph()
25
+
26
+ def __load_style_file(self) -> typing.Dict[str, typing.Dict[str, str]]:
27
+ with open(self.style_file, 'r') as f:
28
+ config = yaml.safe_load(f)
29
+ return config
30
+
31
+ def __pick_style(self, name: str) -> typing.Dict[str, str]:
32
+ categories = self.style_dict['node-categories']
33
+ for cat_id in categories:
34
+ cat = categories[cat_id]
35
+ if name in cat['patterns']:
36
+ return cat['style']
37
+
38
+ return self.style_dict['defaults']['node']['style']
39
+
40
+
41
+ def __construct_graph(self) -> graphviz.Digraph:
42
+ viz_graph = graphviz.Digraph(format="svg",
43
+ graph_attr={'rankdir': 'LR'})
44
+ # Generate nodes
45
+ for node_id in self.nir_graph.nodes:
46
+ name = type(self.nir_graph.nodes[node_id]).__name__
47
+ style = self.__pick_style(name)
48
+ viz_graph.node(node_id, label=name, **style)
49
+
50
+ # Generate edges
51
+ for src_id, tgt_id in self.nir_graph.edges:
52
+ viz_graph.edge(src_id, tgt_id)
53
+
54
+ return viz_graph
55
+
56
+ def show(self) -> None:
57
+ if importlib.util.find_spec("IPython"):
58
+ import IPython
59
+
60
+ svg_output = self.viz_graph.pipe(format="svg")
61
+ image = IPython.display.SVG(svg_output)
62
+ IPython.display.display(image)
63
+ else:
64
+ print("error: cannot display graph: no IPython environment detected.")
65
+
66
+ def to_image(self) -> PIL.Image.Image:
67
+ svg_output = self.viz_graph.pipe(format="svg")
68
+ png_bytes = cairosvg.svg2png(bytestring=svg_output)
69
+ return PIL.Image.open(io.BytesIO(png_bytes))
70
+
71
+ def __repr__(self) -> str:
72
+ return self.viz_graph.pipe(format="svg", encoding="utf-8")
@@ -0,0 +1,77 @@
1
+ Metadata-Version: 2.4
2
+ Name: nirviz
3
+ Version: 0.1.1
4
+ Summary: Visualisation tool for the Neuromorphic Intermediate Representation
5
+ Author-email: Jens Egholm Pedersen <jens@jepedersen.dk>, Michail Rontionov <mrontionov@mront.io>
6
+ License-Expression: BSD-3-Clause
7
+ Project-URL: homepage, https://github.com/open-neuromorphic/nirviz
8
+ Classifier: Development Status :: 1 - Planning
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
13
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
14
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
15
+ Classifier: Topic :: Scientific/Engineering :: Physics
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Requires-Python: >=3.9
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: nir
21
+ Requires-Dist: graphviz
22
+ Requires-Dist: cairosvg
23
+ Requires-Dist: pyyaml
24
+ Dynamic: license-file
25
+
26
+ # Neuromorphic Intermediate Representation Visualisation Tool
27
+
28
+ Turn your NIR definitions into a nice graph, the original publication serving as a template.
29
+
30
+ Customise your node colour preferences in [style.yml](./style.yml), and quickly generate graphs from your neuromorphic networks.
31
+
32
+ This work is in progress.
33
+
34
+ ## Running Example (Jupyter Notebook)
35
+ By running the following code (from a notebook),
36
+ ```python
37
+ import nir
38
+ import nirviz
39
+ import numpy as np
40
+
41
+
42
+ a = np.random.randn(2)
43
+ ir = nir.NIRGraph(
44
+ nodes={
45
+ "input": nir.Input(input_type=np.array([2])),
46
+ "affine1": nir.Affine(weight=np.zeros((2,2)), bias=False),
47
+ "cu1": nir.CubaLIF(tau_mem=a, tau_syn=a, r=a, v_leak=a, v_threshold=a, v_reset=a),
48
+ "affine_rec": nir.Affine(weight=np.zeros((2,2)), bias=False),
49
+ "affine2": nir.Affine(weight=np.zeros((2,2)), bias=False),
50
+ "cu2": nir.CubaLIF(tau_mem=a, tau_syn=a, r=a, v_leak=a, v_threshold=a, v_reset=a),
51
+ "output": nir.Output(output_type=np.array([2]))
52
+ },
53
+ edges=[("input", "affine1"), ("affine1", "cu1"), ("affine_rec", "cu1"), ("cu1", "affine_rec"), ("cu1", "affine2"), ("affine2", "cu2"), ("cu2", "output")])
54
+
55
+ viz = nirviz.visualize(ir)
56
+ viz.show()
57
+ ```
58
+
59
+ You would get the following visualisation
60
+
61
+ <picture>
62
+ <img alt="nirviz output" src="https://raw.githubusercontent.com/open-neuromorphic/nirviz/main/img/srnn.png">
63
+ </picture>
64
+
65
+ Similar to Figure 3 of the publication.
66
+
67
+ <picture>
68
+ <img alt="Figure 3 of NIR paper for comparison to output" src="https://raw.githubusercontent.com/open-neuromorphic/nirviz/main/img/fig3.png">
69
+ </picture>
70
+
71
+ ## Running example (CLI)
72
+ To convert a saved NIR graph (e.g. srnn.nir) to a PNG or SVG, you can use one of the following commands:
73
+ ```bash
74
+ python -m nirviz srnn.nir # SVG -> stdout
75
+ python -m nirviz srnn.nir img/srnn.png # PNG -> file
76
+ python -m nirviz srnn.nir img/srnn.svg # SVG -> file
77
+ ```
@@ -0,0 +1,19 @@
1
+ .gitignore
2
+ LICENSE
3
+ README.md
4
+ example.ipynb
5
+ pyproject.toml
6
+ pyvenv.cfg
7
+ srnn.nir
8
+ style.yml
9
+ .github/workflows/pypi.yml
10
+ img/fig3.png
11
+ img/srnn.png
12
+ nirviz/__init__.py
13
+ nirviz/__main__.py
14
+ nirviz/nirviz.py
15
+ nirviz.egg-info/PKG-INFO
16
+ nirviz.egg-info/SOURCES.txt
17
+ nirviz.egg-info/dependency_links.txt
18
+ nirviz.egg-info/requires.txt
19
+ nirviz.egg-info/top_level.txt
@@ -0,0 +1,4 @@
1
+ nir
2
+ graphviz
3
+ cairosvg
4
+ pyyaml
@@ -0,0 +1 @@
1
+ nirviz
@@ -0,0 +1,38 @@
1
+ [build-system]
2
+ requires = ["setuptools>=42", "setuptools_scm"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "nirviz"
7
+ dynamic = ["version"]
8
+ description = "Visualisation tool for the Neuromorphic Intermediate Representation"
9
+ license-files = ["LICENSE"]
10
+ license = "BSD-3-Clause"
11
+ readme = "README.md"
12
+ authors = [
13
+ { name = "Jens Egholm Pedersen", email = "jens@jepedersen.dk" },
14
+ { name = "Michail Rontionov", email = "mrontionov@mront.io" }
15
+ ]
16
+ classifiers = [
17
+ "Development Status :: 1 - Planning",
18
+ "Intended Audience :: Developers",
19
+ "Intended Audience :: Science/Research",
20
+ "Programming Language :: Python :: 3",
21
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
22
+ "Topic :: Scientific/Engineering :: Information Analysis",
23
+ "Topic :: Scientific/Engineering :: Mathematics",
24
+ "Topic :: Scientific/Engineering :: Physics",
25
+ "Topic :: Software Development :: Libraries :: Python Modules",
26
+ ]
27
+ dependencies = ["nir", "graphviz", "cairosvg", "pyyaml"]
28
+ requires-python = ">=3.9"
29
+
30
+ [project.urls]
31
+ homepage = "https://github.com/open-neuromorphic/nirviz"
32
+
33
+ [tool.setuptools]
34
+ packages = ["nirviz"]
35
+
36
+ [tool.setuptools_scm]
37
+ version_scheme = "only-version"
38
+ local_scheme = "no-local-version"
@@ -0,0 +1,5 @@
1
+ home = /usr/bin
2
+ include-system-site-packages = false
3
+ version = 3.12.3
4
+ executable = /usr/bin/python3.12
5
+ command = /usr/bin/python3 -m venv /home/mrontio/uni/phd/src/nir-viz
nirviz-0.1.1/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
nirviz-0.1.1/srnn.nir ADDED
Binary file
nirviz-0.1.1/style.yml ADDED
@@ -0,0 +1,34 @@
1
+ node-categories:
2
+ input-output:
3
+ patterns: ["Input", "Output"]
4
+ style:
5
+ color: "black"
6
+ style: "dashed"
7
+ shape: "box"
8
+
9
+ connection:
10
+ patterns: ["Affine", "Linear"]
11
+ style:
12
+ color: "#71c8b0"
13
+ style: "filled"
14
+ shape: "box"
15
+
16
+ transform:
17
+ patterns: ["Flatten"]
18
+ style:
19
+ color: "#e4b9dd"
20
+ style: "filled"
21
+ shape: "box"
22
+
23
+ computation:
24
+ patterns: ["LIF", "CubaLIF"]
25
+ style:
26
+ color: "#a0d5f1"
27
+ style: "filled"
28
+ shape: "box"
29
+
30
+ defaults:
31
+ node:
32
+ style:
33
+ color: "black"
34
+ style: "dashed"