# badcrossbar **Repository Path**: hfutlc/badcrossbar ## Basic Information - **Project Name**: badcrossbar - **Description**: A Python tool for computing and plotting currents and voltages in passive crossbar arrays. - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-03-29 - **Last Updated**: 2021-03-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Table of Contents - [About](#about) - [Background](#background) - [Installation](#installation) - [Usage](#usage) - [Computing](#computing) - [Currents](#currents) - [Voltages](#voltages) - [Plotting](#plotting) - [Currents](#currents-1) - [Voltages](#voltages-1) # About [badcrossbar] is a nodal analysis solver for passive crossbar arrays that suffer from line resistance. It solves for currents in all the branches, as well as for voltages at all the nodes of the crossbar. Additionally, it can create diagrams of crossbar arrays colored according to the values of those currents and voltages (or any other variables). This package has been submitted to [SoftwareX journal](https://www.journals.elsevier.com/softwarex) as Original Software Publication. # Background Conventional crossbars have a structure like the one shown in the image below. Resistive two-terminal **devices** (depicted here as memristors) are connected to **word lines** on one side and to **bit lines** on the other. In the diagram, the crossbar devices are depicted in black, the word lines are depicted as horizontal blue lines and the bit lines as vertical blue lines. Orange circles denote the **nodes**, i.e. the connections between the devices and the word/bit lines. The segments of word and bit lines between neighbouring nodes are often seen as **interconnects** between neighbouring devices and can themselves be modelled as resistive elements. ![Crossbar structure](images/crossbar-structure.png) In most practical scenarios, we want the resistance of the interconnects to be zero. That is because crossbar arrays containing resistive elements can be used as dot product engines, i.e. systems able to compute vector-matrix products in hardware. Specifically, crossbar arrays with interconnect resistance of zero are able to compute the vector-matrix products of applied voltages (vector) and conductances of the crossbar devices (matrix). In the diagram above, voltages are applied on the left side of the word lines and the vector-matrix product is computed in a form of currents at the bottom of the bit lines. # Installation ## Pycairo The plotting sub-package of [badcrossbar] uses [pycairo] package which requires [cairo](https://www.cairographics.org/) and [pkg-config](https://github.com/pkgconf/pkgconf). The instructions for how to install either these dependencies or [pycairo] directly can be found in this subsection. However, if you are not interested in the plotting functions provided by [badcrossbar], computing sub-package works even without [pycairo] installed, thus you can skip to [badcrossbar installation](#badcrossbar). ### Linux As described [here](https://pycairo.readthedocs.io/en/latest/getting_started.html), it is straightforward to install [pycairo] dependencies for most Linux distributions. ##### Ubuntu/Debian ```text apt install libcairo2-dev pkg-config python3-dev ``` ##### Arch Linux ```text pacman -S cairo pkgconf ``` ##### Fedora ```text dnf install cairo-devel pkg-config python3-devel ``` ##### openSUSE ```text zypper install cairo-devel pkg-config python3-devel ``` ### macOS Similarly for macOS (using [Homebrew package manager](https://brew.sh)): ```text brew install cairo pkg-config ``` ### Windows Getting [pycairo] to work on Windows might prove to be a bit more challenging. One suggested solution is to download unofficial [pycairo] binary from [University of California, Irvine website](https://www.lfd.uci.edu/~gohlke/pythonlibs/#pycairo) and install it by typing the following command into the terminal (with the correct filename): ```text pip3 install C:\path\to\file\pycairo-1.19.1-cp38-cp38-win32.whl ``` As stated in the [website](https://www.lfd.uci.edu/~gohlke/pythonlibs/), "the files are provided "as is" without warranty or support of any kind. The entire risk as to the quality and performance is with you." *Note*: you might be able to use `pip`, instead of `pip3`, in your terminal as long as it is an alias for the package management system pip of Python 3 (and not Python 2). ## badcrossbar To install the [badcrossbar] package and its dependencies, type the following commands into the terminal: ```text git clone https://github.com/joksas/badcrossbar cd badcrossbar python3 setup.py install ``` If [pycairo] system requirements are not satisfied, command `python3 setup.py install` will fail to install *all* the dependencies. If you are not planning to install [pycairo], please remove the line containing it from [requirements.txt](requirements.txt) before running `python3 setup.py install`. *Note*: you might be able to use `python`, instead of `python3`, in your terminal as long as it is an alias for Python 3 (and not Python 2). # Usage ## Computing [badcrossbar] allows to compute branch currents and node voltages for arbitrary values of applied voltages, devices' resistances and interconnect resistances. It assumes that voltages are applied on the left side of the word lines and the outputs (in the bottom of the bit lines) are grounded. One can define either a single resistance value for all interconnects, or two separate values for word and bit line segments using optional parameters `r_i_word_line` and `r_i_bit_line`, respectively. One can compute branch currents and node voltages with the function `badcrossbar.compute`, as shown in the code below: ```python import badcrossbar # Applied voltages in volts. applied_voltages = [[1.5], [2.3], [1.7]] # Device resistances in ohms. resistances = [[345, 903, 755, 257, 646], [652, 401, 508, 166, 454], [442, 874, 190, 244, 635]] # Interconnect resistance in ohms. r_i = 0.5 # Computing the solution. solution = badcrossbar.compute(applied_voltages, resistances, r_i) ``` The returned variable (named `solution`, in this case) is a named tuple with fields `currents` and `voltages`. ### Currents `solution.currents` is itself a named tuple with fields `output`, `device`, `word_line` and `bit_line`. The first field represents the output currents, while the rest represent the currents flowing through devices, interconnects along the word lines, and interconnects along the bit lines. All of these branches are depicted in the diagram below: ![Crossbar branches](images/crossbar-branches.png) If `applied_voltages` is an array of shape `(m, p)` (each column representing a different set of inputs) and `resistances` is an array of shape `(m, n)`, then: * `currents.output` will be a [numpy] array of shape `(p, n)`. * `currents.device`, `currents.word_line` and `currents.bit_line` will be [numpy] arrays of shape `(m, n)` if `p = 1`, or [numpy] arrays of shape `(m, n, p)` if `p > 1`. ### Voltages `solution.voltages` is itself a named tuple with fields `word_line` and `bit_line`. They represent the voltages at the nodes on the word and bit lines, respectively. These two types of nodes are depicted in the diagram below: ![Crossbar nodes](images/crossbar-nodes.png) If `applied_voltages` is an array of shape `(m, p)` (each column representing a different set of inputs) and `resistances` is an array of shape `(m, n)`, then `voltages.word_line` and `voltages.bit_line` will be [numpy] arrays of shape `(m, n)` if `p = 1`, or [numpy] arrays of shape `(m, n, p)` if `p > 1`. ### Example Suppose we applied four sets of inputs to a crossbar array and wanted to find the current flowing through the device in the first row and fourth column when the third set of inputs was applied. We could print out the current through that device using the following piece of code: ```python import badcrossbar # Applied voltages in volts. applied_voltages = [[1.5, 4.1, 2.6, 2.1], [2.3, 4.5, 1.1, 0.8], [1.7, 4.0, 3.3, 1.1]] # Device resistances in ohms. resistances = [[345, 903, 755, 257, 646], [652, 401, 508, 166, 454], [442, 874, 190, 244, 635]] # Interconnect resistance in ohms. r_i = 0.5 # Computing the solution. solution = badcrossbar.compute(applied_voltages, resistances, r_i) # Current that we are interested in (note zero-based indexing). current = solution.currents.device[0, 3, 2] print('\nCurrent through the device in question is {} A.'.format(current)) ``` #### Output ```text 2020-06-14 15:09:37 Initialising simulation. 2020-06-14 15:09:37 Started solving for v. 2020-06-14 15:09:37 Solved for v. 2020-06-14 15:09:37 Extracted node voltages. 2020-06-14 15:09:37 Extracted currents from all branches in the crossbar. Current through the device in question is 0.009856197822795886 A. ``` More examples can be found in files [1_single_set_of_inputs.py] and [2_multiple_sets_of_inputs.py]. ### Perfectly insulating devices Devices with infinite resistance can be denoted using resistance value of `numpy.inf` (or equivalently `math.inf`). ## Plotting [badcrossbar] provides [`badcrossbar.plot`] module which allows to color crossbar branches and nodes. This is done by functions `badcrossbar.plot.branches` and `badcrossbar.plot.nodes`, respectively. Although their primary purpose is for plotting currents and voltages, these functions accept arbitrary arrays and color the branches and nodes, according to the values of these arrays. This functionality is explained in more detail in example [3_different_variables.py]. ### Currents The function `badcrossbar.plot.branches` accepts either individual arrays for crossbar devices, word line branches and bit line branches, or a named tuple containing all currents. If any of the arrays are 3D, they are averaged along the third axis. The following piece of code computes and plots average branch currents over four sets of applied inputs: ```python import badcrossbar # Applied voltages in volts. applied_voltages = [[1.5, 4.1, 2.6, 2.1], [2.3, 4.5, 1.1, 0.8], [1.7, 4.0, 3.3, 1.1]] # Device resistances in ohms. resistances = [[345, 903, 755, 257, 646], [652, 401, 508, 166, 454], [442, 874, 190, 244, 635]] # Interconnect resistance in ohms. r_i = 0.5 # Computing the solution. solution = badcrossbar.compute(applied_voltages, resistances, r_i) # Plotting average branch currents over all four sets of inputs. # We additionally set a custom filename and label of the color bar. badcrossbar.plot.branches(currents=solution.currents, filename='average-currents', axis_label='Average current (A)') ``` The produced PDF file should contain a diagram similar to the one shown below: ![Crossbar currents](images/average-currents.png) ### Voltages Similarly, `badcrossbar.plot.nodes` accepts either individual arrays for nodes on the word and bit lines, or a named tuple containing both sets of voltages. If any of the arrays are 3D, they are averaged along the third axis. The following piece of code computes and plots average node voltages over four sets of applied inputs: ```python import badcrossbar # Applied voltages in volts. applied_voltages = [[1.5, 4.1, 2.6, 2.1], [2.3, 4.5, 1.1, 0.8], [1.7, 4.0, 3.3, 1.1]] # Device resistances in ohms. resistances = [[345, 903, 755, 257, 646], [652, 401, 508, 166, 454], [442, 874, 190, 244, 635]] # Interconnect resistance in ohms. r_i = 0.5 # Computing the solution. solution = badcrossbar.compute(applied_voltages, resistances, r_i) # Plotting average node voltages over all four sets of inputs. badcrossbar.plot.nodes(voltages=solution.voltages, axis_label='Average voltage (V)', filename='average-voltages') ``` The produced PDF file should contain a diagram similar to the one shown below. Because the crossbar array, in this case, is small and the interconnect resistance is small relative to the resistance of the devices, we do not see much variation between voltages of nodes of the same type. Differences become more apparent with larger crossbar arrays, as explored in [2_custom_parameters.py], for example. ![Crossbar voltages](images/average-voltages.png) Examples of how to plot currents or voltages of only certain parts of the crossbar, or how to take into account only part of the sets of applied voltages can be found in [1_default_parameters.py]. ### Modifying diagrams Plotting sub-package produces vector images (as PDF files) that can then be edited in any vector graphics manipulation program. However, it also provides option to modify some of the features of the diagram that might be difficult to change once the image is produced. Example [2_custom_parameters.py] explores some of these options, while the complete list of modifiable parameters can be found in function docstrings of [`badcrossbar.plot`] module. [badcrossbar]:https://github.com/joksas/badcrossbar [numpy]:https://github.com/numpy/numpy [pycairo]:https://github.com/pygobject/pycairo [`badcrossbar.plot`]:badcrossbar/plot.py [1_single_set_of_inputs.py]:examples/computing/1_single_set_of_inputs.py [2_multiple_sets_of_inputs.py]:examples/computing/2_multiple_sets_of_inputs.py [1_default_parameters.py]:examples/plotting/1_default_parameters.py [2_custom_parameters.py]:examples/plotting/2_custom_parameters.py [3_different_variables.py]:examples/plotting/3_different_variables.py