Skip to content

Instantly share code, notes, and snippets.

@sklam
Created October 23, 2023 22:10
Show Gist options
  • Save sklam/5e5737137d48d6e5b816d14a90076f1d to your computer and use it in GitHub Desktop.
Save sklam/5e5737137d48d6e5b816d14a90076f1d to your computer and use it in GitHub Desktop.
LoopNest, Shape, ndim inference
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "6ac152c9",
"metadata": {},
"outputs": [],
"source": [
"from __future__ import annotations"
]
},
{
"cell_type": "markdown",
"id": "f766d37c",
"metadata": {},
"source": [
"## Define LoopNestAPI and ShapeAPI as Dialects"
]
},
{
"cell_type": "markdown",
"id": "68080f3b",
"metadata": {},
"source": [
"Pretend that Numba can define Pythonic API that acts like _dialects_/_DSL_. \n",
"We define `LoopNestAPI` and `ShapeAPI` _dialects_. These dialects are just like a normal Python class but will allow Numba to capture their high-level semantic in the compiler for analysis and transformation."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "f661a8bb",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Intel MKL WARNING: Support of Intel(R) Streaming SIMD Extensions 4.2 (Intel(R) SSE4.2) enabled only processors has been deprecated. Intel oneAPI Math Kernel Library 2025.0 will require Intel(R) Advanced Vector Extensions (Intel(R) AVX) instructions.\n",
"Intel MKL WARNING: Support of Intel(R) Streaming SIMD Extensions 4.2 (Intel(R) SSE4.2) enabled only processors has been deprecated. Intel oneAPI Math Kernel Library 2025.0 will require Intel(R) Advanced Vector Extensions (Intel(R) AVX) instructions.\n"
]
}
],
"source": [
"from __future__ import annotations\n",
"\n",
"import numpy as np\n",
"import itertools\n",
"\n",
"\n",
"class LoopNestAPI:\n",
" dim: tuple[int, ...]\n",
" inner: LoopNestAPI | None\n",
"\n",
" def __init__(self, dim, inner=None):\n",
" self.dim = dim\n",
" self.inner = inner\n",
"\n",
" @staticmethod\n",
" def from_tuple(args):\n",
" def expand(*args):\n",
" if not args:\n",
" return None\n",
" [head, *tail] = args\n",
" return LoopNestAPI(head, inner=expand(*tail))\n",
" return expand(*args)\n",
"\n",
" def __iter__(self):\n",
" dims = self.get_dims()\n",
" return iter(itertools.product(*map(lambda x: list(range(x)), dims)))\n",
"\n",
" def get_dims(self):\n",
" if self.inner is None:\n",
" return [self.dim]\n",
" else:\n",
" return [self.dim] + self.inner.get_dims()\n",
"\n",
" def reduce(self, fn, init):\n",
" res = init\n",
" for idx in self:\n",
" res = fn(res, idx)\n",
" return res\n",
"\n",
"\n",
"class ShapeAPI:\n",
" dims: tuple[int,...]\n",
"\n",
" def __init__(self, dims):\n",
" self.dims = tuple(dims)\n",
"\n",
" def deselect(self, axis):\n",
" dims = self.dims\n",
" return ShapeAPI([dims[i] for i in range(len(dims)) if i not in axis])\n",
"\n",
" def select(self, axis):\n",
" dims = self.dims\n",
" return ShapeAPI([dims[i] for i in range(len(dims)) if i in axis])\n",
"\n",
" def to_tuple(self):\n",
" return tuple(self.dims)"
]
},
{
"cell_type": "markdown",
"id": "a5cd52a8",
"metadata": {},
"source": [
"## Define the `np.linalg.norm` impl with the Dialects\n",
"\n",
"We will use a `np.linalge.norm` implementation as demo."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "cc6ba21f",
"metadata": {},
"outputs": [],
"source": [
"def linalg_norm_loopnest(X, axis):\n",
" # # If X ndim>=3 and axis is a 2-tuple\n",
" assert X.ndim >= 3\n",
" assert len(axis) == 2\n",
"\n",
" # peel off the outer shape for result array\n",
" outshape = ShapeAPI(X.shape).deselect(axis).to_tuple()\n",
" # get only the inner shape for reduction\n",
" reduce_axis = ShapeAPI(X.shape).select(axis).to_tuple()\n",
"\n",
" # alloc result array\n",
" res = np.zeros(outshape, dtype=X.dtype)\n",
"\n",
" # loop over the result array indices\n",
" for k in LoopNestAPI.from_tuple(outshape):\n",
" # reduction\n",
" def inner(carry, i):\n",
" idx = (*i, *k)\n",
" x = X[idx]\n",
" return carry + (x.conj() * x).real\n",
"\n",
" tmp = LoopNestAPI.from_tuple(reduce_axis).reduce(inner, init=0.0)\n",
" res[k] = np.sqrt(tmp)\n",
"\n",
" return res"
]
},
{
"cell_type": "markdown",
"id": "60a82062",
"metadata": {},
"source": [
"## Define a low-level impl\n",
"\n",
"The following is a way to implement it in the low-level that current Numba requires."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "670ad3fb",
"metadata": {},
"outputs": [],
"source": [
"def linalg_norm_low_level(X, axis):\n",
" # # If X ndim>=3 and axis is a 2-tuple\n",
" assert X.ndim >= 3\n",
" assert len(axis) == 2\n",
" outdim = [dim for dim in range(X.ndim) if dim not in axis]\n",
" outshape = tuple(np.asarray(X.shape)[outdim])\n",
"\n",
" res = np.zeros(outshape, dtype=X.dtype)\n",
" row_axis, col_axis = axis\n",
" for k in np.ndindex(outshape):\n",
" tmp = 0.0\n",
" for row in range(X.shape[row_axis]):\n",
" for col in range(X.shape[col_axis]):\n",
" idx = (row, col, *k)\n",
" x = X[idx]\n",
" tmp += (x.conj() * x).real\n",
" res[k] = np.sqrt(tmp)\n",
" return res"
]
},
{
"cell_type": "markdown",
"id": "f54bbd08",
"metadata": {},
"source": [
"## Test\n",
"\n",
"Just making sure the two implementation are working"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "d97edf57",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"=========================checking linalg_norm_low_level=========================\n",
"(3, 4) (3, 4)\n",
"=========================checking linalg_norm_loopnest==========================\n",
"(3, 4) (3, 4)\n"
]
}
],
"source": [
"np.random.seed(23)\n",
"X = np.random.random((3, 2, 3, 4))\n",
"axis = (0, 1)\n",
"expect = np.linalg.norm(X, axis=axis)\n",
"impl_list = [\n",
" linalg_norm_low_level,\n",
" linalg_norm_loopnest,\n",
"]\n",
"for impl in impl_list:\n",
" print(f'checking {impl.__name__}'.center(80, '='))\n",
" res = impl(X, axis=axis)\n",
" print(expect.shape, res.shape)\n",
" np.testing.assert_almost_equal(res, expect)"
]
},
{
"cell_type": "markdown",
"id": "7e4fe1e5",
"metadata": {},
"source": [
"# Propagate ndim using EGG"
]
},
{
"cell_type": "markdown",
"id": "756d00b2",
"metadata": {},
"source": [
"This is modelling the `ShapeAPI` dialect. `LoopNestAPI` needs to know the size of the input tuple to work correctly.\n",
"\n",
"In practice, we want users to define these rules so that any dialect can easily define custom inference logic."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "c01c60c7",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The result array will have nd = 1\n",
"The inner reduction loop will have nd = 2\n"
]
}
],
"source": [
"\n",
"from egglog import EGraph, Expr, String, i64, i64Like, var, rewrite, set_, rule, union, eq, StringLike, vars_, run\n",
"from typing import Union\n",
"\n",
"\n",
"egraph = EGraph()\n",
"\n",
"\n",
"# The following EGraph.class_ are like IR nodes but they are available for pattern matching\n",
"\n",
"@egraph.class_\n",
"class Tuple(Expr):\n",
" def __init__(self, nd: i64Like):\n",
" ...\n",
" \n",
" @classmethod\n",
" def symbolic(cls, name: StringLike) -> Tuple:\n",
" ...\n",
" \n",
"\n",
"@egraph.class_\n",
"class Shape(Expr):\n",
" def __init__(self, tup: Tuple):\n",
" ...\n",
" \n",
" def deselect(self, axis: Tuple) -> Shape:\n",
" ...\n",
" \n",
" def select(self, axis: Tuple) -> Shape:\n",
" ...\n",
"\n",
" def to_tuple(self) -> Tuple:\n",
" ...\n",
"\n",
" \n",
"# The following EGraph.functions are for inferring the ndim.\n",
" \n",
"@egraph.function\n",
"def ndim(x: Shape) -> i64:\n",
" ...\n",
" \n",
"@egraph.function\n",
"def length(x: Tuple) -> i64:\n",
" ...\n",
"\n",
"\n",
"# Below, we define the logic rules for the inference\n",
"[shape, shape1] = vars_(\"shape, shape1\", Shape)\n",
"[tup] = vars_(\"tup\", Tuple)\n",
"[n, p, q] = vars_(\"n, p, q\", i64)\n",
"egraph.register(\n",
" \n",
" # -- rule for Tuple length\n",
" rule( Tuple(n) ).then( set_( length(Tuple(n)) ).to( n ) ),\n",
" \n",
" # -- rule for Shape ctor\n",
" # Given p == length(tup)\n",
" # shape == Shape(tup)\n",
" # Then ndim(shape) = p\n",
" rule( \n",
" eq(p).to(length(tup)),\n",
" eq(shape).to(Shape(tup))\n",
" ).then( set_( ndim(shape) ).to( p ) ),\n",
" \n",
" # -- rule for Shape.deselect\n",
" # Given p == ndim(shape)\n",
" # q == length(tup)\n",
" # shape1 == shape.deselect(tup)\n",
" # Then ndim(shape1) = (p - q)\n",
" rule( \n",
" eq(p).to(ndim(shape)),\n",
" eq(q).to(length(tup)),\n",
" eq(shape1).to(shape.deselect(tup))\n",
" ).then( set_( ndim(shape1) ).to( p - q ) ),\n",
" \n",
" # -- rule for Shape.select\n",
" # Given q == length(tup)\n",
" # shape1 == shape.select(tup)\n",
" # Then ndim(shape1) = q\n",
" rule(\n",
" eq(q).to(length(tup)),\n",
" eq(shape1).to(shape.select(tup))\n",
" ).then( set_( ndim(shape1) ).to( q ) ),\n",
" \n",
" # -- rule for Shape.to_tuple\n",
" # Given p == ndim(shape)\n",
" # tup == shape.to_tuple()\n",
" # Then length(tup) = p\n",
" rule(\n",
" eq(p).to(ndim(shape)),\n",
" eq(tup).to(shape.to_tuple())\n",
" ).then( set_( length(tup) ).to( p ) ),\n",
" \n",
")\n",
"\n",
"# Here, we setup facts for the linalg.norm function call\n",
"\n",
"# Facts about the arguments\n",
"X_shape = egraph.let(\"X_shape\", Tuple(3))\n",
"axis = egraph.let(\"axis\", Tuple(2))\n",
"\n",
"# Expressions to evaluate\n",
"deselect_expr = Shape(X_shape).deselect(axis).to_tuple()\n",
"select_expr = Shape(X_shape).select(Tuple(2)).to_tuple()\n",
"egraph.let(\"y\", deselect_expr )\n",
"egraph.let(\"z\", select_expr )\n",
"\n",
"# Run\n",
"egraph.run(5)\n",
"\n",
"# Extract\n",
"print(\"The result array will have nd =\", egraph.extract(length(deselect_expr)))\n",
"print(\"The inner reduction loop will have nd =\", egraph.extract(length(select_expr)))"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "f722cd96",
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
"<?xml-stylesheet href=\"/var/folders/6k/r_fxgcx50z3gkdnckfxkfrv40000gp/T/graphviz-styles.css\" type=\"text/css\"?>\n",
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
"<!-- Generated by graphviz version 2.50.0 (0)\n",
" -->\n",
"<!-- Pages: 1 -->\n",
"<svg width=\"557pt\" height=\"388pt\"\n",
" viewBox=\"0.00 0.00 557.00 388.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
"<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 384)\">\n",
"<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-384 553,-384 553,4 -4,4\"/>\n",
"<g id=\"clust22\" class=\"cluster\">\n",
"<title>outer_cluster_i64&#45;17615343019692007359</title>\n",
"</g>\n",
"<g id=\"clust23\" class=\"cluster\">\n",
"<title>cluster_i64&#45;17615343019692007359</title>\n",
"<path fill=\"#fb8072\" stroke=\"black\" stroke-dasharray=\"5,2\" d=\"M23,-90C23,-90 175,-90 175,-90 181,-90 187,-96 187,-102 187,-102 187,-120 187,-120 187,-126 181,-132 175,-132 175,-132 23,-132 23,-132 17,-132 11,-126 11,-120 11,-120 11,-102 11,-102 11,-96 17,-90 23,-90\"/>\n",
"</g>\n",
"<g id=\"clust25\" class=\"cluster\">\n",
"<title>outer_cluster_i64&#45;11743562013128004906</title>\n",
"</g>\n",
"<g id=\"clust26\" class=\"cluster\">\n",
"<title>cluster_i64&#45;11743562013128004906</title>\n",
"<path fill=\"#fb8072\" stroke=\"black\" stroke-dasharray=\"5,2\" d=\"M316,-248C316,-248 526,-248 526,-248 532,-248 538,-254 538,-260 538,-260 538,-278 538,-278 538,-284 532,-290 526,-290 526,-290 316,-290 316,-290 310,-290 304,-284 304,-278 304,-278 304,-260 304,-260 304,-254 310,-248 316,-248\"/>\n",
"</g>\n",
"<g id=\"clust28\" class=\"cluster\">\n",
"<title>outer_cluster_i64&#45;5871781006564002453</title>\n",
"</g>\n",
"<g id=\"clust29\" class=\"cluster\">\n",
"<title>cluster_i64&#45;5871781006564002453</title>\n",
"<path fill=\"#fb8072\" stroke=\"black\" stroke-dasharray=\"5,2\" d=\"M39,-327C39,-327 191,-327 191,-327 197,-327 203,-333 203,-339 203,-339 203,-357 203,-357 203,-363 197,-369 191,-369 191,-369 39,-369 39,-369 33,-369 27,-363 27,-357 27,-357 27,-339 27,-339 27,-333 33,-327 39,-327\"/>\n",
"</g>\n",
"<g id=\"clust1\" class=\"cluster\">\n",
"<title>outer_cluster_5</title>\n",
"</g>\n",
"<g id=\"clust2\" class=\"cluster\">\n",
"<title>cluster_5</title>\n",
"<path fill=\"#ffffb3\" stroke=\"black\" stroke-dasharray=\"5,2\" d=\"M268,-169C268,-169 342,-169 342,-169 348,-169 354,-175 354,-181 354,-181 354,-199 354,-199 354,-205 348,-211 342,-211 342,-211 268,-211 268,-211 262,-211 256,-205 256,-199 256,-199 256,-181 256,-181 256,-175 262,-169 268,-169\"/>\n",
"</g>\n",
"<g id=\"clust4\" class=\"cluster\">\n",
"<title>outer_cluster_2</title>\n",
"</g>\n",
"<g id=\"clust5\" class=\"cluster\">\n",
"<title>cluster_2</title>\n",
"<path fill=\"#ffffb3\" stroke=\"black\" stroke-dasharray=\"5,2\" d=\"M205,-90C205,-90 291,-90 291,-90 297,-90 303,-96 303,-102 303,-102 303,-120 303,-120 303,-126 297,-132 291,-132 291,-132 205,-132 205,-132 199,-132 193,-126 193,-120 193,-120 193,-102 193,-102 193,-96 199,-90 205,-90\"/>\n",
"</g>\n",
"<g id=\"clust7\" class=\"cluster\">\n",
"<title>outer_cluster_3</title>\n",
"</g>\n",
"<g id=\"clust8\" class=\"cluster\">\n",
"<title>cluster_3</title>\n",
"<path fill=\"#ffffb3\" stroke=\"black\" stroke-dasharray=\"5,2\" d=\"M148,-169C148,-169 238,-169 238,-169 244,-169 250,-175 250,-181 250,-181 250,-199 250,-199 250,-205 244,-211 238,-211 238,-211 148,-211 148,-211 142,-211 136,-205 136,-199 136,-199 136,-181 136,-181 136,-175 142,-169 148,-169\"/>\n",
"</g>\n",
"<g id=\"clust10\" class=\"cluster\">\n",
"<title>outer_cluster_1</title>\n",
"</g>\n",
"<g id=\"clust11\" class=\"cluster\">\n",
"<title>cluster_1</title>\n",
"<path fill=\"#bebada\" stroke=\"black\" stroke-dasharray=\"5,2\" d=\"M356,-90C356,-90 438,-90 438,-90 444,-90 450,-96 450,-102 450,-102 450,-120 450,-120 450,-126 444,-132 438,-132 438,-132 356,-132 356,-132 350,-132 344,-126 344,-120 344,-120 344,-102 344,-102 344,-96 350,-90 356,-90\"/>\n",
"</g>\n",
"<g id=\"clust13\" class=\"cluster\">\n",
"<title>outer_cluster_6</title>\n",
"</g>\n",
"<g id=\"clust14\" class=\"cluster\">\n",
"<title>cluster_6</title>\n",
"<path fill=\"#bebada\" stroke=\"black\" stroke-dasharray=\"5,2\" d=\"M198,-248C198,-248 286,-248 286,-248 292,-248 298,-254 298,-260 298,-260 298,-278 298,-278 298,-284 292,-290 286,-290 286,-290 198,-290 198,-290 192,-290 186,-284 186,-278 186,-278 186,-260 186,-260 186,-254 192,-248 198,-248\"/>\n",
"</g>\n",
"<g id=\"clust16\" class=\"cluster\">\n",
"<title>outer_cluster_0</title>\n",
"</g>\n",
"<g id=\"clust17\" class=\"cluster\">\n",
"<title>cluster_0</title>\n",
"<path fill=\"#bebada\" stroke=\"black\" stroke-dasharray=\"5,2\" d=\"M115,-11C115,-11 197,-11 197,-11 203,-11 209,-17 209,-23 209,-23 209,-41 209,-41 209,-47 203,-53 197,-53 197,-53 115,-53 115,-53 109,-53 103,-47 103,-41 103,-41 103,-23 103,-23 103,-17 109,-11 115,-11\"/>\n",
"</g>\n",
"<g id=\"clust19\" class=\"cluster\">\n",
"<title>outer_cluster_4</title>\n",
"</g>\n",
"<g id=\"clust20\" class=\"cluster\">\n",
"<title>cluster_4</title>\n",
"<path fill=\"#bebada\" stroke=\"black\" stroke-dasharray=\"5,2\" d=\"M68,-248C68,-248 156,-248 156,-248 162,-248 168,-254 168,-260 168,-260 168,-278 168,-278 168,-284 162,-290 156,-290 156,-290 68,-290 68,-290 62,-290 56,-284 56,-278 56,-278 56,-260 56,-260 56,-254 62,-248 68,-248\"/>\n",
"</g>\n",
"<!-- Shape_select&#45;7784354942592584825&#45;&gt;Shape___init__&#45;0 -->\n",
"<g id=\"edge1\" class=\"edge\">\n",
"<title>Shape_select&#45;7784354942592584825:s&#45;&gt;Shape___init__&#45;0</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M282,-174C282,-159.26 274.52,-144.71 266.58,-133.41\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"269.95,-134.73 265.41,-132 267.25,-136.97 269.95,-134.73\"/>\n",
"</g>\n",
"<!-- Shape_select&#45;7784354942592584825&#45;&gt;Tuple___init__&#45;11743562013128004906 -->\n",
"<g id=\"edge2\" class=\"edge\">\n",
"<title>Shape_select&#45;7784354942592584825:s&#45;&gt;Tuple___init__&#45;11743562013128004906</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M328,-174C328,-157.83 338.61,-144.81 351.51,-134.96\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"352.56,-136.35 355.57,-132 350.5,-133.52 352.56,-136.35\"/>\n",
"</g>\n",
"<!-- Shape___init__&#45;0&#45;&gt;Tuple___init__&#45;17615343019692007359 -->\n",
"<g id=\"edge3\" class=\"edge\">\n",
"<title>Shape___init__&#45;0:s&#45;&gt;Tuple___init__&#45;17615343019692007359</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M248,-95C248,-73.24 230.09,-58.62 210.07,-49.06\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"213.76,-50.3 209,-47.98 211.28,-52.76 213.76,-50.3\"/>\n",
"</g>\n",
"<!-- Tuple___init__&#45;11743562013128004906&#45;&gt;i64&#45;11743562013128004906 -->\n",
"<g id=\"edge6\" class=\"edge\">\n",
"<title>Tuple___init__&#45;11743562013128004906:s&#45;&gt;i64&#45;11743562013128004906</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M398,-95C398,-73.44 429,-83.4 446.5,-96 494.95,-130.88 505.4,-207.46 507.54,-245.56\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"505.53,-243.21 507.8,-248 509.01,-242.84 505.53,-243.21\"/>\n",
"</g>\n",
"<!-- Tuple___init__&#45;17615343019692007359&#45;&gt;i64&#45;17615343019692007359 -->\n",
"<g id=\"edge8\" class=\"edge\">\n",
"<title>Tuple___init__&#45;17615343019692007359:s&#45;&gt;i64&#45;17615343019692007359</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M157,-16C157,5.56 190.41,-1.6 205.5,-17 225.77,-37.7 202.12,-68.59 181.3,-89.01\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"183.03,-86.09 179.46,-90 184.69,-89.17 183.03,-86.09\"/>\n",
"</g>\n",
"<!-- Shape_deselect&#45;7784354942592584825&#45;&gt;Shape___init__&#45;0 -->\n",
"<g id=\"edge4\" class=\"edge\">\n",
"<title>Shape_deselect&#45;7784354942592584825:s&#45;&gt;Shape___init__&#45;0</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M167,-174C167,-157.23 178.09,-144.38 192.05,-134.85\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"193.22,-136.18 196.47,-132 191.32,-133.24 193.22,-136.18\"/>\n",
"</g>\n",
"<!-- Shape_deselect&#45;7784354942592584825&#45;&gt;Tuple___init__&#45;11743562013128004906 -->\n",
"<g id=\"edge5\" class=\"edge\">\n",
"<title>Shape_deselect&#45;7784354942592584825:s&#45;&gt;Tuple___init__&#45;11743562013128004906</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M220,-174C220,-160.87 290.27,-139.67 342.25,-125.79\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"341.07,-128.88 344,-124.47 338.96,-126.09 341.07,-128.88\"/>\n",
"</g>\n",
"<!-- Shape_to_tuple&#45;10912160959110460649&#45;&gt;Shape_select&#45;7784354942592584825 -->\n",
"<g id=\"edge7\" class=\"edge\">\n",
"<title>Shape_to_tuple&#45;10912160959110460649:s&#45;&gt;Shape_select&#45;7784354942592584825</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M243,-253C243,-235.76 255.14,-221.63 268.68,-211.24\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"265.35,-213.69 269.91,-211 264.67,-210.26 265.35,-213.69\"/>\n",
"</g>\n",
"<!-- Shape_to_tuple&#45;17615343019692007359&#45;&gt;Shape_deselect&#45;7784354942592584825 -->\n",
"<g id=\"edge9\" class=\"edge\">\n",
"<title>Shape_to_tuple&#45;17615343019692007359:s&#45;&gt;Shape_deselect&#45;7784354942592584825</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M113,-253C113,-236.27 124.06,-223.41 137.96,-213.86\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"139.11,-215.19 142.35,-211 137.21,-212.26 139.11,-215.19\"/>\n",
"</g>\n",
"<!-- length&#45;0&#45;&gt;Tuple___init__&#45;17615343019692007359 -->\n",
"<g id=\"edge10\" class=\"edge\">\n",
"<title>length&#45;0:s&#45;&gt;Tuple___init__&#45;17615343019692007359</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M100,-95C100,-78.17 111.5,-63.87 124.07,-53.25\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"120.63,-56 125,-53 119.72,-52.62 120.63,-56\"/>\n",
"</g>\n",
"<!-- ndim&#45;11743562013128004906&#45;&gt;Shape___init__&#45;0 -->\n",
"<g id=\"edge11\" class=\"edge\">\n",
"<title>ndim&#45;11743562013128004906:s&#45;&gt;Shape___init__&#45;0</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M42,-95C42,-17.43 150.7,-53.94 186,-65.5 200.8,-70.35 214.99,-80.23 226.05,-89.55\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"221.72,-89.38 226.98,-90 223.25,-86.23 221.72,-89.38\"/>\n",
"</g>\n",
"<!-- length&#45;5871781006564002453&#45;&gt;Tuple___init__&#45;11743562013128004906 -->\n",
"<g id=\"edge12\" class=\"edge\">\n",
"<title>length&#45;5871781006564002453:s&#45;&gt;Tuple___init__&#45;11743562013128004906</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M451,-253C451,-208.4 427.14,-160.79 411.14,-133.88\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"414.09,-134.55 409.45,-132 411.49,-136.89 414.09,-134.55\"/>\n",
"</g>\n",
"<!-- length&#45;16783941965674463102&#45;&gt;Shape_to_tuple&#45;10912160959110460649 -->\n",
"<g id=\"edge13\" class=\"edge\">\n",
"<title>length&#45;16783941965674463102:s&#45;&gt;Shape_to_tuple&#45;10912160959110460649</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M335,-253C335,-231.81 311.02,-236.43 287.55,-246.02\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"286.79,-244.44 282.87,-248 288.16,-247.67 286.79,-244.44\"/>\n",
"</g>\n",
"<!-- ndim&#45;10912160959110460649&#45;&gt;Shape_select&#45;7784354942592584825 -->\n",
"<g id=\"edge14\" class=\"edge\">\n",
"<title>ndim&#45;10912160959110460649:s&#45;&gt;Shape_select&#45;7784354942592584825</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M393,-253C393,-231.64 375.45,-216.99 356.03,-207.32\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"359.13,-207.28 354,-205.97 357.19,-210.2 359.13,-207.28\"/>\n",
"</g>\n",
"<!-- length&#45;5040379952546458196&#45;&gt;Shape_to_tuple&#45;17615343019692007359 -->\n",
"<g id=\"edge15\" class=\"edge\">\n",
"<title>length&#45;5040379952546458196:s&#45;&gt;Shape_to_tuple&#45;17615343019692007359</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M116,-332C116,-318.81 115.09,-304.08 114.13,-292.28\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"116.36,-294.58 113.7,-290 112.92,-295.24 116.36,-294.58\"/>\n",
"</g>\n",
"<!-- ndim&#45;17615343019692007359&#45;&gt;Shape_deselect&#45;7784354942592584825 -->\n",
"<g id=\"edge16\" class=\"edge\">\n",
"<title>ndim&#45;17615343019692007359:s&#45;&gt;Shape_deselect&#45;7784354942592584825</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M174,-332C174,-289.68 182.51,-240.85 188.14,-213.18\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"188.63,-216.27 189.16,-211 185.46,-214.79 188.63,-216.27\"/>\n",
"</g>\n",
"<!-- Shape_select&#45;7784354942592584825 -->\n",
"<g id=\"node1\" class=\"node\">\n",
"<title>Shape_select&#45;7784354942592584825</title>\n",
"<g id=\"a_node1\"><a xlink:title=\"5: Shape_select&#45;7784354942592584825\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M269.67,-174C269.67,-174 340.33,-174 340.33,-174 345.67,-174 351,-179.33 351,-184.67 351,-184.67 351,-195.33 351,-195.33 351,-200.67 345.67,-206 340.33,-206 340.33,-206 269.67,-206 269.67,-206 264.33,-206 259,-200.67 259,-195.33 259,-195.33 259,-184.67 259,-184.67 259,-179.33 264.33,-174 269.67,-174\"/>\n",
"<text text-anchor=\"start\" x=\"264\" y=\"-186.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">Shape_select</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M269.67,-174C269.67,-174 340.33,-174 340.33,-174 345.67,-174 351,-179.33 351,-184.67 351,-184.67 351,-195.33 351,-195.33 351,-200.67 345.67,-206 340.33,-206 340.33,-206 269.67,-206 269.67,-206 264.33,-206 259,-200.67 259,-195.33 259,-195.33 259,-184.67 259,-184.67 259,-179.33 264.33,-174 269.67,-174\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- Shape___init__&#45;0 -->\n",
"<g id=\"node2\" class=\"node\">\n",
"<title>Shape___init__&#45;0</title>\n",
"<g id=\"a_node2\"><a xlink:title=\"2: Shape___init__&#45;0\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M206.67,-95C206.67,-95 289.33,-95 289.33,-95 294.67,-95 300,-100.33 300,-105.67 300,-105.67 300,-116.33 300,-116.33 300,-121.67 294.67,-127 289.33,-127 289.33,-127 206.67,-127 206.67,-127 201.33,-127 196,-121.67 196,-116.33 196,-116.33 196,-105.67 196,-105.67 196,-100.33 201.33,-95 206.67,-95\"/>\n",
"<text text-anchor=\"start\" x=\"201\" y=\"-107.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">Shape___init__</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M206.67,-95C206.67,-95 289.33,-95 289.33,-95 294.67,-95 300,-100.33 300,-105.67 300,-105.67 300,-116.33 300,-116.33 300,-121.67 294.67,-127 289.33,-127 289.33,-127 206.67,-127 206.67,-127 201.33,-127 196,-121.67 196,-116.33 196,-116.33 196,-105.67 196,-105.67 196,-100.33 201.33,-95 206.67,-95\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- Tuple___init__&#45;11743562013128004906 -->\n",
"<g id=\"node3\" class=\"node\">\n",
"<title>Tuple___init__&#45;11743562013128004906</title>\n",
"<g id=\"a_node3\"><a xlink:title=\"1: Tuple___init__&#45;11743562013128004906\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M358.17,-95C358.17,-95 435.83,-95 435.83,-95 441.17,-95 446.5,-100.33 446.5,-105.67 446.5,-105.67 446.5,-116.33 446.5,-116.33 446.5,-121.67 441.17,-127 435.83,-127 435.83,-127 358.17,-127 358.17,-127 352.83,-127 347.5,-121.67 347.5,-116.33 347.5,-116.33 347.5,-105.67 347.5,-105.67 347.5,-100.33 352.83,-95 358.17,-95\"/>\n",
"<text text-anchor=\"start\" x=\"353\" y=\"-107.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">Tuple___init__</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M358.17,-95C358.17,-95 435.83,-95 435.83,-95 441.17,-95 446.5,-100.33 446.5,-105.67 446.5,-105.67 446.5,-116.33 446.5,-116.33 446.5,-121.67 441.17,-127 435.83,-127 435.83,-127 358.17,-127 358.17,-127 352.83,-127 347.5,-121.67 347.5,-116.33 347.5,-116.33 347.5,-105.67 347.5,-105.67 347.5,-100.33 352.83,-95 358.17,-95\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- Tuple___init__&#45;17615343019692007359 -->\n",
"<g id=\"node4\" class=\"node\">\n",
"<title>Tuple___init__&#45;17615343019692007359</title>\n",
"<g id=\"a_node4\"><a xlink:title=\"0: Tuple___init__&#45;17615343019692007359\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M117.17,-16C117.17,-16 194.83,-16 194.83,-16 200.17,-16 205.5,-21.33 205.5,-26.67 205.5,-26.67 205.5,-37.33 205.5,-37.33 205.5,-42.67 200.17,-48 194.83,-48 194.83,-48 117.17,-48 117.17,-48 111.83,-48 106.5,-42.67 106.5,-37.33 106.5,-37.33 106.5,-26.67 106.5,-26.67 106.5,-21.33 111.83,-16 117.17,-16\"/>\n",
"<text text-anchor=\"start\" x=\"112\" y=\"-28.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">Tuple___init__</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M117.17,-16C117.17,-16 194.83,-16 194.83,-16 200.17,-16 205.5,-21.33 205.5,-26.67 205.5,-26.67 205.5,-37.33 205.5,-37.33 205.5,-42.67 200.17,-48 194.83,-48 194.83,-48 117.17,-48 117.17,-48 111.83,-48 106.5,-42.67 106.5,-37.33 106.5,-37.33 106.5,-26.67 106.5,-26.67 106.5,-21.33 111.83,-16 117.17,-16\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- Shape_deselect&#45;7784354942592584825 -->\n",
"<g id=\"node5\" class=\"node\">\n",
"<title>Shape_deselect&#45;7784354942592584825</title>\n",
"<g id=\"a_node5\"><a xlink:title=\"3: Shape_deselect&#45;7784354942592584825\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M150.17,-174C150.17,-174 235.83,-174 235.83,-174 241.17,-174 246.5,-179.33 246.5,-184.67 246.5,-184.67 246.5,-195.33 246.5,-195.33 246.5,-200.67 241.17,-206 235.83,-206 235.83,-206 150.17,-206 150.17,-206 144.83,-206 139.5,-200.67 139.5,-195.33 139.5,-195.33 139.5,-184.67 139.5,-184.67 139.5,-179.33 144.83,-174 150.17,-174\"/>\n",
"<text text-anchor=\"start\" x=\"145\" y=\"-186.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">Shape_deselect</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M150.17,-174C150.17,-174 235.83,-174 235.83,-174 241.17,-174 246.5,-179.33 246.5,-184.67 246.5,-184.67 246.5,-195.33 246.5,-195.33 246.5,-200.67 241.17,-206 235.83,-206 235.83,-206 150.17,-206 150.17,-206 144.83,-206 139.5,-200.67 139.5,-195.33 139.5,-195.33 139.5,-184.67 139.5,-184.67 139.5,-179.33 144.83,-174 150.17,-174\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- i64&#45;11743562013128004906 -->\n",
"<g id=\"node6\" class=\"node\">\n",
"<title>i64&#45;11743562013128004906</title>\n",
"<g id=\"a_node6\"><a xlink:title=\"i64&#45;11743562013128004906: i64&#45;11743562013128004906\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M502.67,-253C502.67,-253 513.33,-253 513.33,-253 518.67,-253 524,-258.33 524,-263.67 524,-263.67 524,-274.33 524,-274.33 524,-279.67 518.67,-285 513.33,-285 513.33,-285 502.67,-285 502.67,-285 497.33,-285 492,-279.67 492,-274.33 492,-274.33 492,-263.67 492,-263.67 492,-258.33 497.33,-253 502.67,-253\"/>\n",
"<text text-anchor=\"start\" x=\"504\" y=\"-265.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">2</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M502.67,-253C502.67,-253 513.33,-253 513.33,-253 518.67,-253 524,-258.33 524,-263.67 524,-263.67 524,-274.33 524,-274.33 524,-279.67 518.67,-285 513.33,-285 513.33,-285 502.67,-285 502.67,-285 497.33,-285 492,-279.67 492,-274.33 492,-274.33 492,-263.67 492,-263.67 492,-258.33 497.33,-253 502.67,-253\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- Shape_to_tuple&#45;10912160959110460649 -->\n",
"<g id=\"node7\" class=\"node\">\n",
"<title>Shape_to_tuple&#45;10912160959110460649</title>\n",
"<g id=\"a_node7\"><a xlink:title=\"6: Shape_to_tuple&#45;10912160959110460649\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M200.17,-253C200.17,-253 283.83,-253 283.83,-253 289.17,-253 294.5,-258.33 294.5,-263.67 294.5,-263.67 294.5,-274.33 294.5,-274.33 294.5,-279.67 289.17,-285 283.83,-285 283.83,-285 200.17,-285 200.17,-285 194.83,-285 189.5,-279.67 189.5,-274.33 189.5,-274.33 189.5,-263.67 189.5,-263.67 189.5,-258.33 194.83,-253 200.17,-253\"/>\n",
"<text text-anchor=\"start\" x=\"195\" y=\"-265.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">Shape_to_tuple</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M200.17,-253C200.17,-253 283.83,-253 283.83,-253 289.17,-253 294.5,-258.33 294.5,-263.67 294.5,-263.67 294.5,-274.33 294.5,-274.33 294.5,-279.67 289.17,-285 283.83,-285 283.83,-285 200.17,-285 200.17,-285 194.83,-285 189.5,-279.67 189.5,-274.33 189.5,-274.33 189.5,-263.67 189.5,-263.67 189.5,-258.33 194.83,-253 200.17,-253\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- i64&#45;17615343019692007359 -->\n",
"<g id=\"node8\" class=\"node\">\n",
"<title>i64&#45;17615343019692007359</title>\n",
"<g id=\"a_node8\"><a xlink:title=\"i64&#45;17615343019692007359: i64&#45;17615343019692007359\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M151.67,-95C151.67,-95 162.33,-95 162.33,-95 167.67,-95 173,-100.33 173,-105.67 173,-105.67 173,-116.33 173,-116.33 173,-121.67 167.67,-127 162.33,-127 162.33,-127 151.67,-127 151.67,-127 146.33,-127 141,-121.67 141,-116.33 141,-116.33 141,-105.67 141,-105.67 141,-100.33 146.33,-95 151.67,-95\"/>\n",
"<text text-anchor=\"start\" x=\"153\" y=\"-107.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">3</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M151.67,-95C151.67,-95 162.33,-95 162.33,-95 167.67,-95 173,-100.33 173,-105.67 173,-105.67 173,-116.33 173,-116.33 173,-121.67 167.67,-127 162.33,-127 162.33,-127 151.67,-127 151.67,-127 146.33,-127 141,-121.67 141,-116.33 141,-116.33 141,-105.67 141,-105.67 141,-100.33 146.33,-95 151.67,-95\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- Shape_to_tuple&#45;17615343019692007359 -->\n",
"<g id=\"node9\" class=\"node\">\n",
"<title>Shape_to_tuple&#45;17615343019692007359</title>\n",
"<g id=\"a_node9\"><a xlink:title=\"4: Shape_to_tuple&#45;17615343019692007359\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M70.17,-253C70.17,-253 153.83,-253 153.83,-253 159.17,-253 164.5,-258.33 164.5,-263.67 164.5,-263.67 164.5,-274.33 164.5,-274.33 164.5,-279.67 159.17,-285 153.83,-285 153.83,-285 70.17,-285 70.17,-285 64.83,-285 59.5,-279.67 59.5,-274.33 59.5,-274.33 59.5,-263.67 59.5,-263.67 59.5,-258.33 64.83,-253 70.17,-253\"/>\n",
"<text text-anchor=\"start\" x=\"65\" y=\"-265.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">Shape_to_tuple</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M70.17,-253C70.17,-253 153.83,-253 153.83,-253 159.17,-253 164.5,-258.33 164.5,-263.67 164.5,-263.67 164.5,-274.33 164.5,-274.33 164.5,-279.67 159.17,-285 153.83,-285 153.83,-285 70.17,-285 70.17,-285 64.83,-285 59.5,-279.67 59.5,-274.33 59.5,-274.33 59.5,-263.67 59.5,-263.67 59.5,-258.33 64.83,-253 70.17,-253\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- length&#45;0 -->\n",
"<g id=\"node10\" class=\"node\">\n",
"<title>length&#45;0</title>\n",
"<g id=\"a_node10\"><a xlink:title=\"i64&#45;17615343019692007359: length&#45;0\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M86.17,-95C86.17,-95 111.83,-95 111.83,-95 117.17,-95 122.5,-100.33 122.5,-105.67 122.5,-105.67 122.5,-116.33 122.5,-116.33 122.5,-121.67 117.17,-127 111.83,-127 111.83,-127 86.17,-127 86.17,-127 80.83,-127 75.5,-121.67 75.5,-116.33 75.5,-116.33 75.5,-105.67 75.5,-105.67 75.5,-100.33 80.83,-95 86.17,-95\"/>\n",
"<text text-anchor=\"start\" x=\"81\" y=\"-107.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">length</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M86.17,-95C86.17,-95 111.83,-95 111.83,-95 117.17,-95 122.5,-100.33 122.5,-105.67 122.5,-105.67 122.5,-116.33 122.5,-116.33 122.5,-121.67 117.17,-127 111.83,-127 111.83,-127 86.17,-127 86.17,-127 80.83,-127 75.5,-121.67 75.5,-116.33 75.5,-116.33 75.5,-105.67 75.5,-105.67 75.5,-100.33 80.83,-95 86.17,-95\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- ndim&#45;11743562013128004906 -->\n",
"<g id=\"node11\" class=\"node\">\n",
"<title>ndim&#45;11743562013128004906</title>\n",
"<g id=\"a_node11\"><a xlink:title=\"i64&#45;17615343019692007359: ndim&#45;11743562013128004906\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M31.17,-95C31.17,-95 50.83,-95 50.83,-95 56.17,-95 61.5,-100.33 61.5,-105.67 61.5,-105.67 61.5,-116.33 61.5,-116.33 61.5,-121.67 56.17,-127 50.83,-127 50.83,-127 31.17,-127 31.17,-127 25.83,-127 20.5,-121.67 20.5,-116.33 20.5,-116.33 20.5,-105.67 20.5,-105.67 20.5,-100.33 25.83,-95 31.17,-95\"/>\n",
"<text text-anchor=\"start\" x=\"26\" y=\"-107.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">ndim</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M31.17,-95C31.17,-95 50.83,-95 50.83,-95 56.17,-95 61.5,-100.33 61.5,-105.67 61.5,-105.67 61.5,-116.33 61.5,-116.33 61.5,-121.67 56.17,-127 50.83,-127 50.83,-127 31.17,-127 31.17,-127 25.83,-127 20.5,-121.67 20.5,-116.33 20.5,-116.33 20.5,-105.67 20.5,-105.67 20.5,-100.33 25.83,-95 31.17,-95\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- length&#45;5871781006564002453 -->\n",
"<g id=\"node12\" class=\"node\">\n",
"<title>length&#45;5871781006564002453</title>\n",
"<g id=\"a_node12\"><a xlink:title=\"i64&#45;11743562013128004906: length&#45;5871781006564002453\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M437.17,-253C437.17,-253 462.83,-253 462.83,-253 468.17,-253 473.5,-258.33 473.5,-263.67 473.5,-263.67 473.5,-274.33 473.5,-274.33 473.5,-279.67 468.17,-285 462.83,-285 462.83,-285 437.17,-285 437.17,-285 431.83,-285 426.5,-279.67 426.5,-274.33 426.5,-274.33 426.5,-263.67 426.5,-263.67 426.5,-258.33 431.83,-253 437.17,-253\"/>\n",
"<text text-anchor=\"start\" x=\"432\" y=\"-265.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">length</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M437.17,-253C437.17,-253 462.83,-253 462.83,-253 468.17,-253 473.5,-258.33 473.5,-263.67 473.5,-263.67 473.5,-274.33 473.5,-274.33 473.5,-279.67 468.17,-285 462.83,-285 462.83,-285 437.17,-285 437.17,-285 431.83,-285 426.5,-279.67 426.5,-274.33 426.5,-274.33 426.5,-263.67 426.5,-263.67 426.5,-258.33 431.83,-253 437.17,-253\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- length&#45;16783941965674463102 -->\n",
"<g id=\"node13\" class=\"node\">\n",
"<title>length&#45;16783941965674463102</title>\n",
"<g id=\"a_node13\"><a xlink:title=\"i64&#45;11743562013128004906: length&#45;16783941965674463102\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M321.17,-253C321.17,-253 346.83,-253 346.83,-253 352.17,-253 357.5,-258.33 357.5,-263.67 357.5,-263.67 357.5,-274.33 357.5,-274.33 357.5,-279.67 352.17,-285 346.83,-285 346.83,-285 321.17,-285 321.17,-285 315.83,-285 310.5,-279.67 310.5,-274.33 310.5,-274.33 310.5,-263.67 310.5,-263.67 310.5,-258.33 315.83,-253 321.17,-253\"/>\n",
"<text text-anchor=\"start\" x=\"316\" y=\"-265.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">length</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M321.17,-253C321.17,-253 346.83,-253 346.83,-253 352.17,-253 357.5,-258.33 357.5,-263.67 357.5,-263.67 357.5,-274.33 357.5,-274.33 357.5,-279.67 352.17,-285 346.83,-285 346.83,-285 321.17,-285 321.17,-285 315.83,-285 310.5,-279.67 310.5,-274.33 310.5,-274.33 310.5,-263.67 310.5,-263.67 310.5,-258.33 315.83,-253 321.17,-253\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- ndim&#45;10912160959110460649 -->\n",
"<g id=\"node14\" class=\"node\">\n",
"<title>ndim&#45;10912160959110460649</title>\n",
"<g id=\"a_node14\"><a xlink:title=\"i64&#45;11743562013128004906: ndim&#45;10912160959110460649\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M382.17,-253C382.17,-253 401.83,-253 401.83,-253 407.17,-253 412.5,-258.33 412.5,-263.67 412.5,-263.67 412.5,-274.33 412.5,-274.33 412.5,-279.67 407.17,-285 401.83,-285 401.83,-285 382.17,-285 382.17,-285 376.83,-285 371.5,-279.67 371.5,-274.33 371.5,-274.33 371.5,-263.67 371.5,-263.67 371.5,-258.33 376.83,-253 382.17,-253\"/>\n",
"<text text-anchor=\"start\" x=\"377\" y=\"-265.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">ndim</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M382.17,-253C382.17,-253 401.83,-253 401.83,-253 407.17,-253 412.5,-258.33 412.5,-263.67 412.5,-263.67 412.5,-274.33 412.5,-274.33 412.5,-279.67 407.17,-285 401.83,-285 401.83,-285 382.17,-285 382.17,-285 376.83,-285 371.5,-279.67 371.5,-274.33 371.5,-274.33 371.5,-263.67 371.5,-263.67 371.5,-258.33 376.83,-253 382.17,-253\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- length&#45;5040379952546458196 -->\n",
"<g id=\"node15\" class=\"node\">\n",
"<title>length&#45;5040379952546458196</title>\n",
"<g id=\"a_node15\"><a xlink:title=\"i64&#45;5871781006564002453: length&#45;5040379952546458196\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M102.17,-332C102.17,-332 127.83,-332 127.83,-332 133.17,-332 138.5,-337.33 138.5,-342.67 138.5,-342.67 138.5,-353.33 138.5,-353.33 138.5,-358.67 133.17,-364 127.83,-364 127.83,-364 102.17,-364 102.17,-364 96.83,-364 91.5,-358.67 91.5,-353.33 91.5,-353.33 91.5,-342.67 91.5,-342.67 91.5,-337.33 96.83,-332 102.17,-332\"/>\n",
"<text text-anchor=\"start\" x=\"97\" y=\"-344.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">length</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M102.17,-332C102.17,-332 127.83,-332 127.83,-332 133.17,-332 138.5,-337.33 138.5,-342.67 138.5,-342.67 138.5,-353.33 138.5,-353.33 138.5,-358.67 133.17,-364 127.83,-364 127.83,-364 102.17,-364 102.17,-364 96.83,-364 91.5,-358.67 91.5,-353.33 91.5,-353.33 91.5,-342.67 91.5,-342.67 91.5,-337.33 96.83,-332 102.17,-332\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- ndim&#45;17615343019692007359 -->\n",
"<g id=\"node16\" class=\"node\">\n",
"<title>ndim&#45;17615343019692007359</title>\n",
"<g id=\"a_node16\"><a xlink:title=\"i64&#45;5871781006564002453: ndim&#45;17615343019692007359\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M163.17,-332C163.17,-332 182.83,-332 182.83,-332 188.17,-332 193.5,-337.33 193.5,-342.67 193.5,-342.67 193.5,-353.33 193.5,-353.33 193.5,-358.67 188.17,-364 182.83,-364 182.83,-364 163.17,-364 163.17,-364 157.83,-364 152.5,-358.67 152.5,-353.33 152.5,-353.33 152.5,-342.67 152.5,-342.67 152.5,-337.33 157.83,-332 163.17,-332\"/>\n",
"<text text-anchor=\"start\" x=\"158\" y=\"-344.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">ndim</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M163.17,-332C163.17,-332 182.83,-332 182.83,-332 188.17,-332 193.5,-337.33 193.5,-342.67 193.5,-342.67 193.5,-353.33 193.5,-353.33 193.5,-358.67 188.17,-364 182.83,-364 182.83,-364 163.17,-364 163.17,-364 157.83,-364 152.5,-358.67 152.5,-353.33 152.5,-353.33 152.5,-342.67 152.5,-342.67 152.5,-337.33 157.83,-332 163.17,-332\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- i64&#45;5871781006564002453 -->\n",
"<g id=\"node17\" class=\"node\">\n",
"<title>i64&#45;5871781006564002453</title>\n",
"<g id=\"a_node17\"><a xlink:title=\"i64&#45;5871781006564002453: i64&#45;5871781006564002453\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M51.67,-332C51.67,-332 62.33,-332 62.33,-332 67.67,-332 73,-337.33 73,-342.67 73,-342.67 73,-353.33 73,-353.33 73,-358.67 67.67,-364 62.33,-364 62.33,-364 51.67,-364 51.67,-364 46.33,-364 41,-358.67 41,-353.33 41,-353.33 41,-342.67 41,-342.67 41,-337.33 46.33,-332 51.67,-332\"/>\n",
"<text text-anchor=\"start\" x=\"53\" y=\"-344.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">1</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M51.67,-332C51.67,-332 62.33,-332 62.33,-332 67.67,-332 73,-337.33 73,-342.67 73,-342.67 73,-353.33 73,-353.33 73,-358.67 67.67,-364 62.33,-364 62.33,-364 51.67,-364 51.67,-364 46.33,-364 41,-358.67 41,-353.33 41,-353.33 41,-342.67 41,-342.67 41,-337.33 46.33,-332 51.67,-332\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"</g>\n",
"</svg>\n"
],
"text/html": [
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
"<?xml-stylesheet href=\"/var/folders/6k/r_fxgcx50z3gkdnckfxkfrv40000gp/T/graphviz-styles.css\" type=\"text/css\"?>\n",
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
"<!-- Generated by graphviz version 2.50.0 (0)\n",
" -->\n",
"<!-- Pages: 1 -->\n",
"<svg width=\"454pt\" height=\"467pt\"\n",
" viewBox=\"0.00 0.00 454.00 467.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
"<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 463)\">\n",
"<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-463 450,-463 450,4 -4,4\"/>\n",
"<g id=\"clust1\" class=\"cluster\">\n",
"<title>outer_cluster_2</title>\n",
"</g>\n",
"<g id=\"clust2\" class=\"cluster\">\n",
"<title>cluster_2</title>\n",
"<path fill=\"#ffffb3\" stroke=\"black\" stroke-dasharray=\"5,2\" d=\"M233,-90C233,-90 319,-90 319,-90 325,-90 331,-96 331,-102 331,-102 331,-120 331,-120 331,-126 325,-132 319,-132 319,-132 233,-132 233,-132 227,-132 221,-126 221,-120 221,-120 221,-102 221,-102 221,-96 227,-90 233,-90\"/>\n",
"</g>\n",
"<g id=\"clust4\" class=\"cluster\">\n",
"<title>outer_cluster_5</title>\n",
"</g>\n",
"<g id=\"clust5\" class=\"cluster\">\n",
"<title>cluster_5</title>\n",
"<path fill=\"#ffffb3\" stroke=\"black\" stroke-dasharray=\"5,2\" d=\"M141,-169C141,-169 215,-169 215,-169 221,-169 227,-175 227,-181 227,-181 227,-199 227,-199 227,-205 221,-211 215,-211 215,-211 141,-211 141,-211 135,-211 129,-205 129,-199 129,-199 129,-181 129,-181 129,-175 135,-169 141,-169\"/>\n",
"</g>\n",
"<g id=\"clust7\" class=\"cluster\">\n",
"<title>outer_cluster_3</title>\n",
"</g>\n",
"<g id=\"clust8\" class=\"cluster\">\n",
"<title>cluster_3</title>\n",
"<path fill=\"#ffffb3\" stroke=\"black\" stroke-dasharray=\"5,2\" d=\"M333,-248C333,-248 423,-248 423,-248 429,-248 435,-254 435,-260 435,-260 435,-278 435,-278 435,-284 429,-290 423,-290 423,-290 333,-290 333,-290 327,-290 321,-284 321,-278 321,-278 321,-260 321,-260 321,-254 327,-248 333,-248\"/>\n",
"</g>\n",
"<g id=\"clust19\" class=\"cluster\">\n",
"<title>outer_cluster_0</title>\n",
"</g>\n",
"<g id=\"clust20\" class=\"cluster\">\n",
"<title>cluster_0</title>\n",
"<path fill=\"#fb8072\" stroke=\"black\" stroke-dasharray=\"5,2\" d=\"M143,-11C143,-11 225,-11 225,-11 231,-11 237,-17 237,-23 237,-23 237,-41 237,-41 237,-47 231,-53 225,-53 225,-53 143,-53 143,-53 137,-53 131,-47 131,-41 131,-41 131,-23 131,-23 131,-17 137,-11 143,-11\"/>\n",
"</g>\n",
"<g id=\"clust22\" class=\"cluster\">\n",
"<title>outer_cluster_4</title>\n",
"</g>\n",
"<g id=\"clust23\" class=\"cluster\">\n",
"<title>cluster_4</title>\n",
"<path fill=\"#fb8072\" stroke=\"black\" stroke-dasharray=\"5,2\" d=\"M284,-327C284,-327 372,-327 372,-327 378,-327 384,-333 384,-339 384,-339 384,-357 384,-357 384,-363 378,-369 372,-369 372,-369 284,-369 284,-369 278,-369 272,-363 272,-357 272,-357 272,-339 272,-339 272,-333 278,-327 284,-327\"/>\n",
"</g>\n",
"<g id=\"clust25\" class=\"cluster\">\n",
"<title>outer_cluster_6</title>\n",
"</g>\n",
"<g id=\"clust26\" class=\"cluster\">\n",
"<title>cluster_6</title>\n",
"<path fill=\"#fb8072\" stroke=\"black\" stroke-dasharray=\"5,2\" d=\"M23,-169C23,-169 111,-169 111,-169 117,-169 123,-175 123,-181 123,-181 123,-199 123,-199 123,-205 117,-211 111,-211 111,-211 23,-211 23,-211 17,-211 11,-205 11,-199 11,-199 11,-181 11,-181 11,-175 17,-169 23,-169\"/>\n",
"</g>\n",
"<g id=\"clust28\" class=\"cluster\">\n",
"<title>outer_cluster_1</title>\n",
"</g>\n",
"<g id=\"clust29\" class=\"cluster\">\n",
"<title>cluster_1</title>\n",
"<path fill=\"#fb8072\" stroke=\"black\" stroke-dasharray=\"5,2\" d=\"M245,-169C245,-169 327,-169 327,-169 333,-169 339,-175 339,-181 339,-181 339,-199 339,-199 339,-205 333,-211 327,-211 327,-211 245,-211 245,-211 239,-211 233,-205 233,-199 233,-199 233,-181 233,-181 233,-175 239,-169 245,-169\"/>\n",
"</g>\n",
"<g id=\"clust16\" class=\"cluster\">\n",
"<title>outer_cluster_i64&#45;17615343019692007359</title>\n",
"</g>\n",
"<g id=\"clust17\" class=\"cluster\">\n",
"<title>cluster_i64&#45;17615343019692007359</title>\n",
"<path fill=\"#bebada\" stroke=\"black\" stroke-dasharray=\"5,2\" d=\"M51,-90C51,-90 203,-90 203,-90 209,-90 215,-96 215,-102 215,-102 215,-120 215,-120 215,-126 209,-132 203,-132 203,-132 51,-132 51,-132 45,-132 39,-126 39,-120 39,-120 39,-102 39,-102 39,-96 45,-90 51,-90\"/>\n",
"</g>\n",
"<g id=\"clust10\" class=\"cluster\">\n",
"<title>outer_cluster_i64&#45;5871781006564002453</title>\n",
"</g>\n",
"<g id=\"clust11\" class=\"cluster\">\n",
"<title>cluster_i64&#45;5871781006564002453</title>\n",
"<path fill=\"#bebada\" stroke=\"black\" stroke-dasharray=\"5,2\" d=\"M255,-406C255,-406 407,-406 407,-406 413,-406 419,-412 419,-418 419,-418 419,-436 419,-436 419,-442 413,-448 407,-448 407,-448 255,-448 255,-448 249,-448 243,-442 243,-436 243,-436 243,-418 243,-418 243,-412 249,-406 255,-406\"/>\n",
"</g>\n",
"<g id=\"clust13\" class=\"cluster\">\n",
"<title>outer_cluster_i64&#45;11743562013128004906</title>\n",
"</g>\n",
"<g id=\"clust14\" class=\"cluster\">\n",
"<title>cluster_i64&#45;11743562013128004906</title>\n",
"<path fill=\"#bebada\" stroke=\"black\" stroke-dasharray=\"5,2\" d=\"M93,-248C93,-248 303,-248 303,-248 309,-248 315,-254 315,-260 315,-260 315,-278 315,-278 315,-284 309,-290 303,-290 303,-290 93,-290 93,-290 87,-290 81,-284 81,-278 81,-278 81,-260 81,-260 81,-254 87,-248 93,-248\"/>\n",
"</g>\n",
"<!-- Shape___init__&#45;0&#45;&gt;Tuple___init__&#45;17615343019692007359 -->\n",
"<g id=\"edge1\" class=\"edge\">\n",
"<title>Shape___init__&#45;0:s&#45;&gt;Tuple___init__&#45;17615343019692007359</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M276,-95C276,-73.24 258.09,-58.62 238.07,-49.06\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"241.76,-50.3 237,-47.98 239.28,-52.76 241.76,-50.3\"/>\n",
"</g>\n",
"<!-- Tuple___init__&#45;17615343019692007359&#45;&gt;i64&#45;17615343019692007359 -->\n",
"<g id=\"edge13\" class=\"edge\">\n",
"<title>Tuple___init__&#45;17615343019692007359:s&#45;&gt;i64&#45;17615343019692007359</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M185,-16C185,5.56 218.41,-1.6 233.5,-17 253.77,-37.7 230.12,-68.59 209.3,-89.01\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"211.03,-86.09 207.46,-90 212.69,-89.17 211.03,-86.09\"/>\n",
"</g>\n",
"<!-- Shape_select&#45;7784354942592584825&#45;&gt;Shape___init__&#45;0 -->\n",
"<g id=\"edge2\" class=\"edge\">\n",
"<title>Shape_select&#45;7784354942592584825:s&#45;&gt;Shape___init__&#45;0</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M155,-174C155,-165.84 190.32,-148.4 222.8,-134.09\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"223.69,-135.61 227.57,-132 222.28,-132.4 223.69,-135.61\"/>\n",
"</g>\n",
"<!-- Shape_select&#45;7784354942592584825&#45;&gt;Tuple___init__&#45;11743562013128004906 -->\n",
"<g id=\"edge3\" class=\"edge\">\n",
"<title>Shape_select&#45;7784354942592584825:s&#45;&gt;Tuple___init__&#45;11743562013128004906</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M201,-174C201,-153.46 223.13,-157.7 244.68,-166.96\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"243.98,-168.56 249.26,-169 245.41,-165.37 243.98,-168.56\"/>\n",
"</g>\n",
"<!-- Tuple___init__&#45;11743562013128004906&#45;&gt;i64&#45;11743562013128004906 -->\n",
"<g id=\"edge16\" class=\"edge\">\n",
"<title>Tuple___init__&#45;11743562013128004906:s&#45;&gt;i64&#45;11743562013128004906</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M287,-174C287,-152.44 320.41,-159.6 335.5,-175 356.04,-195.97 331.28,-226.98 309.75,-247.34\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"312.09,-244.56 308.06,-248 313.36,-247.82 312.09,-244.56\"/>\n",
"</g>\n",
"<!-- Shape_deselect&#45;7784354942592584825&#45;&gt;Shape___init__&#45;0 -->\n",
"<g id=\"edge4\" class=\"edge\">\n",
"<title>Shape_deselect&#45;7784354942592584825:s&#45;&gt;Shape___init__&#45;0</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M352,-253C352,-213.97 358.83,-200.19 340,-166 332.63,-152.62 320.52,-141.12 308.81,-132.22\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"312.75,-131.12 307.53,-132 312.16,-134.57 312.75,-131.12\"/>\n",
"</g>\n",
"<!-- Shape_deselect&#45;7784354942592584825&#45;&gt;Tuple___init__&#45;11743562013128004906 -->\n",
"<g id=\"edge5\" class=\"edge\">\n",
"<title>Shape_deselect&#45;7784354942592584825:s&#45;&gt;Tuple___init__&#45;11743562013128004906</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M405,-253C405,-244.93 370.16,-227.44 338.18,-213.09\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"338.77,-211.43 333.49,-211 337.34,-214.63 338.77,-211.43\"/>\n",
"</g>\n",
"<!-- length&#45;5040379952546458196&#45;&gt;Shape_to_tuple&#45;17615343019692007359 -->\n",
"<g id=\"edge6\" class=\"edge\">\n",
"<title>length&#45;5040379952546458196:s&#45;&gt;Shape_to_tuple&#45;17615343019692007359</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M332,-411C332,-397.81 331.09,-383.08 330.13,-371.28\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"332.36,-373.58 329.7,-369 328.92,-374.24 332.36,-373.58\"/>\n",
"</g>\n",
"<!-- Shape_to_tuple&#45;17615343019692007359&#45;&gt;Shape_deselect&#45;7784354942592584825 -->\n",
"<g id=\"edge14\" class=\"edge\">\n",
"<title>Shape_to_tuple&#45;17615343019692007359:s&#45;&gt;Shape_deselect&#45;7784354942592584825</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M329,-332C329,-315.85 339.36,-301.45 350.55,-290.6\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"348.36,-294.37 351.36,-290 346.29,-291.55 348.36,-294.37\"/>\n",
"</g>\n",
"<!-- ndim&#45;17615343019692007359&#45;&gt;Shape_deselect&#45;7784354942592584825 -->\n",
"<g id=\"edge7\" class=\"edge\">\n",
"<title>ndim&#45;17615343019692007359:s&#45;&gt;Shape_deselect&#45;7784354942592584825</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M390,-411C390,-372.27 388.6,-362.56 385,-324 384.04,-313.68 382.61,-302.29 381.3,-292.64\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"383.58,-294.37 380.59,-290 380.2,-295.28 383.58,-294.37\"/>\n",
"</g>\n",
"<!-- length&#45;5871781006564002453&#45;&gt;Tuple___init__&#45;11743562013128004906 -->\n",
"<g id=\"edge8\" class=\"edge\">\n",
"<title>length&#45;5871781006564002453:s&#45;&gt;Tuple___init__&#45;11743562013128004906</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M228,-253C228,-236.04 239.72,-221.79 252.63,-211.25\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"249.22,-213.9 253.66,-211 248.39,-210.5 249.22,-213.9\"/>\n",
"</g>\n",
"<!-- length&#45;16783941965674463102&#45;&gt;Shape_to_tuple&#45;10912160959110460649 -->\n",
"<g id=\"edge9\" class=\"edge\">\n",
"<title>length&#45;16783941965674463102:s&#45;&gt;Shape_to_tuple&#45;10912160959110460649</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M112,-253C112,-237.23 102.34,-222.76 91.98,-211.78\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"95.7,-211.71 90.45,-211 94.11,-214.83 95.7,-211.71\"/>\n",
"</g>\n",
"<!-- Shape_to_tuple&#45;10912160959110460649&#45;&gt;Shape_select&#45;7784354942592584825 -->\n",
"<g id=\"edge15\" class=\"edge\">\n",
"<title>Shape_to_tuple&#45;10912160959110460649:s&#45;&gt;Shape_select&#45;7784354942592584825</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M68,-174C68,-144.86 103.64,-153.62 134.17,-166.85\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"133.73,-168.57 139.01,-169 135.16,-165.37 133.73,-168.57\"/>\n",
"</g>\n",
"<!-- ndim&#45;10912160959110460649&#45;&gt;Shape_select&#45;7784354942592584825 -->\n",
"<g id=\"edge10\" class=\"edge\">\n",
"<title>ndim&#45;10912160959110460649:s&#45;&gt;Shape_select&#45;7784354942592584825</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M170,-253C170,-239.73 171.83,-224.99 173.73,-213.22\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"174.4,-216.29 174.61,-211 171.14,-215.01 174.4,-216.29\"/>\n",
"</g>\n",
"<!-- length&#45;0&#45;&gt;Tuple___init__&#45;17615343019692007359 -->\n",
"<g id=\"edge11\" class=\"edge\">\n",
"<title>length&#45;0:s&#45;&gt;Tuple___init__&#45;17615343019692007359</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M70,-95C70,-65.4 100.03,-49.85 129.27,-41.73\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"128.11,-44.83 131,-40.39 125.97,-42.06 128.11,-44.83\"/>\n",
"</g>\n",
"<!-- ndim&#45;11743562013128004906&#45;&gt;Shape___init__&#45;0 -->\n",
"<g id=\"edge12\" class=\"edge\">\n",
"<title>ndim&#45;11743562013128004906:s&#45;&gt;Shape___init__&#45;0</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M128,-95C128,-54.59 175.6,-52.92 214,-65.5 228.8,-70.35 242.99,-80.23 254.05,-89.55\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"249.72,-89.38 254.98,-90 251.25,-86.23 249.72,-89.38\"/>\n",
"</g>\n",
"<!-- Shape___init__&#45;0 -->\n",
"<g id=\"node1\" class=\"node\">\n",
"<title>Shape___init__&#45;0</title>\n",
"<g id=\"a_node1\"><a xlink:title=\"2: Shape___init__&#45;0\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M234.67,-95C234.67,-95 317.33,-95 317.33,-95 322.67,-95 328,-100.33 328,-105.67 328,-105.67 328,-116.33 328,-116.33 328,-121.67 322.67,-127 317.33,-127 317.33,-127 234.67,-127 234.67,-127 229.33,-127 224,-121.67 224,-116.33 224,-116.33 224,-105.67 224,-105.67 224,-100.33 229.33,-95 234.67,-95\"/>\n",
"<text text-anchor=\"start\" x=\"229\" y=\"-107.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">Shape___init__</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M234.67,-95C234.67,-95 317.33,-95 317.33,-95 322.67,-95 328,-100.33 328,-105.67 328,-105.67 328,-116.33 328,-116.33 328,-121.67 322.67,-127 317.33,-127 317.33,-127 234.67,-127 234.67,-127 229.33,-127 224,-121.67 224,-116.33 224,-116.33 224,-105.67 224,-105.67 224,-100.33 229.33,-95 234.67,-95\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- Tuple___init__&#45;17615343019692007359 -->\n",
"<g id=\"node2\" class=\"node\">\n",
"<title>Tuple___init__&#45;17615343019692007359</title>\n",
"<g id=\"a_node2\"><a xlink:title=\"0: Tuple___init__&#45;17615343019692007359\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M145.17,-16C145.17,-16 222.83,-16 222.83,-16 228.17,-16 233.5,-21.33 233.5,-26.67 233.5,-26.67 233.5,-37.33 233.5,-37.33 233.5,-42.67 228.17,-48 222.83,-48 222.83,-48 145.17,-48 145.17,-48 139.83,-48 134.5,-42.67 134.5,-37.33 134.5,-37.33 134.5,-26.67 134.5,-26.67 134.5,-21.33 139.83,-16 145.17,-16\"/>\n",
"<text text-anchor=\"start\" x=\"140\" y=\"-28.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">Tuple___init__</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M145.17,-16C145.17,-16 222.83,-16 222.83,-16 228.17,-16 233.5,-21.33 233.5,-26.67 233.5,-26.67 233.5,-37.33 233.5,-37.33 233.5,-42.67 228.17,-48 222.83,-48 222.83,-48 145.17,-48 145.17,-48 139.83,-48 134.5,-42.67 134.5,-37.33 134.5,-37.33 134.5,-26.67 134.5,-26.67 134.5,-21.33 139.83,-16 145.17,-16\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- Shape_select&#45;7784354942592584825 -->\n",
"<g id=\"node3\" class=\"node\">\n",
"<title>Shape_select&#45;7784354942592584825</title>\n",
"<g id=\"a_node3\"><a xlink:title=\"5: Shape_select&#45;7784354942592584825\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M142.67,-174C142.67,-174 213.33,-174 213.33,-174 218.67,-174 224,-179.33 224,-184.67 224,-184.67 224,-195.33 224,-195.33 224,-200.67 218.67,-206 213.33,-206 213.33,-206 142.67,-206 142.67,-206 137.33,-206 132,-200.67 132,-195.33 132,-195.33 132,-184.67 132,-184.67 132,-179.33 137.33,-174 142.67,-174\"/>\n",
"<text text-anchor=\"start\" x=\"137\" y=\"-186.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">Shape_select</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M142.67,-174C142.67,-174 213.33,-174 213.33,-174 218.67,-174 224,-179.33 224,-184.67 224,-184.67 224,-195.33 224,-195.33 224,-200.67 218.67,-206 213.33,-206 213.33,-206 142.67,-206 142.67,-206 137.33,-206 132,-200.67 132,-195.33 132,-195.33 132,-184.67 132,-184.67 132,-179.33 137.33,-174 142.67,-174\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- Tuple___init__&#45;11743562013128004906 -->\n",
"<g id=\"node4\" class=\"node\">\n",
"<title>Tuple___init__&#45;11743562013128004906</title>\n",
"<g id=\"a_node4\"><a xlink:title=\"1: Tuple___init__&#45;11743562013128004906\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M247.17,-174C247.17,-174 324.83,-174 324.83,-174 330.17,-174 335.5,-179.33 335.5,-184.67 335.5,-184.67 335.5,-195.33 335.5,-195.33 335.5,-200.67 330.17,-206 324.83,-206 324.83,-206 247.17,-206 247.17,-206 241.83,-206 236.5,-200.67 236.5,-195.33 236.5,-195.33 236.5,-184.67 236.5,-184.67 236.5,-179.33 241.83,-174 247.17,-174\"/>\n",
"<text text-anchor=\"start\" x=\"242\" y=\"-186.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">Tuple___init__</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M247.17,-174C247.17,-174 324.83,-174 324.83,-174 330.17,-174 335.5,-179.33 335.5,-184.67 335.5,-184.67 335.5,-195.33 335.5,-195.33 335.5,-200.67 330.17,-206 324.83,-206 324.83,-206 247.17,-206 247.17,-206 241.83,-206 236.5,-200.67 236.5,-195.33 236.5,-195.33 236.5,-184.67 236.5,-184.67 236.5,-179.33 241.83,-174 247.17,-174\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- Shape_deselect&#45;7784354942592584825 -->\n",
"<g id=\"node5\" class=\"node\">\n",
"<title>Shape_deselect&#45;7784354942592584825</title>\n",
"<g id=\"a_node5\"><a xlink:title=\"3: Shape_deselect&#45;7784354942592584825\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M335.17,-253C335.17,-253 420.83,-253 420.83,-253 426.17,-253 431.5,-258.33 431.5,-263.67 431.5,-263.67 431.5,-274.33 431.5,-274.33 431.5,-279.67 426.17,-285 420.83,-285 420.83,-285 335.17,-285 335.17,-285 329.83,-285 324.5,-279.67 324.5,-274.33 324.5,-274.33 324.5,-263.67 324.5,-263.67 324.5,-258.33 329.83,-253 335.17,-253\"/>\n",
"<text text-anchor=\"start\" x=\"330\" y=\"-265.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">Shape_deselect</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M335.17,-253C335.17,-253 420.83,-253 420.83,-253 426.17,-253 431.5,-258.33 431.5,-263.67 431.5,-263.67 431.5,-274.33 431.5,-274.33 431.5,-279.67 426.17,-285 420.83,-285 420.83,-285 335.17,-285 335.17,-285 329.83,-285 324.5,-279.67 324.5,-274.33 324.5,-274.33 324.5,-263.67 324.5,-263.67 324.5,-258.33 329.83,-253 335.17,-253\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- length&#45;5040379952546458196 -->\n",
"<g id=\"node6\" class=\"node\">\n",
"<title>length&#45;5040379952546458196</title>\n",
"<g id=\"a_node6\"><a xlink:title=\"i64&#45;5871781006564002453: length&#45;5040379952546458196\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M318.17,-411C318.17,-411 343.83,-411 343.83,-411 349.17,-411 354.5,-416.33 354.5,-421.67 354.5,-421.67 354.5,-432.33 354.5,-432.33 354.5,-437.67 349.17,-443 343.83,-443 343.83,-443 318.17,-443 318.17,-443 312.83,-443 307.5,-437.67 307.5,-432.33 307.5,-432.33 307.5,-421.67 307.5,-421.67 307.5,-416.33 312.83,-411 318.17,-411\"/>\n",
"<text text-anchor=\"start\" x=\"313\" y=\"-423.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">length</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M318.17,-411C318.17,-411 343.83,-411 343.83,-411 349.17,-411 354.5,-416.33 354.5,-421.67 354.5,-421.67 354.5,-432.33 354.5,-432.33 354.5,-437.67 349.17,-443 343.83,-443 343.83,-443 318.17,-443 318.17,-443 312.83,-443 307.5,-437.67 307.5,-432.33 307.5,-432.33 307.5,-421.67 307.5,-421.67 307.5,-416.33 312.83,-411 318.17,-411\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- Shape_to_tuple&#45;17615343019692007359 -->\n",
"<g id=\"node7\" class=\"node\">\n",
"<title>Shape_to_tuple&#45;17615343019692007359</title>\n",
"<g id=\"a_node7\"><a xlink:title=\"4: Shape_to_tuple&#45;17615343019692007359\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M286.17,-332C286.17,-332 369.83,-332 369.83,-332 375.17,-332 380.5,-337.33 380.5,-342.67 380.5,-342.67 380.5,-353.33 380.5,-353.33 380.5,-358.67 375.17,-364 369.83,-364 369.83,-364 286.17,-364 286.17,-364 280.83,-364 275.5,-358.67 275.5,-353.33 275.5,-353.33 275.5,-342.67 275.5,-342.67 275.5,-337.33 280.83,-332 286.17,-332\"/>\n",
"<text text-anchor=\"start\" x=\"281\" y=\"-344.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">Shape_to_tuple</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M286.17,-332C286.17,-332 369.83,-332 369.83,-332 375.17,-332 380.5,-337.33 380.5,-342.67 380.5,-342.67 380.5,-353.33 380.5,-353.33 380.5,-358.67 375.17,-364 369.83,-364 369.83,-364 286.17,-364 286.17,-364 280.83,-364 275.5,-358.67 275.5,-353.33 275.5,-353.33 275.5,-342.67 275.5,-342.67 275.5,-337.33 280.83,-332 286.17,-332\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- ndim&#45;17615343019692007359 -->\n",
"<g id=\"node8\" class=\"node\">\n",
"<title>ndim&#45;17615343019692007359</title>\n",
"<g id=\"a_node8\"><a xlink:title=\"i64&#45;5871781006564002453: ndim&#45;17615343019692007359\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M379.17,-411C379.17,-411 398.83,-411 398.83,-411 404.17,-411 409.5,-416.33 409.5,-421.67 409.5,-421.67 409.5,-432.33 409.5,-432.33 409.5,-437.67 404.17,-443 398.83,-443 398.83,-443 379.17,-443 379.17,-443 373.83,-443 368.5,-437.67 368.5,-432.33 368.5,-432.33 368.5,-421.67 368.5,-421.67 368.5,-416.33 373.83,-411 379.17,-411\"/>\n",
"<text text-anchor=\"start\" x=\"374\" y=\"-423.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">ndim</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M379.17,-411C379.17,-411 398.83,-411 398.83,-411 404.17,-411 409.5,-416.33 409.5,-421.67 409.5,-421.67 409.5,-432.33 409.5,-432.33 409.5,-437.67 404.17,-443 398.83,-443 398.83,-443 379.17,-443 379.17,-443 373.83,-443 368.5,-437.67 368.5,-432.33 368.5,-432.33 368.5,-421.67 368.5,-421.67 368.5,-416.33 373.83,-411 379.17,-411\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- i64&#45;5871781006564002453 -->\n",
"<g id=\"node9\" class=\"node\">\n",
"<title>i64&#45;5871781006564002453</title>\n",
"<g id=\"a_node9\"><a xlink:title=\"i64&#45;5871781006564002453: i64&#45;5871781006564002453\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M267.67,-411C267.67,-411 278.33,-411 278.33,-411 283.67,-411 289,-416.33 289,-421.67 289,-421.67 289,-432.33 289,-432.33 289,-437.67 283.67,-443 278.33,-443 278.33,-443 267.67,-443 267.67,-443 262.33,-443 257,-437.67 257,-432.33 257,-432.33 257,-421.67 257,-421.67 257,-416.33 262.33,-411 267.67,-411\"/>\n",
"<text text-anchor=\"start\" x=\"269\" y=\"-423.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">1</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M267.67,-411C267.67,-411 278.33,-411 278.33,-411 283.67,-411 289,-416.33 289,-421.67 289,-421.67 289,-432.33 289,-432.33 289,-437.67 283.67,-443 278.33,-443 278.33,-443 267.67,-443 267.67,-443 262.33,-443 257,-437.67 257,-432.33 257,-432.33 257,-421.67 257,-421.67 257,-416.33 262.33,-411 267.67,-411\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- length&#45;5871781006564002453 -->\n",
"<g id=\"node10\" class=\"node\">\n",
"<title>length&#45;5871781006564002453</title>\n",
"<g id=\"a_node10\"><a xlink:title=\"i64&#45;11743562013128004906: length&#45;5871781006564002453\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M214.17,-253C214.17,-253 239.83,-253 239.83,-253 245.17,-253 250.5,-258.33 250.5,-263.67 250.5,-263.67 250.5,-274.33 250.5,-274.33 250.5,-279.67 245.17,-285 239.83,-285 239.83,-285 214.17,-285 214.17,-285 208.83,-285 203.5,-279.67 203.5,-274.33 203.5,-274.33 203.5,-263.67 203.5,-263.67 203.5,-258.33 208.83,-253 214.17,-253\"/>\n",
"<text text-anchor=\"start\" x=\"209\" y=\"-265.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">length</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M214.17,-253C214.17,-253 239.83,-253 239.83,-253 245.17,-253 250.5,-258.33 250.5,-263.67 250.5,-263.67 250.5,-274.33 250.5,-274.33 250.5,-279.67 245.17,-285 239.83,-285 239.83,-285 214.17,-285 214.17,-285 208.83,-285 203.5,-279.67 203.5,-274.33 203.5,-274.33 203.5,-263.67 203.5,-263.67 203.5,-258.33 208.83,-253 214.17,-253\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- length&#45;16783941965674463102 -->\n",
"<g id=\"node11\" class=\"node\">\n",
"<title>length&#45;16783941965674463102</title>\n",
"<g id=\"a_node11\"><a xlink:title=\"i64&#45;11743562013128004906: length&#45;16783941965674463102\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M98.17,-253C98.17,-253 123.83,-253 123.83,-253 129.17,-253 134.5,-258.33 134.5,-263.67 134.5,-263.67 134.5,-274.33 134.5,-274.33 134.5,-279.67 129.17,-285 123.83,-285 123.83,-285 98.17,-285 98.17,-285 92.83,-285 87.5,-279.67 87.5,-274.33 87.5,-274.33 87.5,-263.67 87.5,-263.67 87.5,-258.33 92.83,-253 98.17,-253\"/>\n",
"<text text-anchor=\"start\" x=\"93\" y=\"-265.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">length</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M98.17,-253C98.17,-253 123.83,-253 123.83,-253 129.17,-253 134.5,-258.33 134.5,-263.67 134.5,-263.67 134.5,-274.33 134.5,-274.33 134.5,-279.67 129.17,-285 123.83,-285 123.83,-285 98.17,-285 98.17,-285 92.83,-285 87.5,-279.67 87.5,-274.33 87.5,-274.33 87.5,-263.67 87.5,-263.67 87.5,-258.33 92.83,-253 98.17,-253\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- Shape_to_tuple&#45;10912160959110460649 -->\n",
"<g id=\"node12\" class=\"node\">\n",
"<title>Shape_to_tuple&#45;10912160959110460649</title>\n",
"<g id=\"a_node12\"><a xlink:title=\"6: Shape_to_tuple&#45;10912160959110460649\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M25.17,-174C25.17,-174 108.83,-174 108.83,-174 114.17,-174 119.5,-179.33 119.5,-184.67 119.5,-184.67 119.5,-195.33 119.5,-195.33 119.5,-200.67 114.17,-206 108.83,-206 108.83,-206 25.17,-206 25.17,-206 19.83,-206 14.5,-200.67 14.5,-195.33 14.5,-195.33 14.5,-184.67 14.5,-184.67 14.5,-179.33 19.83,-174 25.17,-174\"/>\n",
"<text text-anchor=\"start\" x=\"20\" y=\"-186.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">Shape_to_tuple</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M25.17,-174C25.17,-174 108.83,-174 108.83,-174 114.17,-174 119.5,-179.33 119.5,-184.67 119.5,-184.67 119.5,-195.33 119.5,-195.33 119.5,-200.67 114.17,-206 108.83,-206 108.83,-206 25.17,-206 25.17,-206 19.83,-206 14.5,-200.67 14.5,-195.33 14.5,-195.33 14.5,-184.67 14.5,-184.67 14.5,-179.33 19.83,-174 25.17,-174\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- ndim&#45;10912160959110460649 -->\n",
"<g id=\"node13\" class=\"node\">\n",
"<title>ndim&#45;10912160959110460649</title>\n",
"<g id=\"a_node13\"><a xlink:title=\"i64&#45;11743562013128004906: ndim&#45;10912160959110460649\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M159.17,-253C159.17,-253 178.83,-253 178.83,-253 184.17,-253 189.5,-258.33 189.5,-263.67 189.5,-263.67 189.5,-274.33 189.5,-274.33 189.5,-279.67 184.17,-285 178.83,-285 178.83,-285 159.17,-285 159.17,-285 153.83,-285 148.5,-279.67 148.5,-274.33 148.5,-274.33 148.5,-263.67 148.5,-263.67 148.5,-258.33 153.83,-253 159.17,-253\"/>\n",
"<text text-anchor=\"start\" x=\"154\" y=\"-265.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">ndim</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M159.17,-253C159.17,-253 178.83,-253 178.83,-253 184.17,-253 189.5,-258.33 189.5,-263.67 189.5,-263.67 189.5,-274.33 189.5,-274.33 189.5,-279.67 184.17,-285 178.83,-285 178.83,-285 159.17,-285 159.17,-285 153.83,-285 148.5,-279.67 148.5,-274.33 148.5,-274.33 148.5,-263.67 148.5,-263.67 148.5,-258.33 153.83,-253 159.17,-253\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- i64&#45;11743562013128004906 -->\n",
"<g id=\"node14\" class=\"node\">\n",
"<title>i64&#45;11743562013128004906</title>\n",
"<g id=\"a_node14\"><a xlink:title=\"i64&#45;11743562013128004906: i64&#45;11743562013128004906\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M279.67,-253C279.67,-253 290.33,-253 290.33,-253 295.67,-253 301,-258.33 301,-263.67 301,-263.67 301,-274.33 301,-274.33 301,-279.67 295.67,-285 290.33,-285 290.33,-285 279.67,-285 279.67,-285 274.33,-285 269,-279.67 269,-274.33 269,-274.33 269,-263.67 269,-263.67 269,-258.33 274.33,-253 279.67,-253\"/>\n",
"<text text-anchor=\"start\" x=\"281\" y=\"-265.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">2</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M279.67,-253C279.67,-253 290.33,-253 290.33,-253 295.67,-253 301,-258.33 301,-263.67 301,-263.67 301,-274.33 301,-274.33 301,-279.67 295.67,-285 290.33,-285 290.33,-285 279.67,-285 279.67,-285 274.33,-285 269,-279.67 269,-274.33 269,-274.33 269,-263.67 269,-263.67 269,-258.33 274.33,-253 279.67,-253\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- length&#45;0 -->\n",
"<g id=\"node15\" class=\"node\">\n",
"<title>length&#45;0</title>\n",
"<g id=\"a_node15\"><a xlink:title=\"i64&#45;17615343019692007359: length&#45;0\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M56.17,-95C56.17,-95 81.83,-95 81.83,-95 87.17,-95 92.5,-100.33 92.5,-105.67 92.5,-105.67 92.5,-116.33 92.5,-116.33 92.5,-121.67 87.17,-127 81.83,-127 81.83,-127 56.17,-127 56.17,-127 50.83,-127 45.5,-121.67 45.5,-116.33 45.5,-116.33 45.5,-105.67 45.5,-105.67 45.5,-100.33 50.83,-95 56.17,-95\"/>\n",
"<text text-anchor=\"start\" x=\"51\" y=\"-107.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">length</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M56.17,-95C56.17,-95 81.83,-95 81.83,-95 87.17,-95 92.5,-100.33 92.5,-105.67 92.5,-105.67 92.5,-116.33 92.5,-116.33 92.5,-121.67 87.17,-127 81.83,-127 81.83,-127 56.17,-127 56.17,-127 50.83,-127 45.5,-121.67 45.5,-116.33 45.5,-116.33 45.5,-105.67 45.5,-105.67 45.5,-100.33 50.83,-95 56.17,-95\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- ndim&#45;11743562013128004906 -->\n",
"<g id=\"node16\" class=\"node\">\n",
"<title>ndim&#45;11743562013128004906</title>\n",
"<g id=\"a_node16\"><a xlink:title=\"i64&#45;17615343019692007359: ndim&#45;11743562013128004906\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M117.17,-95C117.17,-95 136.83,-95 136.83,-95 142.17,-95 147.5,-100.33 147.5,-105.67 147.5,-105.67 147.5,-116.33 147.5,-116.33 147.5,-121.67 142.17,-127 136.83,-127 136.83,-127 117.17,-127 117.17,-127 111.83,-127 106.5,-121.67 106.5,-116.33 106.5,-116.33 106.5,-105.67 106.5,-105.67 106.5,-100.33 111.83,-95 117.17,-95\"/>\n",
"<text text-anchor=\"start\" x=\"112\" y=\"-107.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">ndim</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M117.17,-95C117.17,-95 136.83,-95 136.83,-95 142.17,-95 147.5,-100.33 147.5,-105.67 147.5,-105.67 147.5,-116.33 147.5,-116.33 147.5,-121.67 142.17,-127 136.83,-127 136.83,-127 117.17,-127 117.17,-127 111.83,-127 106.5,-121.67 106.5,-116.33 106.5,-116.33 106.5,-105.67 106.5,-105.67 106.5,-100.33 111.83,-95 117.17,-95\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"<!-- i64&#45;17615343019692007359 -->\n",
"<g id=\"node17\" class=\"node\">\n",
"<title>i64&#45;17615343019692007359</title>\n",
"<g id=\"a_node17\"><a xlink:title=\"i64&#45;17615343019692007359: i64&#45;17615343019692007359\">\n",
"<path fill=\"white\" stroke=\"transparent\" d=\"M179.67,-95C179.67,-95 190.33,-95 190.33,-95 195.67,-95 201,-100.33 201,-105.67 201,-105.67 201,-116.33 201,-116.33 201,-121.67 195.67,-127 190.33,-127 190.33,-127 179.67,-127 179.67,-127 174.33,-127 169,-121.67 169,-116.33 169,-116.33 169,-105.67 169,-105.67 169,-100.33 174.33,-95 179.67,-95\"/>\n",
"<text text-anchor=\"start\" x=\"181\" y=\"-107.3\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">3</text>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M179.67,-95C179.67,-95 190.33,-95 190.33,-95 195.67,-95 201,-100.33 201,-105.67 201,-105.67 201,-116.33 201,-116.33 201,-121.67 195.67,-127 190.33,-127 190.33,-127 179.67,-127 179.67,-127 174.33,-127 169,-121.67 169,-116.33 169,-116.33 169,-105.67 169,-105.67 169,-100.33 174.33,-95 179.67,-95\"/>\n",
"</a>\n",
"</g>\n",
"</g>\n",
"</g>\n",
"</svg>\n"
],
"text/plain": [
"EGraph(_flatted_deps=[], _mod_decls=ModuleDeclarations(_decl=Declarations(_functions={'ndim': FunctionDecl(arg_types=(TypeRefWithVars(name='Shape', args=()),), arg_names=('x',), arg_defaults=(None,), return_type=TypeRefWithVars(name='i64', args=()), mutates_first_arg=False, var_arg_type=None), 'length': FunctionDecl(arg_types=(TypeRefWithVars(name='Tuple', args=()),), arg_names=('x',), arg_defaults=(None,), return_type=TypeRefWithVars(name='i64', args=()), mutates_first_arg=False, var_arg_type=None)}, _classes={'Tuple': ClassDecl(methods={}, class_methods={'__init__': FunctionDecl(arg_types=(TypeRefWithVars(name='i64', args=()),), arg_names=('nd',), arg_defaults=(None,), return_type=TypeRefWithVars(name='Tuple', args=()), mutates_first_arg=False, var_arg_type=None), 'symbolic': FunctionDecl(arg_types=(TypeRefWithVars(name='String', args=()),), arg_names=('name',), arg_defaults=(None,), return_type=TypeRefWithVars(name='Tuple', args=()), mutates_first_arg=False, var_arg_type=None)}, class_variables={}, properties={}, preserved_methods={}, n_type_vars=0), 'Shape': ClassDecl(methods={'deselect': FunctionDecl(arg_types=(TypeRefWithVars(name='Shape', args=()), TypeRefWithVars(name='Tuple', args=())), arg_names=('self', 'axis'), arg_defaults=(None, None), return_type=TypeRefWithVars(name='Shape', args=()), mutates_first_arg=False, var_arg_type=None), 'select': FunctionDecl(arg_types=(TypeRefWithVars(name='Shape', args=()), TypeRefWithVars(name='Tuple', args=())), arg_names=('self', 'axis'), arg_defaults=(None, None), return_type=TypeRefWithVars(name='Shape', args=()), mutates_first_arg=False, var_arg_type=None), 'to_tuple': FunctionDecl(arg_types=(TypeRefWithVars(name='Shape', args=()),), arg_names=('self',), arg_defaults=(None,), return_type=TypeRefWithVars(name='Tuple', args=()), mutates_first_arg=False, var_arg_type=None)}, class_methods={'__init__': FunctionDecl(arg_types=(TypeRefWithVars(name='Tuple', args=()),), arg_names=('tup',), arg_defaults=(None,), return_type=TypeRefWithVars(name='Shape', args=()), mutates_first_arg=False, var_arg_type=None)}, class_variables={}, properties={}, preserved_methods={}, n_type_vars=0)}, _constants={}, _egg_fn_to_callable_refs=defaultdict(<class 'set'>, {'Tuple___init__': {ClassMethodRef(class_name='Tuple', method_name='__init__')}, 'Tuple_symbolic': {ClassMethodRef(class_name='Tuple', method_name='symbolic')}, '!=': {MethodRef(class_name='Shape', method_name='__ne__'), MethodRef(class_name='Tuple', method_name='__ne__')}, 'Shape___init__': {ClassMethodRef(class_name='Shape', method_name='__init__')}, 'Shape_deselect': {MethodRef(class_name='Shape', method_name='deselect')}, 'Shape_select': {MethodRef(class_name='Shape', method_name='select')}, 'Shape_to_tuple': {MethodRef(class_name='Shape', method_name='to_tuple')}, 'ndim': {FunctionRef(name='ndim')}, 'length': {FunctionRef(name='length')}}), _callable_ref_to_egg_fn={ClassMethodRef(class_name='Tuple', method_name='__init__'): 'Tuple___init__', ClassMethodRef(class_name='Tuple', method_name='symbolic'): 'Tuple_symbolic', MethodRef(class_name='Tuple', method_name='__ne__'): '!=', ClassMethodRef(class_name='Shape', method_name='__init__'): 'Shape___init__', MethodRef(class_name='Shape', method_name='deselect'): 'Shape_deselect', MethodRef(class_name='Shape', method_name='select'): 'Shape_select', MethodRef(class_name='Shape', method_name='to_tuple'): 'Shape_to_tuple', MethodRef(class_name='Shape', method_name='__ne__'): '!=', FunctionRef(name='ndim'): 'ndim', FunctionRef(name='length'): 'length'}, _egg_sort_to_type_ref={'Tuple': JustTypeRef(name='Tuple', args=()), 'Shape': JustTypeRef(name='Shape', args=())}, _type_ref_to_egg_sort={JustTypeRef(name='Tuple', args=()): 'Tuple', JustTypeRef(name='Shape', args=()): 'Shape'})), _token=None)"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"egraph"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b9283114",
"metadata": {},
"outputs": [],
"source": []
}
],
"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.11.5"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
@saulshanabrook
Copy link

