diff --git a/binder/requirements.txt b/binder/requirements.txt
index 4e195eb..b8ff6ec 100644
--- a/binder/requirements.txt
+++ b/binder/requirements.txt
@@ -1,3 +1,4 @@
numpy
pandas
sphinxcontrib-svgbob
+toolz
diff --git a/binder/runtime.txt b/binder/runtime.txt
index 9850e86..5509089 100644
--- a/binder/runtime.txt
+++ b/binder/runtime.txt
@@ -1 +1 @@
-python-3.8
+python-3.10
diff --git a/sparsetensorviz/notebooks/Example_Rank4-binsparse.ipynb b/sparsetensorviz/notebooks/Example_Rank4-binsparse.ipynb
new file mode 100644
index 0000000..2d675fb
--- /dev/null
+++ b/sparsetensorviz/notebooks/Example_Rank4-binsparse.ipynb
@@ -0,0 +1,8053 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "36476992",
+ "metadata": {},
+ "source": [
+ "# Examples of \"bundling\" dimensions into groups to form the custom Binsparse levels\n",
+ "\n",
+ "Custom formats: https://graphblas.org/binsparse-specification/#custom_formats\n",
+ "\n",
+ "### Data of bundled groups\n",
+ "- `Sparse(n1)`\n",
+ " - pointers array (or scalar `k` for ELLPACK-like compression) if not initial level\n",
+ " - indices for `n1` dimensions\n",
+ "- `Dense(n2)`\n",
+ " - N/A\n",
+ "\n",
+ "_Note that indices grouped together in this scheme are all the same length, so they can be stored as separate 1-d arrays or a single 2-d array_"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "1d023620",
+ "metadata": {},
+ "source": [
+ "# Examples for rank 4 array\n",
+ "\n",
+ "Pass COO-like data to `SparseTensor` with one array of indices for each dimension.\n",
+ "\n",
+ "The default is to create CSF sparse data structure where all levels are Sparse."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "3f4ad63f",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|S[2] -|S -----|\n",
+ ".------. .---.\n",
+ "|i0 i1 |p2 |i2 |\n",
+ "`------' `---'\n",
+ "================\n",
+ ".-----. 0 .--.\n",
+ "|0 0 |-+--|1 |\n",
+ "`-----' `--|2 |\n",
+ ".-----. 2 |--|\n",
+ "|0 1 |----|0 |\n",
+ "`-----' 3 `--'\n",
+ ".-----. .--.\n",
+ "|1 0 |-+--|0 |\n",
+ "`-----' `--|2 |\n",
+ ".-----. 5 |--|\n",
+ "|1 1 |-+--|0 |\n",
+ "`-----' |--|1 |\n",
+ " `--|2 |\n",
+ " 8 `--'"
+ ]
+ },
+ "execution_count": 1,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "from sparsetensorviz import SparseTensor\n",
+ "import itertools\n",
+ "import IPython\n",
+ "\n",
+ "indices = [\n",
+ " [0, 0, 1, 1, 0, 1, 1, 1],\n",
+ " [0, 0, 0, 0, 1, 1, 1, 1],\n",
+ " [1, 2, 0, 2, 0, 0, 1, 2],\n",
+ "]\n",
+ "sp = SparseTensor.from_binsparse(indices, structure=\"Sparse(2)-Sparse\")\n",
+ "# Display as SVG diagram\n",
+ "sp"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "833d4e98",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[array([0, 0, 1, 1]), array([0, 1, 0, 1]), array([1, 2, 0, 0, 2, 0, 1, 2])]"
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "sp.indices"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "e3399807",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[None, array([0, 2, 3, 5, 8])]"
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "sp.pointers"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "e0bdf4f2",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "|S[2] -|S -----|\n",
+ ".------. .---.\n",
+ "|i0 i1 |p2 |i2 |\n",
+ "`------' `---'\n",
+ "================\n",
+ ".-----. 0 .--.\n",
+ "|0 0 |-+--|1 |\n",
+ "`-----' `--|2 |\n",
+ ".-----. 2 |--|\n",
+ "|0 1 |----|0 |\n",
+ "`-----' 3 `--'\n",
+ ".-----. .--.\n",
+ "|1 0 |-+--|0 |\n",
+ "`-----' `--|2 |\n",
+ ".-----. 5 |--|\n",
+ "|1 1 |-+--|0 |\n",
+ "`-----' |--|1 |\n",
+ " `--|2 |\n",
+ " 8 `--'\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Can also display as ASCII diagram\n",
+ "print(sp)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "1f3d5d9c-a512-43c1-aec6-2578d6dd8724",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|D -|S[2] -----|\n",
+ ".~~~. .------.\n",
+ "┊i0 ┊p1 |i1 i2 |\n",
+ "`~~~' `------'\n",
+ "================\n",
+ ".~~. 0 .-----.\n",
+ "┊0 ┊-+--|0 1 |\n",
+ "`~~' | `-----'\n",
+ " | .-----.\n",
+ " |--|0 2 |\n",
+ " | `-----'\n",
+ " | .-----.\n",
+ " `--|1 0 |\n",
+ " 3 `-----'\n",
+ ".~~. .-----.\n",
+ "┊1 ┊-+--|0 0 |\n",
+ "`~~' | `-----'\n",
+ " | .-----.\n",
+ " |--|0 2 |\n",
+ " | `-----'\n",
+ " | .-----.\n",
+ " |--|1 0 |\n",
+ " | `-----'\n",
+ " | .-----.\n",
+ " |--|1 1 |\n",
+ " | `-----'\n",
+ " | .-----.\n",
+ " `--|1 2 |\n",
+ " 8 `-----'"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# Example with a dense level\n",
+ "SparseTensor.from_binsparse(indices, structure=\"Dense-S(2)\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "e5dd4568-7c48-404f-9ba2-f4f25ab0ce4f",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|S -|S -----|S -----|\n",
+ ".---. .---. .---.\n",
+ "|i0 |p1 |i1 |p2 |i2 |\n",
+ "`---' `---' `---'\n",
+ "=====================\n",
+ ".--. 0 .--. 0 .--.\n",
+ "|0 |-+--|0 |-+--|1 |\n",
+ "`--' | `--' `--|2 |\n",
+ " | .--. 2 |--|\n",
+ " `--|1 |----|0 |\n",
+ " 2 `--' 3 `--'\n",
+ ".--. .--. .--.\n",
+ "|1 |-+--|0 |-+--|0 |\n",
+ "`--' | `--' `--|2 |\n",
+ " | .--. 5 |--|\n",
+ " `--|1 |-+--|0 |\n",
+ " 4 `--' |--|1 |\n",
+ " `--|2 |\n",
+ " 8 `--'"
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# The default structure is all levels Sparse(1), which is traditional compressed sparse fiber (CSF)\n",
+ "SparseTensor.from_binsparse(indices)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "51842351",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Display all standard sparse structures\n",
+ "\n",
+ "Hyphonated connecting lines indicate pointers that don't need stored."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "03f416ee-3b94-4044-8f4f-6e6f5d76b199",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def display_header(st):\n",
+ " binsparse_groups = \", \".join(st.binsparse_groups)\n",
+ " taco_structure = \", \".join(st.taco_structure)\n",
+ " text = (\n",
+ " \"```\\n\"\n",
+ " f\"binsparse_groups = [{binsparse_groups}]\\n\\n\"\n",
+ " f\"taco_structure = [{taco_structure}]\\n\\n\"\n",
+ " \"```\"\n",
+ " # f\"Erik's structure = {st.structure}\\n\\n\"\n",
+ " )\n",
+ " IPython.display.display(IPython.display.Markdown(text))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "232210ab",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/markdown": [
+ "# `S[4]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Sparse(4)]\n",
+ "\n",
+ "taco_structure = [compressed-nonunique, singleton, singleton, singleton]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|S[4] -------|\n",
+ ".------------.\n",
+ "|i0 i1 i2 i3 |\n",
+ "`------------'\n",
+ "==============\n",
+ ".-----------.\n",
+ "|0 0 0 1 |\n",
+ "`-----------'\n",
+ ".-----------.\n",
+ "|0 0 0 2 |\n",
+ "`-----------'\n",
+ ".-----------.\n",
+ "|0 1 0 0 |\n",
+ "`-----------'\n",
+ ".-----------.\n",
+ "|0 1 0 2 |\n",
+ "`-----------'\n",
+ ".-----------.\n",
+ "|0 1 1 0 |\n",
+ "`-----------'\n",
+ ".-----------.\n",
+ "|1 1 1 0 |\n",
+ "`-----------'\n",
+ ".-----------.\n",
+ "|1 1 1 1 |\n",
+ "`-----------'\n",
+ ".-----------.\n",
+ "|1 1 1 2 |\n",
+ "`-----------'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "# `S[3]-S[1]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Sparse(3), Sparse(1)]\n",
+ "\n",
+ "taco_structure = [compressed, singleton, singleton, compressed]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|S[3] ----|S -----|\n",
+ ".---------. .---.\n",
+ "|i0 i1 i2 |p3 |i3 |\n",
+ "`---------' `---'\n",
+ "===================\n",
+ ".--------. 0 .--.\n",
+ "|0 0 0 |-+--|1 |\n",
+ "`--------' `--|2 |\n",
+ ".--------. 2 |--|\n",
+ "|0 1 0 |-+--|0 |\n",
+ "`--------' `--|2 |\n",
+ ".--------. 4 |--|\n",
+ "|0 1 1 |----|0 |\n",
+ "`--------' 5 `--'\n",
+ ".--------. .--.\n",
+ "|1 1 1 |-+--|0 |\n",
+ "`--------' |--|1 |\n",
+ " `--|2 |\n",
+ " 8 `--'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "# `S[2]-S[2]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Sparse(2), Sparse(2)]\n",
+ "\n",
+ "taco_structure = [compressed, singleton, compressed-nonunique, singleton]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|S[2] -|S[2] -----|\n",
+ ".------. .------.\n",
+ "|i0 i1 |p2 |i2 i3 |\n",
+ "`------' `------'\n",
+ "===================\n",
+ ".-----. 0 .-----.\n",
+ "|0 0 |-+--|0 1 |\n",
+ "`-----' | `-----'\n",
+ " | .-----.\n",
+ " `--|0 2 |\n",
+ " 2 `-----'\n",
+ ".-----. .-----.\n",
+ "|0 1 |-+--|0 0 |\n",
+ "`-----' | `-----'\n",
+ " | .-----.\n",
+ " |--|0 2 |\n",
+ " | `-----'\n",
+ " | .-----.\n",
+ " `--|1 0 |\n",
+ " 5 `-----'\n",
+ ".-----. .-----.\n",
+ "|1 1 |-+--|1 0 |\n",
+ "`-----' | `-----'\n",
+ " | .-----.\n",
+ " |--|1 1 |\n",
+ " | `-----'\n",
+ " | .-----.\n",
+ " `--|1 2 |\n",
+ " 8 `-----'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "# `S[2]-D[1]-S[1]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Sparse(2), Dense(1), Sparse(1)]\n",
+ "\n",
+ "taco_structure = [compressed, singleton, dense, compressed]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|S[2] -|D -----|S -----|\n",
+ ".------. .~~~. .---.\n",
+ "|i0 i1 |p2 ┊i2 ┊p3 |i3 |\n",
+ "`------' `~~~' `---'\n",
+ "========================\n",
+ ".-----. 0 .~~. 0 .--.\n",
+ "|0 0 |~+~~┊0 ┊-+--|1 |\n",
+ "`-----' ┊ `~~' `--|2 |\n",
+ " ┊ .~~. 2 `--'\n",
+ " `~~┊1 ┊)\n",
+ " 2 `~~'\n",
+ ".-----. .~~. 2 .--.\n",
+ "|0 1 |~+~~┊0 ┊-+--|0 |\n",
+ "`-----' ┊ `~~' `--|2 |\n",
+ " ┊ .~~. 4 |--|\n",
+ " `~~┊1 ┊----|0 |\n",
+ " 4 `~~' 5 `--'\n",
+ ".-----. .~~.\n",
+ "|1 1 |~+~~┊0 ┊)\n",
+ "`-----' ┊ `~~'\n",
+ " ┊ .~~. 5 .--.\n",
+ " `~~┊1 ┊-+--|0 |\n",
+ " 6 `~~' |--|1 |\n",
+ " `--|2 |\n",
+ " 8 `--'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "# `S[2]-S[1]-S[1]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Sparse(2), Sparse(1), Sparse(1)]\n",
+ "\n",
+ "taco_structure = [compressed, singleton, compressed, compressed]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|S[2] -|S -----|S -----|\n",
+ ".------. .---. .---.\n",
+ "|i0 i1 |p2 |i2 |p3 |i3 |\n",
+ "`------' `---' `---'\n",
+ "========================\n",
+ ".-----. 0 .--. 0 .--.\n",
+ "|0 0 |----|0 |-+--|1 |\n",
+ "`-----' 1 `--' `--|2 |\n",
+ ".-----. .--. 2 |--|\n",
+ "|0 1 |-+--|0 |-+--|0 |\n",
+ "`-----' | `--' `--|2 |\n",
+ " | .--. 4 |--|\n",
+ " `--|1 |----|0 |\n",
+ " 3 `--' 5 `--'\n",
+ ".-----. .--. .--.\n",
+ "|1 1 |----|1 |-+--|0 |\n",
+ "`-----' 4 `--' |--|1 |\n",
+ " `--|2 |\n",
+ " 8 `--'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "# `D[1]-S[3]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Dense(1), Sparse(3)]\n",
+ "\n",
+ "taco_structure = [dense, compressed-nonunique, singleton, singleton]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|D -|S[3] --------|\n",
+ ".~~~. .---------.\n",
+ "┊i0 ┊p1 |i1 i2 i3 |\n",
+ "`~~~' `---------'\n",
+ "===================\n",
+ ".~~. 0 .--------.\n",
+ "┊0 ┊-+--|0 0 1 |\n",
+ "`~~' | `--------'\n",
+ " | .--------.\n",
+ " |--|0 0 2 |\n",
+ " | `--------'\n",
+ " | .--------.\n",
+ " |--|1 0 0 |\n",
+ " | `--------'\n",
+ " | .--------.\n",
+ " |--|1 0 2 |\n",
+ " | `--------'\n",
+ " | .--------.\n",
+ " `--|1 1 0 |\n",
+ " 5 `--------'\n",
+ ".~~. .--------.\n",
+ "┊1 ┊-+--|1 1 0 |\n",
+ "`~~' | `--------'\n",
+ " | .--------.\n",
+ " |--|1 1 1 |\n",
+ " | `--------'\n",
+ " | .--------.\n",
+ " `--|1 1 2 |\n",
+ " 8 `--------'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "# `D[1]-S[2]-S[1]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Dense(1), Sparse(2), Sparse(1)]\n",
+ "\n",
+ "taco_structure = [dense, compressed, singleton, compressed]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|D -|S[2] -----|S -----|\n",
+ ".~~~. .------. .---.\n",
+ "┊i0 ┊p1 |i1 i2 |p3 |i3 |\n",
+ "`~~~' `------' `---'\n",
+ "========================\n",
+ ".~~. 0 .-----. 0 .--.\n",
+ "┊0 ┊-+--|0 0 |-+--|1 |\n",
+ "`~~' | `-----' `--|2 |\n",
+ " | .-----. 2 |--|\n",
+ " |--|1 0 |-+--|0 |\n",
+ " | `-----' `--|2 |\n",
+ " | .-----. 4 |--|\n",
+ " `--|1 1 |----|0 |\n",
+ " 3 `-----' 5 `--'\n",
+ ".~~. .-----. .--.\n",
+ "┊1 ┊----|1 1 |-+--|0 |\n",
+ "`~~' 4 `-----' |--|1 |\n",
+ " `--|2 |\n",
+ " 8 `--'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "# `D[2]-S[2]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Dense(2), Sparse(2)]\n",
+ "\n",
+ "taco_structure = [dense, dense, compressed-nonunique, singleton]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|D[2] -|S[2] -----|\n",
+ ".~~ ~~~. .------.\n",
+ "┊i0 i1 ┊p2 |i2 i3 |\n",
+ "`~~ ~~~' `------'\n",
+ "===================\n",
+ ".~~ ~~. 0 .-----.\n",
+ "┊0 0 ┊-+--|0 1 |\n",
+ "`~~ ~~' | `-----'\n",
+ " | .-----.\n",
+ " `--|0 2 |\n",
+ " 2 `-----'\n",
+ " ~~. .-----.\n",
+ " 1 ┊-+--|0 0 |\n",
+ " ~~' | `-----'\n",
+ ".~~ ~~. | .-----.\n",
+ "┊1 0 ┊)|--|0 2 |\n",
+ "`~~ ~~' | `-----'\n",
+ " | .-----.\n",
+ " `--|1 0 |\n",
+ " 5 `-----'\n",
+ " ~~. 5 .-----.\n",
+ " 1 ┊-+--|1 0 |\n",
+ " ~~' | `-----'\n",
+ " | .-----.\n",
+ " |--|1 1 |\n",
+ " | `-----'\n",
+ " | .-----.\n",
+ " `--|1 2 |\n",
+ " 8 `-----'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "# `D[3]-S[1]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Dense(3), Sparse(1)]\n",
+ "\n",
+ "taco_structure = [dense, dense, dense, compressed]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|D[3] ----|S -----|\n",
+ ".~~ ~~ ~~~. .---.\n",
+ "┊i0 i1 i2 ┊p3 |i3 |\n",
+ "`~~ ~~ ~~~' `---'\n",
+ "===================\n",
+ ".~~ ~~ ~~. 0 .--.\n",
+ "┊0 0 0 ┊-+--|1 |\n",
+ "`~~ ~~ ~~' `--|2 |\n",
+ " ~~. 2 `--'\n",
+ " 1 ┊)\n",
+ " ~~'\n",
+ " ~~ ~~. 2 .--.\n",
+ " 1 0 ┊-+--|0 |\n",
+ " ~~ ~~' `--|2 |\n",
+ " ~~. 4 |--|\n",
+ " 1 ┊----|0 |\n",
+ " ~~' 5 `--'\n",
+ ".~~ ~~ ~~.\n",
+ "┊1 0 0 ┊)\n",
+ "`~~ ~~ ~~'\n",
+ " ~~. 5\n",
+ " 1 ┊)\n",
+ " ~~'\n",
+ " ~~ ~~. 5\n",
+ " 1 0 ┊)\n",
+ " ~~ ~~'\n",
+ " ~~. 5 .--.\n",
+ " 1 ┊-+--|0 |\n",
+ " ~~' |--|1 |\n",
+ " `--|2 |\n",
+ " 8 `--'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "# `D[2]-S[1]-S[1]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Dense(2), Sparse(1), Sparse(1)]\n",
+ "\n",
+ "taco_structure = [dense, dense, compressed, compressed]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|D[2] -|S -----|S -----|\n",
+ ".~~ ~~~. .---. .---.\n",
+ "┊i0 i1 ┊p2 |i2 |p3 |i3 |\n",
+ "`~~ ~~~' `---' `---'\n",
+ "========================\n",
+ ".~~ ~~. 0 .--. 0 .--.\n",
+ "┊0 0 ┊----|0 |-+--|1 |\n",
+ "`~~ ~~' 1 `--' `--|2 |\n",
+ " ~~. .--. 2 |--|\n",
+ " 1 ┊-+--|0 |-+--|0 |\n",
+ " ~~' | `--' `--|2 |\n",
+ ".~~ ~~. | .--. 4 |--|\n",
+ "┊1 0 ┊)`--|1 |----|0 |\n",
+ "`~~ ~~' 3 `--' 5 `--'\n",
+ " ~~. 3 .--. .--.\n",
+ " 1 ┊----|1 |-+--|0 |\n",
+ " ~~' 4 `--' |--|1 |\n",
+ " `--|2 |\n",
+ " 8 `--'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "# `D[1]-S[1]-S[2]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Dense(1), Sparse(1), Sparse(2)]\n",
+ "\n",
+ "taco_structure = [dense, compressed, compressed-nonunique, singleton]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|D -|S -----|S[2] -----|\n",
+ ".~~~. .---. .------.\n",
+ "┊i0 ┊p1 |i1 |p2 |i2 i3 |\n",
+ "`~~~' `---' `------'\n",
+ "========================\n",
+ ".~~. 0 .--. 0 .-----.\n",
+ "┊0 ┊-+--|0 |-+--|0 1 |\n",
+ "`~~' | `--' | `-----'\n",
+ " | | .-----.\n",
+ " | `--|0 2 |\n",
+ " | 2 `-----'\n",
+ " | .--. .-----.\n",
+ " `--|1 |-+--|0 0 |\n",
+ " 2 `--' | `-----'\n",
+ " | .-----.\n",
+ " |--|0 2 |\n",
+ " | `-----'\n",
+ " | .-----.\n",
+ " `--|1 0 |\n",
+ " 5 `-----'\n",
+ ".~~. .--. .-----.\n",
+ "┊1 ┊----|1 |-+--|1 0 |\n",
+ "`~~' 3 `--' | `-----'\n",
+ " | .-----.\n",
+ " |--|1 1 |\n",
+ " | `-----'\n",
+ " | .-----.\n",
+ " `--|1 2 |\n",
+ " 8 `-----'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "# `D[1]-S[1]-D[1]-S[1]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Dense(1), Sparse(1), Dense(1), Sparse(1)]\n",
+ "\n",
+ "taco_structure = [dense, compressed, dense, compressed]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|D -|S -----|D -----|S -----|\n",
+ ".~~~. .---. .~~~. .---.\n",
+ "┊i0 ┊p1 |i1 |p2 ┊i2 ┊p3 |i3 |\n",
+ "`~~~' `---' `~~~' `---'\n",
+ "=============================\n",
+ ".~~. 0 .--. 0 .~~. 0 .--.\n",
+ "┊0 ┊-+--|0 |~+~~┊0 ┊-+--|1 |\n",
+ "`~~' | `--' ┊ `~~' `--|2 |\n",
+ " | ┊ .~~. 2 `--'\n",
+ " | `~~┊1 ┊)\n",
+ " | 2 `~~'\n",
+ " | .--. .~~. 2 .--.\n",
+ " `--|1 |~+~~┊0 ┊-+--|0 |\n",
+ " 2 `--' ┊ `~~' `--|2 |\n",
+ " ┊ .~~. 4 |--|\n",
+ " `~~┊1 ┊----|0 |\n",
+ " 4 `~~' 5 `--'\n",
+ ".~~. .--. .~~.\n",
+ "┊1 ┊----|1 |~+~~┊0 ┊)\n",
+ "`~~' 3 `--' ┊ `~~'\n",
+ " ┊ .~~. 5 .--.\n",
+ " `~~┊1 ┊-+--|0 |\n",
+ " 6 `~~' |--|1 |\n",
+ " `--|2 |\n",
+ " 8 `--'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "# `D[1]-S[1]-S[1]-S[1]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Dense(1), Sparse(1), Sparse(1), Sparse(1)]\n",
+ "\n",
+ "taco_structure = [dense, compressed, compressed, compressed]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|D -|S -----|S -----|S -----|\n",
+ ".~~~. .---. .---. .---.\n",
+ "┊i0 ┊p1 |i1 |p2 |i2 |p3 |i3 |\n",
+ "`~~~' `---' `---' `---'\n",
+ "=============================\n",
+ ".~~. 0 .--. 0 .--. 0 .--.\n",
+ "┊0 ┊-+--|0 |----|0 |-+--|1 |\n",
+ "`~~' | `--' 1 `--' `--|2 |\n",
+ " | .--. .--. 2 |--|\n",
+ " `--|1 |-+--|0 |-+--|0 |\n",
+ " 2 `--' | `--' `--|2 |\n",
+ " | .--. 4 |--|\n",
+ " `--|1 |----|0 |\n",
+ " 3 `--' 5 `--'\n",
+ ".~~. .--. .--. .--.\n",
+ "┊1 ┊----|1 |----|1 |-+--|0 |\n",
+ "`~~' 3 `--' 4 `--' |--|1 |\n",
+ " `--|2 |\n",
+ " 8 `--'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "# `S[1]-S[3]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Sparse(1), Sparse(3)]\n",
+ "\n",
+ "taco_structure = [compressed, compressed-nonunique, singleton, singleton]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|S -|S[3] --------|\n",
+ ".---. .---------.\n",
+ "|i0 |p1 |i1 i2 i3 |\n",
+ "`---' `---------'\n",
+ "===================\n",
+ ".--. 0 .--------.\n",
+ "|0 |-+--|0 0 1 |\n",
+ "`--' | `--------'\n",
+ " | .--------.\n",
+ " |--|0 0 2 |\n",
+ " | `--------'\n",
+ " | .--------.\n",
+ " |--|1 0 0 |\n",
+ " | `--------'\n",
+ " | .--------.\n",
+ " |--|1 0 2 |\n",
+ " | `--------'\n",
+ " | .--------.\n",
+ " `--|1 1 0 |\n",
+ " 5 `--------'\n",
+ ".--. .--------.\n",
+ "|1 |-+--|1 1 0 |\n",
+ "`--' | `--------'\n",
+ " | .--------.\n",
+ " |--|1 1 1 |\n",
+ " | `--------'\n",
+ " | .--------.\n",
+ " `--|1 1 2 |\n",
+ " 8 `--------'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "# `S[1]-S[2]-S[1]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Sparse(1), Sparse(2), Sparse(1)]\n",
+ "\n",
+ "taco_structure = [compressed, compressed, singleton, compressed]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|S -|S[2] -----|S -----|\n",
+ ".---. .------. .---.\n",
+ "|i0 |p1 |i1 i2 |p3 |i3 |\n",
+ "`---' `------' `---'\n",
+ "========================\n",
+ ".--. 0 .-----. 0 .--.\n",
+ "|0 |-+--|0 0 |-+--|1 |\n",
+ "`--' | `-----' `--|2 |\n",
+ " | .-----. 2 |--|\n",
+ " |--|1 0 |-+--|0 |\n",
+ " | `-----' `--|2 |\n",
+ " | .-----. 4 |--|\n",
+ " `--|1 1 |----|0 |\n",
+ " 3 `-----' 5 `--'\n",
+ ".--. .-----. .--.\n",
+ "|1 |----|1 1 |-+--|0 |\n",
+ "`--' 4 `-----' |--|1 |\n",
+ " `--|2 |\n",
+ " 8 `--'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "# `S[1]-D[1]-S[2]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Sparse(1), Dense(1), Sparse(2)]\n",
+ "\n",
+ "taco_structure = [compressed, dense, compressed-nonunique, singleton]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|S -|D -----|S[2] -----|\n",
+ ".---. .~~~. .------.\n",
+ "|i0 |p1 ┊i1 ┊p2 |i2 i3 |\n",
+ "`---' `~~~' `------'\n",
+ "========================\n",
+ ".--. 0 .~~. 0 .-----.\n",
+ "|0 |~+~~┊0 ┊-+--|0 1 |\n",
+ "`--' ┊ `~~' | `-----'\n",
+ " ┊ | .-----.\n",
+ " ┊ `--|0 2 |\n",
+ " ┊ 2 `-----'\n",
+ " ┊ .~~. .-----.\n",
+ " `~~┊1 ┊-+--|0 0 |\n",
+ " 2 `~~' | `-----'\n",
+ ".--. .~~. | .-----.\n",
+ "|1 |~+~~┊0 ┊)|--|0 2 |\n",
+ "`--' ┊ `~~' | `-----'\n",
+ " ┊ | .-----.\n",
+ " ┊ `--|1 0 |\n",
+ " ┊ 5 `-----'\n",
+ " ┊ .~~. 5 .-----.\n",
+ " `~~┊1 ┊-+--|1 0 |\n",
+ " 4 `~~' | `-----'\n",
+ " | .-----.\n",
+ " |--|1 1 |\n",
+ " | `-----'\n",
+ " | .-----.\n",
+ " `--|1 2 |\n",
+ " 8 `-----'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "# `S[1]-D[2]-S[1]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Sparse(1), Dense(2), Sparse(1)]\n",
+ "\n",
+ "taco_structure = [compressed, dense, dense, compressed]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|S -|D[2] -----|S -----|\n",
+ ".---. .~~ ~~~. .---.\n",
+ "|i0 |p1 ┊i1 i2 ┊p3 |i3 |\n",
+ "`---' `~~ ~~~' `---'\n",
+ "========================\n",
+ ".--. 0 .~~ ~~. 0 .--.\n",
+ "|0 |~+~~┊0 0 ┊-+--|1 |\n",
+ "`--' ┊ `~~ ~~' `--|2 |\n",
+ " ┊ ~~. 2 `--'\n",
+ " ┊ 1 ┊)\n",
+ " ┊ ~~'\n",
+ " ┊ .~~ ~~. 2 .--.\n",
+ " `~~┊1 0 ┊-+--|0 |\n",
+ " 2 `~~ ~~' `--|2 |\n",
+ " ~~. 4 |--|\n",
+ " 1 ┊----|0 |\n",
+ " ~~' 5 `--'\n",
+ ".--. .~~ ~~.\n",
+ "|1 |~+~~┊0 0 ┊)\n",
+ "`--' ┊ `~~ ~~'\n",
+ " ┊ ~~. 5\n",
+ " ┊ 1 ┊)\n",
+ " ┊ ~~'\n",
+ " ┊ .~~ ~~. 5\n",
+ " `~~┊1 0 ┊)\n",
+ " 4 `~~ ~~'\n",
+ " ~~. 5 .--.\n",
+ " 1 ┊-+--|0 |\n",
+ " ~~' |--|1 |\n",
+ " `--|2 |\n",
+ " 8 `--'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "# `S[1]-D[1]-S[1]-S[1]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Sparse(1), Dense(1), Sparse(1), Sparse(1)]\n",
+ "\n",
+ "taco_structure = [compressed, dense, compressed, compressed]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|S -|D -----|S -----|S -----|\n",
+ ".---. .~~~. .---. .---.\n",
+ "|i0 |p1 ┊i1 ┊p2 |i2 |p3 |i3 |\n",
+ "`---' `~~~' `---' `---'\n",
+ "=============================\n",
+ ".--. 0 .~~. 0 .--. 0 .--.\n",
+ "|0 |~+~~┊0 ┊----|0 |-+--|1 |\n",
+ "`--' ┊ `~~' 1 `--' `--|2 |\n",
+ " ┊ .~~. .--. 2 |--|\n",
+ " `~~┊1 ┊-+--|0 |-+--|0 |\n",
+ " 2 `~~' | `--' `--|2 |\n",
+ ".--. .~~. | .--. 4 |--|\n",
+ "|1 |~+~~┊0 ┊)`--|1 |----|0 |\n",
+ "`--' ┊ `~~' 3 `--' 5 `--'\n",
+ " ┊ .~~. 3 .--. .--.\n",
+ " `~~┊1 ┊----|1 |-+--|0 |\n",
+ " 4 `~~' 4 `--' |--|1 |\n",
+ " `--|2 |\n",
+ " 8 `--'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "# `S[1]-S[1]-S[2]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Sparse(1), Sparse(1), Sparse(2)]\n",
+ "\n",
+ "taco_structure = [compressed, compressed, compressed-nonunique, singleton]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|S -|S -----|S[2] -----|\n",
+ ".---. .---. .------.\n",
+ "|i0 |p1 |i1 |p2 |i2 i3 |\n",
+ "`---' `---' `------'\n",
+ "========================\n",
+ ".--. 0 .--. 0 .-----.\n",
+ "|0 |-+--|0 |-+--|0 1 |\n",
+ "`--' | `--' | `-----'\n",
+ " | | .-----.\n",
+ " | `--|0 2 |\n",
+ " | 2 `-----'\n",
+ " | .--. .-----.\n",
+ " `--|1 |-+--|0 0 |\n",
+ " 2 `--' | `-----'\n",
+ " | .-----.\n",
+ " |--|0 2 |\n",
+ " | `-----'\n",
+ " | .-----.\n",
+ " `--|1 0 |\n",
+ " 5 `-----'\n",
+ ".--. .--. .-----.\n",
+ "|1 |----|1 |-+--|1 0 |\n",
+ "`--' 3 `--' | `-----'\n",
+ " | .-----.\n",
+ " |--|1 1 |\n",
+ " | `-----'\n",
+ " | .-----.\n",
+ " `--|1 2 |\n",
+ " 8 `-----'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "# `S[1]-S[1]-D[1]-S[1]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Sparse(1), Sparse(1), Dense(1), Sparse(1)]\n",
+ "\n",
+ "taco_structure = [compressed, compressed, dense, compressed]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|S -|S -----|D -----|S -----|\n",
+ ".---. .---. .~~~. .---.\n",
+ "|i0 |p1 |i1 |p2 ┊i2 ┊p3 |i3 |\n",
+ "`---' `---' `~~~' `---'\n",
+ "=============================\n",
+ ".--. 0 .--. 0 .~~. 0 .--.\n",
+ "|0 |-+--|0 |~+~~┊0 ┊-+--|1 |\n",
+ "`--' | `--' ┊ `~~' `--|2 |\n",
+ " | ┊ .~~. 2 `--'\n",
+ " | `~~┊1 ┊)\n",
+ " | 2 `~~'\n",
+ " | .--. .~~. 2 .--.\n",
+ " `--|1 |~+~~┊0 ┊-+--|0 |\n",
+ " 2 `--' ┊ `~~' `--|2 |\n",
+ " ┊ .~~. 4 |--|\n",
+ " `~~┊1 ┊----|0 |\n",
+ " 4 `~~' 5 `--'\n",
+ ".--. .--. .~~.\n",
+ "|1 |----|1 |~+~~┊0 ┊)\n",
+ "`--' 3 `--' ┊ `~~'\n",
+ " ┊ .~~. 5 .--.\n",
+ " `~~┊1 ┊-+--|0 |\n",
+ " 6 `~~' |--|1 |\n",
+ " `--|2 |\n",
+ " 8 `--'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "# `S[1]-S[1]-S[1]-S[1]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Sparse(1), Sparse(1), Sparse(1), Sparse(1)]\n",
+ "\n",
+ "taco_structure = [compressed, compressed, compressed, compressed]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|S -|S -----|S -----|S -----|\n",
+ ".---. .---. .---. .---.\n",
+ "|i0 |p1 |i1 |p2 |i2 |p3 |i3 |\n",
+ "`---' `---' `---' `---'\n",
+ "=============================\n",
+ ".--. 0 .--. 0 .--. 0 .--.\n",
+ "|0 |-+--|0 |----|0 |-+--|1 |\n",
+ "`--' | `--' 1 `--' `--|2 |\n",
+ " | .--. .--. 2 |--|\n",
+ " `--|1 |-+--|0 |-+--|0 |\n",
+ " 2 `--' | `--' `--|2 |\n",
+ " | .--. 4 |--|\n",
+ " `--|1 |----|0 |\n",
+ " 3 `--' 5 `--'\n",
+ ".--. .--. .--. .--.\n",
+ "|1 |----|1 |----|1 |-+--|0 |\n",
+ "`--' 3 `--' 4 `--' |--|1 |\n",
+ " `--|2 |\n",
+ " 8 `--'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "indices = [\n",
+ " [0, 0, 0, 0, 0, 1, 1, 1],\n",
+ " [0, 0, 1, 1, 1, 1, 1, 1],\n",
+ " [0, 0, 0, 0, 1, 1, 1, 1],\n",
+ " [1, 2, 0, 2, 0, 0, 1, 2],\n",
+ "]\n",
+ "sparsities = [\"S\", \"C\", \"DC\"]\n",
+ "for sparsity in itertools.product(sparsities, sparsities, sparsities, [\"S\"]):\n",
+ " structure = \"-\".join(sparsity)\n",
+ " st = SparseTensor(indices, shape=(2, 2, 2, 3), structure=structure, as_binsparse=True)\n",
+ " if \"expanded\" in \" \".join(st.binsparse_groups):\n",
+ " continue\n",
+ " abbvs = []\n",
+ " for abbv, n in st.binsparse_structure:\n",
+ " abbvs.append(f\"{abbv}[{n}]\")\n",
+ " IPython.display.display(IPython.display.Markdown(f\"# `{'-'.join(abbvs)}`\"))\n",
+ " display_header(st)\n",
+ " IPython.display.display(st)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "44c45d8f-b109-4965-8dd8-d88122788164",
+ "metadata": {},
+ "source": [
+ "The visualization via `sparsetensorviz` admits an \"expanded sparse\" level denoted by `Sparse(n, expanded=1)`.\n",
+ "This is a Sparse level that is followed by a Dense level and has values duplicated to expand to match the dense level.\n",
+ "\n",
+ "This is not yet part of the standard binsparse formats (I think), because the regular `Sparse(n)` level without duplicates is preferred.\n",
+ "\n",
+ "Similarly, I don't know if these are valid TACO formats."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "34660901-7de1-40ea-b192-49b2c7a5ae3b",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/markdown": [
+ "# `SE[2]-D[1]-S[1]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Sparse(2, expanded=1), Dense(1), Sparse(1)]\n",
+ "\n",
+ "taco_structure = [compressed-nonunique, singleton, dense, compressed]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|SE[2] |D -----|S -----|\n",
+ ".------. .~~~. .---.\n",
+ "|i0 i1 |p2 ┊i2 ┊p3 |i3 |\n",
+ "`------' `~~~' `---'\n",
+ "========================\n",
+ ".-----. 0 .~~. 0 .--.\n",
+ "|0 0 |~~~~┊0 ┊-+--|1 |\n",
+ "`-----' 1 `~~' `--|2 |\n",
+ ".-----. .~~. 2 `--'\n",
+ "|0 0 |~~~~┊1 ┊)\n",
+ "`-----' 2 `~~'\n",
+ ".-----. .~~. 2 .--.\n",
+ "|0 1 |~~~~┊0 ┊-+--|0 |\n",
+ "`-----' 3 `~~' `--|2 |\n",
+ ".-----. .~~. 4 |--|\n",
+ "|0 1 |~~~~┊1 ┊----|0 |\n",
+ "`-----' 4 `~~' 5 `--'\n",
+ ".-----. .~~.\n",
+ "|1 1 |~~~~┊0 ┊)\n",
+ "`-----' 5 `~~'\n",
+ ".-----. .~~. 5 .--.\n",
+ "|1 1 |~~~~┊1 ┊-+--|0 |\n",
+ "`-----' 6 `~~' |--|1 |\n",
+ " `--|2 |\n",
+ " 8 `--'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "# `SE[1]-D[1]-S[2]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Sparse(1, expanded=1), Dense(1), Sparse(2)]\n",
+ "\n",
+ "taco_structure = [compressed-nonunique, dense, compressed-nonunique, singleton]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|SE |D -----|S[2] -----|\n",
+ ".---. .~~~. .------.\n",
+ "|i0 |p1 ┊i1 ┊p2 |i2 i3 |\n",
+ "`---' `~~~' `------'\n",
+ "========================\n",
+ ".--. 0 .~~. 0 .-----.\n",
+ "|0 |~~~~┊0 ┊-+--|0 1 |\n",
+ "`--' 1 `~~' | `-----'\n",
+ " | .-----.\n",
+ " `--|0 2 |\n",
+ " 2 `-----'\n",
+ ".--. .~~. .-----.\n",
+ "|0 |~~~~┊1 ┊-+--|0 0 |\n",
+ "`--' 2 `~~' | `-----'\n",
+ ".--. .~~. | .-----.\n",
+ "|1 |~~~~┊0 ┊)|--|0 2 |\n",
+ "`--' 3 `~~' | `-----'\n",
+ " | .-----.\n",
+ " `--|1 0 |\n",
+ " 5 `-----'\n",
+ ".--. .~~. 5 .-----.\n",
+ "|1 |~~~~┊1 ┊-+--|1 0 |\n",
+ "`--' 4 `~~' | `-----'\n",
+ " | .-----.\n",
+ " |--|1 1 |\n",
+ " | `-----'\n",
+ " | .-----.\n",
+ " `--|1 2 |\n",
+ " 8 `-----'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "# `SE[1]-D[2]-S[1]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Sparse(1, expanded=1), Dense(2), Sparse(1)]\n",
+ "\n",
+ "taco_structure = [compressed-nonunique, dense, dense, compressed]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|SE |D[2] -----|S -----|\n",
+ ".---. .~~ ~~~. .---.\n",
+ "|i0 |p1 ┊i1 i2 ┊p3 |i3 |\n",
+ "`---' `~~ ~~~' `---'\n",
+ "========================\n",
+ ".--. 0 .~~ ~~. 0 .--.\n",
+ "|0 |~~~~┊0 0 ┊-+--|1 |\n",
+ "`--' 1 `~~ ~~' `--|2 |\n",
+ " ~~. 2 `--'\n",
+ " 1 ┊)\n",
+ " ~~'\n",
+ ".--. .~~ ~~. 2 .--.\n",
+ "|0 |~~~~┊1 0 ┊-+--|0 |\n",
+ "`--' 2 `~~ ~~' `--|2 |\n",
+ " ~~. 4 |--|\n",
+ " 1 ┊----|0 |\n",
+ " ~~' 5 `--'\n",
+ ".--. .~~ ~~.\n",
+ "|1 |~~~~┊0 0 ┊)\n",
+ "`--' 3 `~~ ~~'\n",
+ " ~~. 5\n",
+ " 1 ┊)\n",
+ " ~~'\n",
+ ".--. .~~ ~~. 5\n",
+ "|1 |~~~~┊1 0 ┊)\n",
+ "`--' 4 `~~ ~~'\n",
+ " ~~. 5 .--.\n",
+ " 1 ┊-+--|0 |\n",
+ " ~~' |--|1 |\n",
+ " `--|2 |\n",
+ " 8 `--'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "# `SE[1]-D[1]-S[1]-S[1]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Sparse(1, expanded=1), Dense(1), Sparse(1), Sparse(1)]\n",
+ "\n",
+ "taco_structure = [compressed-nonunique, dense, compressed, compressed]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|SE |D -----|S -----|S -----|\n",
+ ".---. .~~~. .---. .---.\n",
+ "|i0 |p1 ┊i1 ┊p2 |i2 |p3 |i3 |\n",
+ "`---' `~~~' `---' `---'\n",
+ "=============================\n",
+ ".--. 0 .~~. 0 .--. 0 .--.\n",
+ "|0 |~~~~┊0 ┊----|0 |-+--|1 |\n",
+ "`--' 1 `~~' 1 `--' `--|2 |\n",
+ ".--. .~~. .--. 2 |--|\n",
+ "|0 |~~~~┊1 ┊-+--|0 |-+--|0 |\n",
+ "`--' 2 `~~' | `--' `--|2 |\n",
+ ".--. .~~. | .--. 4 |--|\n",
+ "|1 |~~~~┊0 ┊)`--|1 |----|0 |\n",
+ "`--' 3 `~~' 3 `--' 5 `--'\n",
+ ".--. .~~. 3 .--. .--.\n",
+ "|1 |~~~~┊1 ┊----|1 |-+--|0 |\n",
+ "`--' 4 `~~' 4 `--' |--|1 |\n",
+ " `--|2 |\n",
+ " 8 `--'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "# `D[1]-SE[1]-D[1]-S[1]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Dense(1), Sparse(1, expanded=1), Dense(1), Sparse(1)]\n",
+ "\n",
+ "taco_structure = [dense, compressed-nonunique, dense, compressed]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|D -|SE ----|D -----|S -----|\n",
+ ".~~~. .---. .~~~. .---.\n",
+ "┊i0 ┊p1 |i1 |p2 ┊i2 ┊p3 |i3 |\n",
+ "`~~~' `---' `~~~' `---'\n",
+ "=============================\n",
+ ".~~. 0 .--. 0 .~~. 0 .--.\n",
+ "┊0 ┊-+--|0 |~~~~┊0 ┊-+--|1 |\n",
+ "`~~' | `--' 1 `~~' `--|2 |\n",
+ " | .--. .~~. 2 `--'\n",
+ " |--|0 |~~~~┊1 ┊)\n",
+ " | `--' 2 `~~'\n",
+ " | .--. .~~. 2 .--.\n",
+ " |--|1 |~~~~┊0 ┊-+--|0 |\n",
+ " | `--' 3 `~~' `--|2 |\n",
+ " | .--. .~~. 4 |--|\n",
+ " `--|1 |~~~~┊1 ┊----|0 |\n",
+ " 4 `--' 4 `~~' 5 `--'\n",
+ ".~~. .--. .~~.\n",
+ "┊1 ┊-+--|1 |~~~~┊0 ┊)\n",
+ "`~~' | `--' 5 `~~'\n",
+ " | .--. .~~. 5 .--.\n",
+ " `--|1 |~~~~┊1 ┊-+--|0 |\n",
+ " 6 `--' 6 `~~' |--|1 |\n",
+ " `--|2 |\n",
+ " 8 `--'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "# `S[1]-SE[1]-D[1]-S[1]`"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "```\n",
+ "binsparse_groups = [Sparse(1), Sparse(1, expanded=1), Dense(1), Sparse(1)]\n",
+ "\n",
+ "taco_structure = [compressed, compressed-nonunique, dense, compressed]\n",
+ "\n",
+ "```"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ ""
+ ],
+ "text/plain": [
+ "|S -|SE ----|D -----|S -----|\n",
+ ".---. .---. .~~~. .---.\n",
+ "|i0 |p1 |i1 |p2 ┊i2 ┊p3 |i3 |\n",
+ "`---' `---' `~~~' `---'\n",
+ "=============================\n",
+ ".--. 0 .--. 0 .~~. 0 .--.\n",
+ "|0 |-+--|0 |~~~~┊0 ┊-+--|1 |\n",
+ "`--' | `--' 1 `~~' `--|2 |\n",
+ " | .--. .~~. 2 `--'\n",
+ " |--|0 |~~~~┊1 ┊)\n",
+ " | `--' 2 `~~'\n",
+ " | .--. .~~. 2 .--.\n",
+ " |--|1 |~~~~┊0 ┊-+--|0 |\n",
+ " | `--' 3 `~~' `--|2 |\n",
+ " | .--. .~~. 4 |--|\n",
+ " `--|1 |~~~~┊1 ┊----|0 |\n",
+ " 4 `--' 4 `~~' 5 `--'\n",
+ ".--. .--. .~~.\n",
+ "|1 |-+--|1 |~~~~┊0 ┊)\n",
+ "`--' | `--' 5 `~~'\n",
+ " | .--. .~~. 5 .--.\n",
+ " `--|1 |~~~~┊1 ┊-+--|0 |\n",
+ " 6 `--' 6 `~~' |--|1 |\n",
+ " `--|2 |\n",
+ " 8 `--'"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "for sparsity in itertools.product(sparsities, sparsities, sparsities, [\"S\"]):\n",
+ " structure = \"-\".join(sparsity)\n",
+ " st = SparseTensor(indices, shape=(2, 2, 2, 3), structure=structure, as_binsparse=True)\n",
+ " if \"expanded\" not in \" \".join(st.binsparse_groups):\n",
+ " continue\n",
+ " abbvs = []\n",
+ " for abbv, n in st.binsparse_structure:\n",
+ " abbvs.append(f\"{abbv}[{n}]\")\n",
+ " IPython.display.display(IPython.display.Markdown(f\"# `{'-'.join(abbvs)}`\"))\n",
+ " display_header(st)\n",
+ " IPython.display.display(st)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "07f40f47",
+ "metadata": {},
+ "source": [
+ "# voilà!"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "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.10.8"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/sparsetensorviz/requirements.txt b/sparsetensorviz/requirements.txt
index 5da331c..23d138a 100644
--- a/sparsetensorviz/requirements.txt
+++ b/sparsetensorviz/requirements.txt
@@ -1,2 +1,3 @@
numpy
pandas
+toolz
diff --git a/sparsetensorviz/setup.py b/sparsetensorviz/setup.py
index 3eece6e..d96b555 100644
--- a/sparsetensorviz/setup.py
+++ b/sparsetensorviz/setup.py
@@ -39,6 +39,9 @@
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3 :: Only",
"Intended Audience :: Developers",
"Intended Audience :: Science/Research",
diff --git a/sparsetensorviz/sparsetensorviz/_bundle_binsparse.py b/sparsetensorviz/sparsetensorviz/_bundle_binsparse.py
new file mode 100644
index 0000000..97d68fe
--- /dev/null
+++ b/sparsetensorviz/sparsetensorviz/_bundle_binsparse.py
@@ -0,0 +1,227 @@
+import re
+
+import toolz
+
+from ._core import SparseTensor, unabbreviate
+
+
+def num(match, name, tokenlength=2):
+ s = match.group(name)
+ if s is None:
+ return 0
+ return len(s) // tokenlength
+
+
+def trim(ma, s):
+ start, stop = ma.span(0)
+ assert start == 0
+ return s[stop:]
+
+
+class MatcherBase:
+ @classmethod
+ def match(cls, s):
+ return cls.pattern.match(s)
+
+
+class AllSparse(MatcherBase):
+ """[S-]S"""
+
+ pattern = re.compile("^S(?P(-S)*)$")
+
+ def __new__(cls, ma, s, *, abbreviate=False):
+ numS = num(ma, "S") + 1
+ if abbreviate:
+ return [("S", numS)]
+ return [f"Sparse({numS})"]
+
+
+class SparseFull(MatcherBase):
+ """[S-][F-]F"""
+
+ pattern = re.compile("^(?P(S-)*)(?P(F-)*)F$")
+
+ def __new__(cls, ma, s, *, abbreviate=False):
+ numS = num(ma, "S")
+ numF = num(ma, "F") + 1
+ if abbreviate:
+ return [("S", numS), ("D", numF)]
+ return [f"Sparse({numS})", f"Dense({numF})"]
+
+
+class Sparse(MatcherBase):
+ """[S-]DC-"""
+
+ pattern = re.compile("^(?P(S-)*)DC-")
+
+ def __new__(cls, ma, s, *, abbreviate=False):
+ numS = num(ma, "S") + 1
+ if abbreviate:
+ return [("S", numS)]
+ return [f"Sparse({numS})"]
+
+
+class Dense(MatcherBase):
+ """[C-]C-"""
+
+ pattern = re.compile("^(?P(C-)+)")
+
+ def __new__(cls, ma, s, *, abbreviate=False):
+ numC = num(ma, "C")
+ if abbreviate:
+ return [("D", numC)]
+ return [f"Dense({numC})"]
+
+
+class SparseCompressed(MatcherBase):
+ """[S-]S-[C-]C-"""
+
+ pattern = re.compile("^(?P(S-)+)(?P(C-)+)")
+
+ def __new__(cls, ma, s, *, abbreviate=False):
+ numS = num(ma, "S")
+ numC = num(ma, "C")
+ if abbreviate:
+ return [("SE", numS), ("D", numC)]
+ return [f"Sparse({numS}, expanded=1)", f"Dense({numC})"]
+
+
+def to_binsparse_groups(s, abbreviate=False):
+ if isinstance(s, SparseTensor):
+ s = s.abbreviation
+ elif not isinstance(s, str):
+ raise TypeError(
+ f"s argument to to_bundled_groups should be str or SparseTensor; got {type(s)}"
+ )
+ if "-" not in s:
+ s = "-".join(s)
+ orig_s = s
+ rv = []
+ matchers = [
+ Sparse, # [S-]DC- -> Sparse(N)
+ Dense, # [C-]C- -> Dense(N)
+ SparseCompressed, # [S-]S-[C-]C- -> Sparse(M, expanded=1), Dense(N)
+ # Terminal patterns
+ AllSparse, # [S-]S$ -> Sparse(N)
+ SparseFull, # [S-][F-]F$ -> Sparse(M), Dense(N)
+ ]
+ while s:
+ for matcher in matchers:
+ if ma := matcher.match(s):
+ rv.extend(matcher(ma, s, abbreviate=abbreviate))
+ s = trim(ma, s)
+ break
+ else: # pragma: no cover
+ raise ValueError(f"Invalid structure {orig_s!r}; unable to handle {s!r}")
+ return rv
+
+
+def structure_from_binsparse(binsparse_structure):
+ """Convert user-input binsparse stucture to an internal structure"""
+ # This is super quick and sloppy! It allows some very sloppy input
+ if not isinstance(binsparse_structure, str):
+ text = "-".join(
+ val if isinstance(val, str) else "".join(map(str, val)) for val in binsparse_structure
+ )
+ else:
+ text = binsparse_structure
+ # Step 1: tokenize input string
+ token_map = {
+ "sparseexpanded": "SE",
+ "expandedsparse": "SE",
+ "sparse": "S",
+ "se": "SE",
+ "es": "SE",
+ "s": "S",
+ "dense": "D",
+ "d": "D",
+ "expanded": "E", # to handle `Sparse(3, expanded=1)`
+ }
+ ignore = "- []()_=,"
+ tokens = []
+ t = text.lower()
+ while t:
+ cont = False
+ if t[0] in ignore:
+ t = t[1:]
+ continue
+ for k, v in token_map.items():
+ if t.startswith(k):
+ tokens.append(v)
+ t = t[len(k) :]
+ cont = True
+ break
+ if cont:
+ continue
+ for i, c in enumerate(t):
+ if not c.isdecimal():
+ if i == 0:
+ raise ValueError(f"Bad input: {binsparse_structure}")
+ tokens.append(int(t[:i]))
+ t = t[i:]
+ cont = True
+ break
+ if cont:
+ continue
+ if t.isdecimal():
+ tokens.append(int(t))
+ break
+ raise ValueError(f"Bad input: {binsparse_structure}")
+
+ # Step 2: process tokens to form canonical binsparse format (abbreviated)
+ levels = []
+ it = toolz.sliding_window(4, tokens + [None] * 3)
+ for cur, n1, n2, n3 in it:
+ if cur == "D":
+ if isinstance(n1, int):
+ next(it)
+ else:
+ n1 = 1
+ levels.append(("D", n1))
+ elif cur == "S":
+ if isinstance(n1, int):
+ next(it)
+ else:
+ n1, n2, n3 = 1, n1, n2
+ if n2 != "E":
+ levels.append(("S", n1))
+ else:
+ next(it)
+ if isinstance(n3, int):
+ if n3 != 1:
+ raise ValueError(f"Bad input: {binsparse_structure}")
+ next(it)
+ levels.append(("SE", n3))
+ elif cur == "SE":
+ if isinstance(n1, int):
+ next(it)
+ if n1 != 1:
+ raise ValueError(f"Bad input: {binsparse_structure}")
+ else:
+ n1 = 1
+ levels.append(("SE", n1))
+ else:
+ raise ValueError(f"Bad input: {binsparse_structure}")
+
+ for i, (cur, n) in enumerate(levels):
+ if cur == "SE":
+ if len(levels) == i + 1 or levels[i + 1][0] != "D":
+ raise ValueError(f"Sparse({n}, expanded=1) level must be followed by a Dense level")
+ if n != 1:
+ raise ValueError(f"Bad input: {binsparse_structure}")
+ if levels[-1][0] == "D":
+ raise ValueError("Sparse structure must end in Sparse level; got Dense")
+
+ # Step 3: convert to internal levels
+ converted_levels = []
+ for level, n in levels:
+ if level == "D":
+ converted_levels.extend(["C"] * n)
+ elif level == "S":
+ converted_levels.extend(["S"] * (n - 1))
+ converted_levels.append("DC")
+ else: # level == "SE"
+ converted_levels.extend(["S"] * n)
+ assert converted_levels[-1] == "DC"
+ converted_levels[-1] = "S"
+ return unabbreviate("-".join(converted_levels))
diff --git a/sparsetensorviz/sparsetensorviz/_core.py b/sparsetensorviz/sparsetensorviz/_core.py
index 68d91fd..4f1d202 100644
--- a/sparsetensorviz/sparsetensorviz/_core.py
+++ b/sparsetensorviz/sparsetensorviz/_core.py
@@ -18,13 +18,32 @@ def issorted(array):
class SparseTensor:
@classmethod
- def from_taco(cls, arrays, shape=None, structure=None, *, group_indices=False):
+ def from_taco(
+ cls, arrays, shape=None, structure=None, *, group_indices=False, as_binsparse=False
+ ):
if structure is not None:
structure = _from_taco(structure)
- return cls(arrays, shape=shape, structure=structure, group_indices=group_indices)
+ return cls(
+ arrays,
+ shape=shape,
+ structure=structure,
+ group_indices=group_indices,
+ as_binsparse=as_binsparse,
+ )
- def __init__(self, arrays, shape=None, structure=None, *, group_indices=False):
+ @classmethod
+ def from_binsparse(cls, arrays, shape=None, structure=None):
+ from ._bundle_binsparse import structure_from_binsparse
+
+ if structure is not None:
+ structure = structure_from_binsparse(structure)
+ return cls(arrays, shape=shape, structure=structure, as_binsparse=True)
+
+ def __init__(
+ self, arrays, shape=None, structure=None, *, group_indices=False, as_binsparse=False
+ ):
self.group_indices = group_indices
+ self.as_binsparse = as_binsparse
if not isinstance(arrays, (list, tuple)):
raise TypeError("arrays argument must be a list or tuple of numpy arrays")
if not arrays:
@@ -327,21 +346,37 @@ def bundled_groups(self):
return to_bundled_groups(self)
- def _repr_svg_(self, *, as_taco=False, as_groups=None):
+ @property
+ def binsparse_groups(self):
+ from ._bundle_binsparse import to_binsparse_groups
+
+ return to_binsparse_groups(self)
+
+ @property
+ def binsparse_structure(self):
+ from ._bundle_binsparse import to_binsparse_groups
+
+ return to_binsparse_groups(self, abbreviate=True)
+
+ def _repr_svg_(self, *, as_taco=False, as_groups=None, as_binsparse=None):
try:
from ._formatting import to_svg
except ImportError:
return
if as_groups is None:
as_groups = self.group_indices
- return to_svg(self, as_taco=as_taco, as_groups=as_groups)
+ if as_binsparse is None:
+ as_binsparse = self.as_binsparse
+ return to_svg(self, as_taco=as_taco, as_groups=as_groups, as_binsparse=as_binsparse)
- def __repr__(self, *, as_taco=False, as_groups=None):
+ def __repr__(self, *, as_taco=False, as_groups=None, as_binsparse=None):
from ._formatting import to_text
if as_groups is None:
as_groups = self.group_indices
- return to_text(self, as_taco=as_taco, as_groups=as_groups)
+ if as_binsparse is None:
+ as_binsparse = self.as_binsparse
+ return to_text(self, as_taco=as_taco, as_groups=as_groups, as_binsparse=as_binsparse)
class TacoView:
diff --git a/sparsetensorviz/sparsetensorviz/_formatting.py b/sparsetensorviz/sparsetensorviz/_formatting.py
index 9dc4fa6..3387886 100644
--- a/sparsetensorviz/sparsetensorviz/_formatting.py
+++ b/sparsetensorviz/sparsetensorviz/_formatting.py
@@ -274,7 +274,11 @@ def draw_pointer(canvas, x, y, w, ptr, *, center_right=False, skip_if_nonempty=F
canvas[y][x + i] = c
-def to_text(self, *, squared=False, compact=None, as_taco=False, as_groups=False):
+def to_text(
+ self, *, squared=False, compact=None, as_taco=False, as_groups=False, as_binsparse=True
+):
+ if as_binsparse:
+ as_groups = True
indices = self._indices
pointers = self._pointers
index_widths, pointers_widths, xoffsets, yoffsets = get_layout(
@@ -332,10 +336,13 @@ def to_text(self, *, squared=False, compact=None, as_taco=False, as_groups=False
i -= 1
draw_box(header, x, 0, w + 1, f"i{i} ", square=squared, dashed=index is None)
for i, (x, w) in enumerate(zip(xoffsets[1::2], pointers_widths)):
+ if as_binsparse:
+ i += 1
draw_pointer(header, x + 1, 1, w - 2, f"p{i}", center_right=False)
draw_line(header, 0, 3, len("".join(header[0]).rstrip()), double=True)
# Very top: group columns together and display dimension type
+ # (we'll redo the top for `as_binsparse` below)
w = "".join(header[1]).rindex("|")
top = ["-"] * (w + 1)
top[w] = "|"
@@ -386,11 +393,44 @@ def trim(row, start, stop):
for start, stop in reversed(trim_ranges):
combined = [trim(row, start, stop) for row in combined]
+ if as_binsparse:
+ # Redo the very top
+ top = ["-"] * len(combined[0])
+ bars = [0]
+ bars.extend(i - 1 for i, c in enumerate(combined[2]) if c == "p")
+ for x, (abbv, n) in zip(bars, self.binsparse_structure):
+ top[x] = "|"
+ x += 1
+ for c in abbv:
+ top[x] = c
+ x += 1
+ if n != 1:
+ top[x] = "["
+ x += 1
+ for c in str(n):
+ top[x] = c
+ x += 1
+ top[x] = "]"
+ x += 1
+ top[x] = " "
+ top[-1] = "|"
+
+ combined[0] = "".join(top)
+
return "\n".join(combined)
-def to_svg(self, *, squared=False, compact=None, as_taco=False, as_groups=False):
+def to_svg(
+ self, *, squared=False, compact=None, as_taco=False, as_groups=False, as_binsparse=False
+):
from sphinxcontrib.svgbob._svgbob import to_svg as _to_svg
- text = to_text(self, squared=squared, compact=compact, as_taco=as_taco, as_groups=as_groups)
+ text = to_text(
+ self,
+ squared=squared,
+ compact=compact,
+ as_taco=as_taco,
+ as_groups=as_groups,
+ as_binsparse=as_binsparse,
+ )
return _to_svg(text)