saulshanabrook commented Feb 8, 2024

I am coming back to this after finishing some refactoring work on egglog and wanted to check to see if I am understanding things properly before trying to implement the features needed for this.

Thank you again for posting this example! I am excited about getting this sort of thing to work and it's essential to have real use cases like this to drive the development...


Let's say we implement an API like you proposed on top of the existing array api bindings:

from typing import Callable, ClassVar, Iterator

import numpy as np

import egglog.exp.array_api as enp
from egglog import Expr, convert, rewrite, ruleset


class ShapeAPI(Expr):
    def __init__(self, dims: enp.TupleInt) -> None:
        ...

    def deselect(self, axis: enp.TupleInt) -> ShapeAPI:
        ...

    def select(self, axis: enp.TupleInt) -> ShapeAPI:
        ...

    def to_tuple(self) -> enp.TupleInt:
        ...

class OptionalLoopNestAPI(Expr):
    def __init__(self, value: LoopNestAPI) -> None:
        ...

    NONE: ClassVar[OptionalLoopNestAPI]

    def unwrap(self) -> LoopNestAPI:
        ...


class LoopNestAPI(Expr):
    def __init__(self, dim: enp.Int, inner: OptionalLoopNestAPI = OptionalLoopNestAPI.NONE) -> None:
        ...

    @classmethod
    def from_tuple(cls, args: enp.TupleInt) -> OptionalLoopNestAPI:
        ...

    def __iter__(self) -> Iterator[enp.TupleInt]:
        return iter(self.indices)

    @property
    def indices(self) -> enp.TupleTupleInt:
        ...

    def get_dims(self) -> enp.TupleInt:
        ...

    def reduce(self, fn: Callable[[enp.NDArray, enp.TupleInt], enp.NDArray], init: enp.NDArray) -> enp.NDArray:
        ...

With that, a user could write a loopnest function with it:

def linalg_norm_loopnest_egglog(X: enp.NDArray, axis: enp.TupleInt) -> enp.NDArray:
    # peel off the outer shape for result array
    outshape = ShapeAPI(X.shape).deselect(axis).to_tuple()
    # get only the inner shape for reduction
    reduce_axis = ShapeAPI(X.shape).select(axis).to_tuple()

    return enp.NDArray.from_fn(
        outshape,
        X.dtype,
        lambda k: enp.sqrt(
            LoopNestAPI.from_tuple(reduce_axis)
            .unwrap()
            .reduce(lambda carry, i: carry + enp.real(enp.conj(x := X[i + k]) * x), init=0.0)
        ).to_value(),
    )

By applying some rewrite rules (included below in a dropdown), we could translate away the custom APIs and end up with something like this expression:

def linalg_norm_array_api(X: enp.NDArray, axis: enp.TupleInt) -> enp.NDArray:
    outdim = enp.range_(X.ndim).filter(lambda x: ~axis.contains(x))
    outshape = convert(convert(X.shape, enp.NDArray)[outdim], enp.TupleInt)
    row_axis, col_axis = axis
    return enp.NDArray.from_fn(
        outshape,
        X.dtype,
        lambda k: enp.sqrt(
            enp.int_product(enp.range_(X.shape[row_axis]), enp.range_(X.shape[col_axis]))
            .map_to_ndarray(lambda rc: enp.real(enp.conj(x := X[rc + k]) * x))
            .sum()
        ).to_value(),
    )

Then, using the existing source generation, we could create this Python source from that function:

def linalg_norm_low_level(
    X: np.ndarray[tuple, np.dtype[np.float64]], axis: tuple[int, int]
) -> np.ndarray[tuple, np.dtype[np.float64]]:
    # # If X ndim>=3 and axis is a 2-tuple
    assert X.ndim >= 3
    assert len(axis) == 2
    #  Y - 2
    outdim = [dim for dim in range(X.ndim) if dim not in axis]

    outshape = tuple(np.asarray(X.shape)[outdim])

    res = np.zeros(outshape, dtype=X.dtype)
    row_axis, col_axis = axis
    for k in np.ndindex(outshape):
        tmp = 0.0
        for row in range(X.shape[row_axis]):
            for col in range(X.shape[col_axis]):
                idx = (row, col, *k)
                x = X[idx]
                tmp += (x.conj() * x).real
        res[k] = np.sqrt(tmp)
    return res

Does that seem about right?

Implementation

I haven't tried any of this code, and to implement it would require adding functions as values to egglog. I believe this could be done without any (or minimal) changes to the core, but before starting on it, I wanted to check in to see if this use case is accurate enough to give it a go.

Also implementing generic functions and types would go a long way in making the code a bit more fluid, but in the examples I put here, I assume we don't have that feature yet. It should all still be possible without it, just more repetition.

I believe I could add them as a Python pre-processor and this use case would also be a good model to show how they help with readability.


Rewrite rules:
@ruleset
def shape_api_ruleset(dims: enp.TupleInt, axis: enp.TupleInt):  # noqa: ANN201
    yield rewrite(ShapeAPI(dims).deselect(axis)).to(
        ShapeAPI(enp.range_(dims.length()).filter(lambda i: ~axis.contains(i)).map(lambda i: dims[i]))
    )
    yield rewrite(ShapeAPI(dims).select(axis)).to(
        ShapeAPI(enp.range_(dims.length()).filter(lambda i: axis.contains(i)).map(lambda i: dims[i]))
    )
    yield rewrite(ShapeAPI(dims).to_tuple()).to(dims)


@ruleset
def loopnest_api_ruleset(
    head: enp.Int,
    tail: enp.TupleInt,
    lna: LoopNestAPI,
    fn: Callable[[enp.NDArray, enp.TupleInt], enp.NDArray],
    init: enp.NDArray,
    dim: enp.Int,
):
    # from_tuple
    yield rewrite(LoopNestAPI.from_tuple(enp.TupleInt.EMPTY)).to(OptionalLoopNestAPI.NONE)
    yield rewrite(
        LoopNestAPI.from_tuple(enp.TupleInt.some(head, tail)),
    ).to(
        OptionalLoopNestAPI(LoopNestAPI(head, LoopNestAPI.from_tuple(tail))),
    )
    # reduce
    yield rewrite(lna.reduce(fn, init)).to(lna.indices.reduce_ndarray(fn, init))
    # get_dims
    yield rewrite(LoopNestAPI(dim, OptionalLoopNestAPI.NONE).get_dims()).to(enp.TupleInt(dim))
    yield rewrite(LoopNestAPI(dim, OptionalLoopNestAPI(lna)).get_dims()).to(enp.TupleInt(dim) + lna.get_dims())
    # indices
    yield rewrite(lna.indices).to(lna.get_dims().map_tuple_int(enp.range_).product())

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment