From 40f67901b5f534f2b044988076571800f49c7c6c Mon Sep 17 00:00:00 2001 From: Leif Van Holland Date: Wed, 28 Jan 2026 10:20:38 +0000 Subject: [PATCH] updated readme and fixed typing tests --- .gitignore | 3 +- .pre-commit-config.yaml | 1 + README.md | 107 +++++++------ .../benchmark_overhead_additional_args_0.png | Bin 0 -> 24302 bytes .../benchmark_overhead_additional_args_10.png | Bin 0 -> 24933 bytes ...benchmark_overhead_additional_args_100.png | Bin 0 -> 25203 bytes noxfile.py | 1 - setup.py | 2 +- speedtest.py | 137 ++++++++++------ speedtest_plot.ipynb | 149 ++++++++++++++++++ src/tensor_shape_assert/__init__.py | 2 +- src/tensor_shape_assert/types.py | 13 +- src/tensor_shape_assert_test.py | 22 +-- src/tensor_shape_assert_typesafe_test.py | 78 ++++----- src/test_utils.py | 19 +++ 15 files changed, 369 insertions(+), 165 deletions(-) create mode 100644 .pre-commit-config.yaml create mode 100644 assets/benchmark_overhead_additional_args_0.png create mode 100644 assets/benchmark_overhead_additional_args_10.png create mode 100644 assets/benchmark_overhead_additional_args_100.png create mode 100644 speedtest_plot.ipynb create mode 100644 src/test_utils.py diff --git a/.gitignore b/.gitignore index 5e5bbf0..e89da07 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ build **/.nox **/.pytest_cache .vscode -**/.mypy_cache \ No newline at end of file +**/.mypy_cache +benchmark_results.csv \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..a62c209 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1 @@ +repos: [] \ No newline at end of file diff --git a/README.md b/README.md index 1bc60ec..e063d44 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,45 @@ A minimal utility library that * supports any array class that exposes a ``shape`` property, * and a lot more! +## Installation + +Currently, the package can only be installed directly from the repository with +```bash +pip install git+https://github.com/leifvan/tensor-shape-assert +``` + ## Usage + Decorate functions with ``@check_tensor_shapes()`` and any parameter with a type hint of type ``ShapedTensor[]`` will be dynamically checked for the correct shape. A shape descriptor is a *string* of space-separated length descriptions for each dimension. The return value can also be annotated in the same way. +### Simple example + +```python +import torch +from .tensor_shape_assert import check_tensor_shapes, ShapedTensor + +@check_tensor_shapes() +def my_simple_func( + x: ShapedTensor["a b 3"], + y: ShapedTensor["b 2"] +) -> ShapedTensor["a"]: + + z = x[:, :, :2] + y[None] + return (z[:, :, 0] * z[:, :, 1]).sum(dim=1) +``` + +Calling it like this +```python +my_simple_func(torch.zeros(5, 4, 3), y=torch.zeros(4, 2)) # works +``` +passes the test, because ``a=5 and b=4`` matches for both input and output annotations. + +For +```python +my_simple_func(torch.zeros(5, 4, 3), y=torch.zeros(4, 3)) # fails +``` +the test fails, because `y` is expected to have length 2 in the second dimension. + ### Integers * Sizes can be defined explicitly as an integer, e.g. ``"5 3"`` (only arrays of shape ``(5, 3)`` are valid) @@ -44,51 +80,11 @@ There are convenience functions that access the current states of the shape vari You can even go one step further and do a check tensors inside the wrapped function directly with ``assert_shape_here(x, )``, which will run a check on the object or shape ``x`` given the descriptor and add previously unseen variables in the descriptor to the state inside the wrapped function. This way you can check the output of the function against tensor shapes that only appear in the body of the function. -## Installation - -Currently, the package can only be installed directly from the repository with -```bash -pip install git+https://github.com/leifvan/tensor-shape-assert -``` - ## Compatibility While the examples below are using PyTorch, *tensor-shape-assert* requires very minimal functionality and is compatible with any array class that has a ``shape`` method, which includes popular frameworks such as NumPy, TensorFlow, Jax and more generally frameworks that conform to the [Python array API standard](https://data-apis.org/array-api/latest/). -## Examples - -Here are two examples that demonstrate how the annotation works. - -### Simple example - -```python -import torch -from .tensor_shape_assert import check_tensor_shapes, ShapedTensor - -@check_tensor_shapes() -def my_simple_func( - x: ShapedTensor["a b 3"], - y: ShapedTensor["b 2"] -) -> ShapedTensor["a"]: - - z = x[:, :, :2] + y[None] - return (z[:, :, 0] * z[:, :, 1]).sum(dim=1) -``` - -Calling it like this -```python -my_simple_func(torch.zeros(5, 4, 3), y=torch.zeros(4, 2)) # works -``` -passes the test, because ``a=5 and b=4`` matches for both input and output annotations. - -For -```python -my_simple_func(torch.zeros(5, 4, 3), y=torch.zeros(4, 3)) # fails -``` -the test fails, because `y` is expected to have length 2 in the second dimension. - ---- -### Complex example +## More Examples The complex example additionally contains tuple and optional annotations. ```python @@ -186,6 +182,25 @@ def my_func(x: ShapedTensor["n k"], k: int): my_func(torch.zeros(10, 2), k=2) # works my_func(torch.zeros(10, 2), k=3) # works ``` +--- +### Type safety (new in version 0.3) + +If you are using static type checkers like MyPy, you can use the more verbose but type safe literal syntax. For this, you are also required to specify the array type. You can either do this manually with ``ShapedLiteral``, or use the predefined aliases ``ShapedTorchLiteral``, ``ShapedNumpyLiteral``. The example will show both options for PyTorch. + +```python +from typing import Literal as L + +@check_tensor_shapes() +def my_simple_func( + x: ShapedTorchLiteral[L["a b 3"]], + y: ShapedTorchLiteral[L["b 2"]] +) -> ShapedLiteral[torch.Tensor, L["a"]]: + + z = x[:, :, :2] + y[None] + return (z[:, :, 0] * z[:, :, 1]).sum(dim=1) +``` + +Another benefit from using the typed version is that tooltips in VS Code are more helpful, as they can pass trough the ``Literal`` string. This way you can check the annotated shape without having to open the file with the annotated code. ## Known bugs * [ ] ``get_shape_variables`` does not work if checks are disabled. This should be possible but give a performance warning, recommending not to use this feature in performance-critical applications. @@ -208,7 +223,7 @@ reraise * [x] donnx * [x] sparse * ~~[ ] cupy~~ (we leave this out for now because it requires CUDA) -* [ ] check compatibility with static type checkers +* [x] check compatibility with static type checkers * [ ] rewrite README to give a cleaner overview over the features * [ ] support union of shape descriptors (but this might break the current simplicity) * [ ] benchmark speed to understand impact in tight loops @@ -216,11 +231,11 @@ reraise * ~~[ ] device annotation~~ (device definition not standardized in Python array API 2024.12, see [this section of the specifications](https://data-apis.org/array-api/2024.12/design_topics/device_support.html#device-support)) * [ ] add variable names for dtype * [ ] add more helpful message when parameter / output is not the expected type -* [ ] (maybe instead of the one before) catch and reraise all exceptions inside +* [x] (maybe instead of the one before) catch and reraise all exceptions inside wrapper and reraise with additional info about exception location -* [ ] improve hints for static type checking (currently it assumes either torch +* [x] improve hints for static type checking (currently it assumes either torch or the object just having a .shape parameter) -* [ ] come up with a way to allow union of shape descriptors * [ ] make get_shape_variables work in check modes "never" and "once" without performance overhead -* [ ] work on performance overhead in general \ No newline at end of file +* [ ] work on performance overhead in general + diff --git a/assets/benchmark_overhead_additional_args_0.png b/assets/benchmark_overhead_additional_args_0.png new file mode 100644 index 0000000000000000000000000000000000000000..d0abadb50dd49ccc8dcae91a0ee0fb08cc4357c6 GIT binary patch literal 24302 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 literal 0 HcmV?d00001 diff --git a/noxfile.py b/noxfile.py index 6e87c4f..ed760c2 100644 --- a/noxfile.py +++ b/noxfile.py @@ -41,4 +41,3 @@ def typecheck(session): session.install("numpy", "torch") session.install("mypy") session.run("mypy", "src") - diff --git a/setup.py b/setup.py index 956a480..b394d6e 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ setup( name="tensor-shape-assert", - version="0.2.6", + version="0.3.0", description="A simple runtime assert library for tensor-based frameworks.", long_description=long_description, long_description_content_type="text/markdown", diff --git a/speedtest.py b/speedtest.py index 329773d..b9588b4 100644 --- a/speedtest.py +++ b/speedtest.py @@ -1,61 +1,96 @@ -from src.tensor_shape_assert import ShapedTensor, check_tensor_shapes, set_global_check_mode -import torch +from src.tensor_shape_assert import ShapedTensor, check_tensor_shapes +from test_utils import get_library_by_name, NAME_LIBRARY_MAP from time import time -from typing import NamedTuple +from tqdm import tqdm +from tabulate import tabulate -def benchmark(f, num_runs, num_additonal_args): - start_time = time() - - add = [torch.zeros(10, 5) for _ in range(num_additonal_args)] - - for _ in range(num_runs): - f( - torch.zeros((10, 20, 30)), - torch.zeros((20, 2)), - *add - ) - return (time() - start_time) / num_runs - -def func(x: ShapedTensor["a b c"], y: ShapedTensor["b d"], *args: tuple[ShapedTensor["10 5"], ...]) -> ShapedTensor["a d"]: - k = sum(args) - z = x[..., None] * y[None, :, None] # a b c d - return z.sum(dim=(1, 2)) # a d - -if __name__ == "__main__": - - num_runs = 10000 - for num_add_args in (0, 10, 100): +def benchmark(f, num_additonal_args, xp): - func_always = check_tensor_shapes(check_mode="always")(func) - func_once = check_tensor_shapes(check_mode="once")(func) - func_never = check_tensor_shapes(check_mode="never")(func) + # prepare args + x = xp.zeros((10, 20, 30)) + y = xp.zeros((20, 2)) + add = [xp.zeros((10, 5)) for _ in range(num_additonal_args)] - # full shape checking + # some warmup + for _ in range(2 ** 7): + f(x, y, *add) - duration_with = benchmark(func_always, num_runs=num_runs, num_additonal_args=num_add_args) - - # check once - - set_global_check_mode("once") - - duration_global_check_once = benchmark(func_once, num_runs=num_runs, num_additonal_args=num_add_args) - - # disabled global checking - - set_global_check_mode("never") - - duration_global_check_never = benchmark(func_never, num_runs=num_runs, num_additonal_args=num_add_args) + # actual benchmark + start_time = time() + for i in tqdm(range(2 ** 16)): + f(x, y, *add) - # no annotations + if i % 500 == 0: + if time() - start_time > 5: + break + return (time() - start_time) / (i + 1) # type: ignore - duration_not_annotated = benchmark(func, num_runs=num_runs, num_additonal_args=num_add_args) +def func( + x: ShapedTensor["a b c"], + y: ShapedTensor["b d"], + *args: tuple[ShapedTensor["10 5"], ...] +) -> ShapedTensor["a d"]: + z = x[:, :, :, None] * y[None, :, None] + return z.sum(axis=(1, 2)) # type: ignore - # print results and compute percentage - print(f"\nBenchmarking with {num_add_args} additional arguments:") - print(f"Duration without annotations: {duration_not_annotated*1000:.4f} ms, 100.00%") - print(f"Duration with global check never: {duration_global_check_never*1000:.4f} ms, {duration_global_check_never/duration_not_annotated*100:5.2f}%") - print(f"Duration with global check once: {duration_global_check_once*1000:.4f} ms, {duration_global_check_once/duration_not_annotated*100:5.2f}%") - print(f"Duration with shape checking: {duration_with*1000:.4f} ms, {duration_with/duration_not_annotated*100:5.2f}%") - print("-" * 50) \ No newline at end of file +if __name__ == "__main__": + func_always = check_tensor_shapes(check_mode="always")(func) + func_once = check_tensor_shapes(check_mode="once")(func) + func_never = check_tensor_shapes(check_mode="never")(func) + + results = [] + + def add_results(lib, num_add_args, mode, duration): + + if mode == "not_annotated": + duration_not_annotated = duration + else: + duration_not_annotated = None + for r in results: + if r["library"] == lib and r["additional args"] == num_add_args and r["check mode"] == "not_annotated": + duration_not_annotated = r["duration (ms)"] / 1000 + break + + results.append({ + "library": lib, + "additional args": num_add_args, + "check mode": mode, + "duration (ms)": duration * 1000, + "overhead (ms)": (duration - duration_not_annotated) * 1000, + "relative (%)": duration / duration_not_annotated + }) + + for lib in NAME_LIBRARY_MAP.keys(): + try: + xp = get_library_by_name(lib) + except ModuleNotFoundError: + print("skipping", lib, ", not installed") + continue + + try: + for num_add_args in (0, 10, 100): + + add_results(lib, num_add_args, "not_annotated", benchmark(func, num_additonal_args=num_add_args, xp=xp)) + add_results(lib, num_add_args, "never", benchmark(func_never, num_additonal_args=num_add_args, xp=xp)) + add_results(lib, num_add_args, "once", benchmark(func_once, num_additonal_args=num_add_args, xp=xp)) + add_results(lib, num_add_args, "always", benchmark(func_always, num_additonal_args=num_add_args, xp=xp)) + + # if lib == "torch": + # import torch + # add_results("torch-compile", num_add_args, "not_annotated", benchmark(torch.compile(func), num_additonal_args=num_add_args, xp=xp)) + # add_results("torch-compile", num_add_args, "never", benchmark(torch.compile(func_never), num_additonal_args=num_add_args, xp=xp)) + # add_results("torch-compile", num_add_args, "once", benchmark(torch.compile(func_once), num_additonal_args=num_add_args, xp=xp)) + # add_results("torch-compile", num_add_args, "always", benchmark(torch.compile(func_always), num_additonal_args=num_add_args, xp=xp)) + + print(tabulate(results, tablefmt="github", floatfmt=(None, None, None, ".5f", ".4e", ".2%"))) # type: ignore + except Exception as e: + print("Error benchmarking", lib, ":", e) + + # write results to csv file + + with open("benchmark_results.csv", "w") as f: + f.write("library,additional args,check mode,duration (ms),relative (%),overhead (ms)\n") + for r in results: + f.write(f"{r['library']},{r['additional args']},{r['check mode']},{r['duration (ms)']:.5f},{r['relative (%)']:.2%},{r['overhead (ms)']:.10f}\n") diff --git a/speedtest_plot.ipynb b/speedtest_plot.ipynb new file mode 100644 index 0000000..7f54d15 --- /dev/null +++ b/speedtest_plot.ipynb @@ -0,0 +1,149 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "5c9b03cc", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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6Orq4sGDBwrLHzx4wJu7EhER0Tt7qyBUqVIlHD9+HEOHDsWECROk6/HIZDJ89NFH+Omnn1Q6uVhfXx/e3t7Yv38/OnfuDACQy+XYv38/hg0bprLtEBERkXZ66ytLV6lSBbt378aTJ08QHx8PAKhevXqJ5walp6fj+vXr0uPExETExMTA2toalStXRmhoKIKCguDj44OGDRsiLCwMGRkZ6NevX4m2V1zh4eEIDw9HXl6eWrdDREREmlOiW2wAgJWVFRo2bPjOBZw+fRqtWrWSHoeGhgIAgoKCEBERgZ49e+LRo0eYNGkS7t+/Dy8vL0RGRhZ6HSBVCQkJQUhICNLS0mBhYaHWbREREZFmlDgIqYqfn1+Rt7wYNmwYD4WR9pqiwiA+5anq+iIiKgPe+l5jRERERGUFgxARERFpLQYhIiIi0loMQoUIDw+Hu7s7GjRooOlSiIiISE0YhAoREhKC2NhYREdHa7oUIiIiUhMGISIiItJaDEJERESktRiEiIiISGsxCBEREZHWYhAiIiIircUgVAiePk9ERFT2MQgVgqfPExERlX0MQkRERKS1GISIiIhIazEIERERkdZiECIiIiKtxSBEREREWotBiIiIiLQWg1AheB0hIiKiso9BqBC8jhAREVHZxyBEREREWotBiIiIiLRWOU0XQEQUPjhKZX2FLG2tsr6IqOzjiBARERFpLQYhIiIi0loMQkRERKS1GISIiIhIazEIERERkdZiECoEryxNRERU9jEIFYJXliYiIir7GISIiIhIazEIERERkdZiECIiIiKtxSBEREREWotBiIiIiLQWgxARERFpLQYhIiIi0loMQkRERKS1GISIiIhIazEIERERkdZiECoE7zVGRERU9jEIFYL3GiMiIir7GISIiIhIazEIERERkdZiECIiIiKtxSBEREREWotBiIiIiLQWgxARERFpLQYhIiIi0loMQkRERKS1GISIiIhIazEIERERkdZiECIiIiKtxSBEREREWotBiIiIiLQWg1AhwsPD4e7ujgYNGmi6FCIiIlITBqFChISEIDY2FtHR0ZouhYiIiNSEQYiIiIi0FoMQERERaS0GISIiItJaDEJERESktRiEiIiISGsxCBEREZHWYhAiIiIircUgRERERFqLQYiIiIi0FoMQERERaS0GISIiItJaDEJERESktRiEiIiISGsxCBEREZHWYhAiIiIircUgRERERFqLQYiIiIi0FoMQERERaS0GISIiItJaDEKFCA8Ph7u7Oxo0aKDpUoiIiEhNGIQKERISgtjYWERHR2u6FCIiIlITBiEiIiLSWgxCREREpLUYhIiIiEhrMQgRERGR1mIQIiIiIq3FIERERERai0GIiIiItBaDEBEREWktBiEiIiLSWgxCREREpLUYhIiIiEhrMQgRERGR1mIQIiIiIq3FIERERERai0GIiIiItBaDEBEREWktBiEiIiLSWgxCREREpLUYhIiIiEhrMQgRERGR1mIQIiIiIq3FIERERERai0GIiIiItBaDEBEREWktBiEiIiLSWgxCREREpLUYhIiIiEhrMQgRERGR1mIQIiIiIq2lNUEoMzMTzs7OGDNmjKZLISIioveE1gShmTNnonHjxpoug4iIiN4jWhGE4uPjceXKFbRr107TpRAREdF7RONB6PDhwwgICICjoyNkMhm2b9+u1CY8PBwuLi4wNDREo0aNcOrUqbfaxpgxYzB79mwVVUxERERlhcaDUEZGBurWrYvw8PAC12/YsAGhoaGYPHkyzp49i7p168Lf3x8PHz6U2nh5eaF27dpKP3fv3sWff/6JGjVqoEaNGqW1S0RERPSBKKfpAtq1a/fGQ1YLFizAwIED0a9fPwDA0qVLsXPnTqxYsQLjx48HAMTExBT6/BMnTmD9+vXYtGkT0tPTkZOTA3Nzc0yaNKnA9llZWcjKypIep6WllWCviIiI6EOg8SD0JtnZ2Thz5gwmTJggLdPR0UHbtm1x/PjxYvUxe/Zs6bBYREQELl26VGgIym8/derUdyu8mFzG71RZX0mGvVTWl2eVyirra+PsXJX1FeVX8KhhSYQsba2yvj4knqs8VdbXRpX1RESkORo/NPYmycnJyMvLg52dncJyOzs73L9/Xy3bnDBhAp4+fSr93L59Wy3bISIiIs17r0eEVC04OLjINgYGBjAwMFB/MURERKRx7/WIkK2tLXR1dfHgwQOF5Q8ePIC9vb2GqiIiIqKy4r0OQvr6+vD29sb+/fulZXK5HPv370eTJk00WBkRERGVBRo/NJaeno7r169LjxMTExETEwNra2tUrlwZoaGhCAoKgo+PDxo2bIiwsDBkZGRIZ5GpS3h4OMLDw5GXl6fW7RAREZHmaDwInT59Gq1atZIeh4aG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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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CADo3Lkz4uPj5ZY/f9fplStXllg/NDQUoaGhyi2SiIhIyThGSM0UFBQgPj4eR44cgYuLi6rLISIiUikGITVz4cIFeHl5wcXFBaNGjVJ1OURERCrFU2NlCA8PR3h4OAoLC1VdikK5u7vzsRhERET/H48IlSE4OBjx8fE4ceKEqkshIiIiJWEQIiIiIrXFIERERERqi0GIiIiI1BaDEBEREaktBiEiIiJSWwxCREREpLZ4HyElCB8VU6XbC17WsUq3R0REVF3wiBBViadPn6q6BCIiohIYhNTUpk2b4OrqCj09PVhYWKBz587IyclBUFAQevXqhVmzZqFmzZowNjbGqFGjkJ+fL60bFRWFdu3awdTUFBYWFujRoweSkpKk5SkpKZDJZNiwYQN8fHygq6uLtWvX4vr16/D394eZmRkMDAzg4uKCnTt3SutduHABXbt2haGhIaysrDB48GCkp6dX6etCRETqhUFIDd25cwcBAQEYNmwYEhISsH//fvTp00d64nx0dLQ0//fff8eWLVswa9Ysaf2cnByEhobi5MmTiI6OhoaGBnr37o2ioiK57UyZMgXjx49HQkIC/Pz8EBwcjLy8PBw8eBDnz5/HN998A0NDQwBAZmYmOnbsCA8PD5w8eRJRUVG4e/cu+vfvX3UvDBERqR2OEVJDd+7cQUFBAfr06QN7e3sAgKurq7RcW1sbK1euhL6+PlxcXDB79mxMmjQJc+bMgYaGBvr27SvX38qVK1GzZk3Ex8ejadOm0vwJEyagT58+0vSNGzfQt29faVv169eXlv3444/w8PDAV199JdevnZ0drly5gkaNGin2RSAiIgKPCKklNzc3dOrUCa6urujXrx9WrFiBBw8eyC3X19eXpr29vZGdnY2bN28CABITExEQEID69evD2NgYDg4OAJ4Fned5eXnJTY8bNw5z585F27ZtMWPGDJw7d05advbsWezbtw+GhobST5MmTQBA7rQbERGRIjEIqSFNTU38888/2LVrF5ydnbFkyRI0btwYycnJ5Vrf398fGRkZWLFiBY4dO4Zjx44BgNw4IgAwMDCQmx4xYgSuXbuGwYMH4/z58/Dy8sKSJUsAANnZ2fD390dcXJzcT2JiIjp06KCAvSYiIiqJQUhNyWQytG3bFrNmzcKZM2egra2NrVu3Anh2dObx48dS26NHj8LQ0BB2dna4f/8+Ll++jGnTpqFTp05wcnKSO5r0KnZ2dhg1ahS2bNmCzz77DCtWrAAANG/eHBcvXoSDgwMaNmwo9/NioCIiIlIUBiE1dOzYMXz11Vc4efIkbty4gS1btuDevXtwcnIC8OzIzvDhwxEfH4+dO3dixowZCAkJgYaGBszMzGBhYYHly5fj6tWriImJQWhoaLm2O2HCBOzevRvJyck4ffo09u3bJ20zODgYGRkZCAgIwIkTJ5CUlITdu3dj6NChKCwsVNprQURE6o2DpZXgTb/BobGxMQ4ePIiwsDBkZWXB3t4eixYtQteuXbFhwwZ06tQJjo6O6NChA/Ly8hAQEICZM2cCADQ0NLB+/XqMGzcOTZs2RePGjfHDDz/A19f3ldstLCxEcHAwbt26BWNjY7z33nv4/vvvAQC2traIjY3F5MmT0aVLF+Tl5cHe3h7vvfceNDSY14mISDkYhNSQk5MToqKiXtpm1qxZcpfMP69z586Ij4+Xm1d86T0AODg4yE0XKx4PVBZHR0ds2bLlpW2IiIgUiX9qExERkdpiECIiIiK1xVNjJCcyMlLVJRAREVUZHhEiIiIitcUjQgpQ2sBgqjp8/YneXOGjYhTW15t+RS69nXhE6DVoamoCKHlHZapaubm5AAAtLS0VV0JERG8bHhF6DTVq1IC+vj7u3bsHLS0t3u+migkhkJubi7S0NJiamkrBlIiIqLwYhF6DTCaDjY0NkpOTcf36dVWXo7ZMTU1hbW2t6jKIiOgtxCD0mrS1teHo6MjTYyqipaXFI0FERFRpDEIKoKGhAV1dXVWXQURERBVU7Qe13Lx5E76+vnB2dkazZs2wceNGVZdEREREb4hqf0SoRo0aCAsLg7u7O1JTU+Hp6Ylu3brBwMBA1aURERGRilX7IGRjYwMbGxsAgLW1NSwtLZGRkcEgRERUhoQmTorrzDdccX0RKYHKT40dPHgQ/v7+sLW1hUwmw7Zt20q0CQ8Ph4ODA3R1ddGqVSscP368Uts6deoUCgsLYWdn95pVExERUXWg8iCUk5MDNzc3hIeX/lfDhg0bEBoaihkzZuD06dNwc3ODn58f0tLSpDbu7u5o2rRpiZ/bt29LbTIyMjBkyBAsX75c6ftEREREbweVnxrr2rUrunbtWuby7777DiNHjsTQoUMBAMuWLcOOHTuwcuVKTJkyBQAQFxf30m3k5eWhV69emDJlCtq0afPKtnl5edJ0VlZWOfeEiIiI3jYqPyL0Mvn5+Th16hQ6d+4szdPQ0EDnzp1x5MiRcvUhhEBQUBA6duyIwYMHv7L9/PnzYWJiIv3wNBoREVH19UYHofT0dBQWFsLKykpuvpWVFVJTU8vVR2xsLDZs2IBt27bB3d0d7u7uOH/+fJntp06diocPH0o/N2/efK19ICIiojeXyk+NKVu7du1QVFRU7vY6OjrQ0dFRYkVERET0pnijjwhZWlpCU1MTd+/elZt/9+5dPluKiIiIXtsbHYS0tbXh6emJ6OhoaV5RURGio6Ph7e2twsqIiIioOlD5qbHs7GxcvXpVmk5OTkZcXBzMzc1Rt25dhIaGIjAwEF5eXmjZsiXCwsKQk5MjXUWmLOHh4QgPD0dhYaFSt0NEpAiuq1wV1tcfCuuJ6M2n8iB08uRJvPPOO9J0aGgoACAwMBCRkZEYMGAA7t27h+nTpyM1NRXu7u6IiooqMYBa0YKDgxEcHIysrCyYmJgodVtERESkGioPQr6+vhBCvLRNSEgIQkJCqqgiIiIiUhdv9BghIiIiImViECIiIiK1ValTY8nJyTh06BCuX7+O3Nxc1KxZEx4eHvD29oaurq6iayQiIiJSigoFobVr12Lx4sU4efIkrKysYGtrCz09PWRkZCApKQm6uroYNGgQJk+eDHt7e2XVXCV41RgREVH1V+5TYx4eHvjhhx8QFBSE69ev486dOzh16hT+/fdfxMfHIysrC3/++SeKiorg5eWFjRs3KrNupQsODkZ8fDxOnDih6lKIiIhIScp9ROjrr7+Gn59fmct1dHTg6+sLX19fzJs3DykpKYqoj4iIiEhpyh2EXhaCXmRhYQELC4tKFURERERUVSo1WPr06dPQ0tKCq+uzO5n++eefiIiIgLOzM2bOnAltbW2FFklEb56EJk4K68vpUoLC+iIiqohKXT7/ySef4MqVKwCAa9eu4cMPP4S+vj42btyIzz//XKEFEhERESlLpYLQlStX4O7uDgDYuHEjOnTogHXr1iEyMhKbN29WZH0qEx4eDmdnZ7Ro0ULVpRAREZGSVCoICSFQVFQEANi7dy+6desGALCzs0N6erriqlMhXjVGRERU/VUqCHl5eWHu3LlYvXo1Dhw4gO7duwN4dqNFZT8MlYiIiEhRKhWEwsLCcPr0aYSEhOCLL75Aw4YNAQCbNm1CmzZtFFogERERkbJU6qqxZs2a4fz58yXmL1iwAJqamq9dFBEREVFVqFQQel52drY0XqiYlpbW63ZLREREpHSVOjWWnJyM7t27w8DAACYmJjAzM4OZmRlMTU1hZmam6BqJiIiIlKJSR4Q++ugjCCGwcuVKWFlZQSaTKbouIiIiIqWrVBA6e/YsTp06hcaNGyu6njcGnz5PRERU/VXq1FiLFi1w8+ZNRdfyRuF9hIiIiKq/Sh0R+uWXXzBq1Cj8999/aNq0aYnB0c2aNVNIcURERETKVKkgdO/ePSQlJWHo0KHSPJlMBiEEZDIZTycRERHRW6FSQWjYsGHw8PDA77//zsHSRERE9NaqVBC6fv06tm/fLt1RmoiIiOhtVKnB0h07dsTZs2cVXQsRERFRlarUESF/f398+umnOH/+PFxdXUsMlu7Zs6dCiiMiIiJSpkoFoVGjRgEAZs+eXWIZB0sTERHR26JSp8aKiorK/KkuISg8PBzOzs5o0aKFqkshIiIiJalUEFIHvKEiERFR9VfuILR+/fpyd3rz5k3ExsZWqiAiIiKiqlLuILR06VI4OTnh22+/RUJCQonlDx8+xM6dOzFw4EA0b94c9+/fV2ihRERERIpW7sHSBw4cwPbt27FkyRJMnToVBgYGsLKygq6uLh48eIDU1FRYWloiKCgIFy5cgJWVlTLrJiIiInptFbpqrGfPnujZsyfS09Px77//4vr163j8+DEsLS3h4eEBDw8PaGhw2BERERG9HSp1+bylpSV69eql4FKIiIiIqlalghARkSKFj4pRWF/ByzoqrC8iqv54HouIiIjUFoMQERERqS0GISIiIlJbDEJl4CM2iIiIqr9yD5YODQ0td6ffffddpYp5kwQHByM4OBhZWVkwMTFRdTlEVB3NVOB3S726iuuLSI2UOwidOXNGbvr06dMoKChA48aNAQBXrlyBpqYmPD09FVshERERkZKUOwjt27dP+vd3330HIyMjrFq1CmZmZgCABw8eYOjQoWjfvr3iqyQiIiJSgkqNEVq0aBHmz58vhSAAMDMzw9y5c7Fo0SKFFUdERESkTJW6oWJWVhbu3btXYv69e/fw6NGj1y6KiOhN5TBlh8L6StFVWFdEVEmVOiLUu3dvDB06FFu2bMGtW7dw69YtbN68GcOHD0efPn0UXSMRERGRUlTqiNCyZcswceJEDBw4EE+fPn3WUY0aGD58OBYsWKDQAomIiIiUpVJBSF9fHz/99BMWLFiApKQkAECDBg1gYGCg0OKIiIiIlOm1HrpqYGCAZs2aKaoWIiIioipV6SB08uRJ/PHHH7hx4wby8/Pllm3ZsuW1CyMiIiJStkoNll6/fj3atGmDhIQEbN26FU+fPsXFixcRExPDuzATERHRW6NSQeirr77C999/j7/++gva2tpYvHgxLl26hP79+6NuXd7mnYiIiN4OlQpCSUlJ6N69OwBAW1sbOTk5kMlk+PTTT7F8+XKFFkhERESkLJUKQmZmZtKNE2vXro0LFy4AADIzM5Gbm6u46lSIT58nIiKq/ioVhDp06IB//vkHANCvXz+MHz8eI0eOREBAADp16qTQAlUlODgY8fHxOHHihKpLISIiIiWp1FVjP/74I548eQIA+OKLL6ClpYXDhw+jb9++mDZtmkILJCIiIlKWSgUhc3Nz6d8aGhqYMmWKwgoiIiIiqiqVOjUGPBswPW3aNAQEBCAtLQ0AsGvXLly8eFFhxREREREpU6WC0IEDB+Dq6opjx45hy5YtyM7OBgCcPXsWM2bMUGiBRERERMpSqSA0ZcoUzJ07F//88w+0tbWl+R07dsTRo0cVVhwRERGRMlUqCJ0/fx69e/cuMb9WrVpIT09/7aKIiIiIqkKlgpCpqSnu3LlTYv6ZM2dQu3bt1y6KiIiIqCpUKgh9+OGHmDx5MlJTUyGTyVBUVITY2FhMnDgRQ4YMUXSNREREREpR6WeNNWnSBHZ2dsjOzoazszM6dOiANm3a8D5CRERE9Nao1H2EtLW1sWLFCnz55Ze4cOECsrOz4eHhAUdHR0XXR0RERKQ0lQpCxerWrcunzRMREdFbq1JBqLCwEJGRkYiOjkZaWhqKiorklsfExCikOCIiIiJlqlQQGj9+PCIjI9G9e3c0bdoUMplM0XURERERKV2lgtD69evxxx9/oFu3boquh4iIiKjKVOqqMW1tbTRs2FDRtRARERFVqUoFoc8++wyLFy+GEELR9RARERFVmXKfGuvTp4/cdExMDHbt2gUXFxdoaWnJLduyZYtiqiMiIiJSonIHIRMTE7np0p41RkRERPQ2KXcQioiIAAAUFBRg3bp16NKlC6ytrZVWmKqFh4cjPDwchYWFqi6FiIiIlKTCY4Rq1KiBUaNGIS8vTxn1vDGCg4MRHx+PEydOqLoUIiIiUpJKDZZu2bIlzpw5o+haiIiIiKpUpe4jNGbMGHz22We4desWPD09YWBgILe8WbNmCimOiIiISJkqFYQ+/PBDAMC4ceOkeTKZDEIIyGQyjqshIiKit0KlglBycrKi6yAiIiKqcpUKQvb29oqug4iIiKjKVWqwNACsXr0abdu2ha2tLa5fvw4ACAsLw59//qmw4oiIiIiUqVJBaOnSpQgNDUW3bt2QmZkpjQkyNTVFWFiYIusjIiIiUppKBaElS5ZgxYoV+OKLL6CpqSnN9/Lywvnz5xVWHBEREZEyVSoIJScnw8PDo8R8HR0d5OTkvHZRRERERFWhUkGoXr16iIuLKzE/KioKTk5Or1sTERERUZWo1FVjoaGhCA4OxpMnTyCEwPHjx/H7779j/vz5+OWXXxRdIxEREZFSVCoIjRgxAnp6epg2bRpyc3MxcOBA2NraYvHixdLNFomIiIjedJUKQgAwaNAgDBo0CLm5ucjOzkatWrUUWRcRERGR0lVqjNDcuXOlu0vr6+szBBEREdFbqVJBaOPGjWjYsCHatGmDn376Cenp6Yqui4iIiEjpKhWEzp49i3PnzsHX1xcLFy6Era0tunfvjnXr1iE3N1fRNRIREREpRaUfseHi4oKvvvoK165dw759++Dg4IAJEybA2tpakfURERERKU2lg9DzDAwMoKenB21tbTx9+lQRXRIREREpXaWDUHJyMubNmwcXFxd4eXnhzJkzmDVrFlJTUxVZHxEREZHSVOry+datW+PEiRNo1qwZhg4dioCAANSuXVvRtREREREpVaWCUKdOnbBy5Uo4Ozsruh4iIiKiKlOpIDRv3jwAkC6bt7S0VFxFRCRvpokC+3qouL6IiKqBCo8RyszMRHBwMCwtLWFlZQUrKytYWloiJCQEmZmZSiiRiIiISDkqdEQoIyMD3t7e+O+//zBo0CDpSfPx8fGIjIxEdHQ0Dh8+DDMzM6UUS0RERKRIFQpCs2fPhra2NpKSkmBlZVViWZcuXTB79mx8//33Ci2SiIiISBkqdGps27ZtWLhwYYkQBADW1tb49ttvsXXrVoUVR0RERKRMFQpCd+7cgYuLS5nLmzZtyvsIERER0VujQkHI0tISKSkpZS5PTk6Gubn569akUJmZmfDy8oK7uzuaNm2KFStWqLokIiIiekNUaIyQn58fvvjiC/zzzz/Q1taWW5aXl4cvv/wS7733nkILfF1GRkY4ePAg9PX1kZOTg6ZNm6JPnz6wsLBQdWlERESkYhUeLO3l5QVHR0cEBwejSZMmEEIgISEBP/30E/Ly8rB69Wpl1Vopmpqa0NfXB/AsrAkhIIRQcVVERET0JqjQqbE6dergyJEjcHZ2xtSpU9GrVy/07t0bX3zxBZydnREbGws7O7sKFXDw4EH4+/vD1tYWMpkM27ZtK9EmPDwcDg4O0NXVRatWrXD8+PEKbSMzMxNubm6oU6cOJk2axBtAEhEREYBK3Fm6Xr162LVrFx48eIDExEQAQMOGDSs9NignJwdubm4YNmwY+vTpU2L5hg0bEBoaimXLlqFVq1YICwuDn58fLl++jFq1agEA3N3dUVBQUGLdPXv2wNbWFqampjh79izu3r2LPn364IMPPij1yjciIiJSL5V6xAYAmJmZoWXLlq9dQNeuXdG1a9cyl3/33XcYOXIkhg4dCgBYtmwZduzYgZUrV2LKlCkAgLi4uHJty8rKCm5ubjh06BA++OCDUtvk5eUhLy9Pms7KyirnnhAREdHbpsKP2KhK+fn5OHXqFDp37izN09DQQOfOnXHkyJFy9XH37l08evQIAPDw4UMcPHgQjRs3LrP9/PnzYWJiIv1U9FQfERERvT0qfUSoKqSnp6OwsLDEaSwrKytcunSpXH1cv34dH3/8sTRIeuzYsXB1dS2z/dSpUxEaGipNZ2VlMQxRhTlM2aGwvlJ0FdYVERG94I0OQorQsmXLcp86AwAdHR3o6OgoryAiIiJ6Y7zRp8YsLS2hqamJu3fvys2/e/curK2tVVQVERERVRdvdBDS1taGp6cnoqOjpXlFRUWIjo6Gt7e3CisjIiKi6kDlp8ays7Nx9epVaTo5ORlxcXEwNzdH3bp1ERoaisDAQHh5eaFly5YICwtDTk6OdBWZsoSHhyM8PByFhYVK3Q4RERGpjsqD0MmTJ/HOO+9I08UDlQMDAxEZGYkBAwbg3r17mD59OlJTU+Hu7o6oqCil3wcoODgYwcHByMrKgomJiVK3RURERKqh8iDk6+v7ykdehISEICQkpIoqIiIiInXxRo8RIiIiIlImBiEiIiJSWwxCREREpLYYhMoQHh4OZ2dntGjRQtWlEBERkZIwCJUhODgY8fHxOHHihKpLISIiIiVhECIiIiK1pfLL54mo6riuKvuBwxX1h8J6IiJSHR4RIiIiIrXFIERERERqi0GoDLxqjIiIqPpjECoDrxojIiKq/hiEiIiISG0xCBEREZHaYhAiIiIitcUgRERERGqLQYiIiIjUFoMQERERqS0GoTLwPkJERETVH4NQGXgfISIiouqPQYiIiIjUFoMQERERqS0GISIiIlJbDEJERESkthiEiIiISG0xCBEREZHaYhAiIiIitcUgVAbeUJGIiKj6YxAqA2+oSEREVP0xCBEREZHaYhAiIiIitcUgRERERGqLQYiIiIjUFoMQERERqS0GISIiIlJbDEJERESkthiEiIiISG0xCBEREZHaYhAqAx+xQUREVP0xCJWBj9ggIiKq/hiEiIiISG0xCBEREZHaYhAiIiIitcUgRERERGqLQYiIiIjUFoMQERERqS0GISIiIlJbDEJERESkthiEiIiISG0xCBEREZHaYhAiIiIitcUgRERERGqLQagMfPo8ERFR9ccgVAY+fZ6IiKj6YxAiIiIitcUgRERERGqLQYiIiIjUFoMQERERqS0GISIiIlJbDEJERESkthiEiIiISG0xCBEREZHaYhAiIiIitcUgRERERGqLQYiIiIjUFoMQERERqS0GISIiIlJbDEJERESkthiEiIiISG0xCBEREZHaYhAiIiIitcUgRERERGqLQagM4eHhcHZ2RosWLVRdChERESkJg1AZgoODER8fjxMnTqi6FCIiIlISBiEiIiJSWwxCREREpLYYhIiIiEhtMQgRERGR2mIQIiIiIrXFIERERERqi0GIiIiI1BaDEBEREaktBiEiIiJSWwxCREREpLYYhIiIiEhtMQgRERGR2mIQIiIiIrXFIERERERqi0GIiIiI1BaDEBEREaktBiEiIiJSWwxCREREpLYYhIiIiEhtMQgRERGR2mIQIiIiIrXFIERERERqi0GIiIiI1BaDEBEREaktBiEiIiJSWwxCREREpLYYhIiIiEhtqU0Qys3Nhb29PSZOnKjqUoiIiOgNoTZBaN68eWjdurWqyyAiIqI3iFoEocTERFy6dAldu3ZVdSlERET0BlF5EDp48CD8/f1ha2sLmUyGbdu2lWgTHh4OBwcH6OrqolWrVjh+/HiFtjFx4kTMnz9fQRUTERFRdaHyIJSTkwM3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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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kAJoaGhAV1dX1WUQERFRGXFSCxEREaktBiEiIiJSWwxCREREpLYYhIiIiEhtMQgRERGR2mIQIiIiIrXFIERERERqi0GIiIiI1BaDEBEREaktBiEiIiJSWwxCxQgPD4ezszMaNmyo6lKIiIhISRiEihEcHIzY2FicOnVK1aUQERGRkjAIERERkdpiECIiIiK1xSBEREREaotBiIiIiNRWpfI8KTExEYcPH8bNmzeRmZkJKysreHp6omnTptDV1VV0jURERERKUaYgtHbtWixcuBCnT5+GtbU17OzsoKenh9TUVCQkJEBXVxd9+/bFuHHjUL16dWXVTERERKQQpQ5Cnp6e0NbWRlBQEDZv3gx7e3u59VlZWTh27BjWr18Pb29vLFmyBD169FB4wURERESKUuog9P3338PPz6/Y9To6OvD19YWvry9mzZqFpKQkRdRHREREpDSlDkJvCkGvs7CwgIWFRbkKIiIiInpXynXW2NmzZ3Hx4kXp8V9//YWuXbvif//7H7KzsxVWHBEREZEylSsIffnll7h27RoA4MaNG+jduzf09fWxceNGfPPNNwotkIiIiEhZyhWErl27Bg8PDwDAxo0b0apVK6xbtw4RERHYvHmzIusjIiIiUppyBSEhBPLz8wEA+/btQ8eOHQEA9vb2SElJUVx1REREREpUriDk7e2NmTNnYs2aNTh48CA6deoE4OWFFq2trRVaIBEREZGylCsIhYWF4ezZswgJCcG3336L2rVrAwA2bdqEZs2aKbRAIiIiImUp1y026tevL3fWWIG5c+dCU1PzrYsiIiIiehfKFYRelZGRIc0XKqClpfW23RIRUQUQPjRaYX0FL2ujsL6ICpTr0FhiYiI6deoEAwMDmJiYwMzMDGZmZjA1NYWZmZmiayQiIiJSinKNCH3++ecQQmDlypWwtraGTCZTdF1ERERESleuIHT+/HmcOXMGdevWVXQ9RERERO9MuQ6NNWzYELdv31Z0LURERETvVLlGhH755RcMHToU//33H1xdXQtNjq5fv75CiiMiIiJSpnIFoYcPHyIhIQEDBgyQlslkMgghIJPJkJeXp7AC39bt27fRr18/JCcno1KlSpg0aRJ69Oih6rKIiIjoPVCuIDRw4EB4enrijz/+eO8nS1eqVAlhYWHw8PDA/fv34eXlhY4dO8LAwEDVpREREZGKlSsI3bx5E9u3b5euKP0+s7W1ha2tLQDAxsYGlpaWSE1NZRAiIiKi8k2WbtOmDc6fP6+QAg4dOgR/f3/Y2dlBJpNh27ZthdqEh4fDwcEBurq6aNy4MU6ePFmubZ05cwZ5eXmwt7d/y6qJiIioIijXiJC/vz+++uorXLx4EW5uboUmS3fp0qXUfT179gzu7u4YOHAgPv3000LrN2zYgNDQUCxbtgyNGzdGWFgY/Pz8cPXqVVSuXBkA4OHhgdzc3ELP3bNnD+zs7AAAqamp6N+/P1asWPHGerKyspCVlSU9Tk9PL/W+EBER0YelXEFo6NChAIDp06cXWlfWydIdOnRAhw4dil2/YMECDBkyRJqYvWzZMuzYsQMrV67E+PHjAQAxMTFv3EZWVha6du2K8ePHl3hT2NmzZ2PatGmlrp+IiIg+XOU6NJafn1/sjyLPGMvOzsaZM2fQrl27/ytYQwPt2rXDsWPHStWHEAJBQUFo06YN+vXrV2L7CRMm4MmTJ9IPr5dERERUcZUrCL0rKSkpyMvLg7W1tdxya2tr3L9/v1R9HDlyBBs2bMC2bdvg4eEBDw8PXLx4sdj2Ojo6MDY2lvshIiKiiqnUh8bWr1+P3r17l6rt7du3cevWLTRv3rzchSlKixYtkJ+fr+oyiIiI6D1U6hGhpUuXwsnJCXPmzEFcXFyh9U+ePMHOnTvRp08fNGjQAI8ePXrr4iwtLaGpqYkHDx7ILX/w4AFsbGzeun8iIiJSb6UOQgcPHsQPP/yAvXv3wtXVFcbGxnB0dISbmxuqVq0KCwsLDBw4ENWqVcOlS5fKdOZYcbS1teHl5YWoqChpWX5+PqKiotC0adO37v9NwsPD4ezsjIYNGyp1O0RERKQ6ZTprrEuXLujSpQtSUlLw77//4ubNm3j+/DksLS3h6ekJT09PaGiUbdpRRkYGrl+/Lj1OTExETEwMzM3NUa1aNYSGhiIwMBDe3t5o1KgRwsLC8OzZM7nbeyhDcHAwgoODkZ6eDhMTE6Vui4iIiFSjXKfPW1paomvXrgop4PTp02jdurX0ODQ0FAAQGBiIiIgI9OrVCw8fPsTkyZNx//59eHh4IDIystAEaiIiIqKyKlcQUiRfX18IId7YJiQkBCEhIe+oIiIiIlIX7/Xp80RERETKxCBUDE6WJiIiqvgYhIoRHByM2NhYnDp1StWlEBERkZIwCBEREZHaKvVk6YKzuUpjwYIF5SqGiIjKx221m8L6uhhY/G2IiCqaUgehc+fOyT0+e/YscnNzUbduXQDAtWvXoKmpCS8vL8VWSERERKQkpQ5C+/fvl/69YMECGBkZYfXq1TAzMwMAPH78GAMGDEDLli0VX6UKhIeHIzw8HHl5eaouhYiIiJSkXHOE5s+fj9mzZ0shCADMzMwwc+ZMzJ8/X2HFqRInSxMREVV85QpC6enpePjwYaHlDx8+xNOnT9+6KCIiIqJ3oVxBqFu3bhgwYAC2bNmCO3fu4M6dO9i8eTMGDRqETz/9VNE1EhERESlFuW6xsWzZMowZMwZ9+vRBTk7Oy44qVcKgQYMwd+5chRZIREREpCzlCkL6+vpYsmQJ5s6di4SEBABArVq1YGBgoNDiiIiIiJTprW66amBggPr16yuqFiIiIqJ3qtxB6PTp0/jzzz9x69YtZGdny63bsmXLWxemajx9nojUVVw9J8V15huuuL6IlKBck6XXr1+PZs2aIS4uDlu3bkVOTg4uX76M6OhomJiYKLpGleDp80RERBVfuYLQd999hx9//BF///03tLW1sXDhQly5cgU9e/ZEtWrVFF0jERERkVKUKwglJCSgU6dOAABtbW08e/YMMpkMX331FZYvX67QAomIiIiUpVxByMzMTLpwYpUqVXDp0iUAQFpaGjIzMxVXHREREZESlWuydKtWrbB37164ubmhR48eGDVqFKKjo7F37160bdtW0TUSERERKUW5gtDixYvx4sULAMC3334LLS0tHD16FN27d8fEiRMVWiARERGRspQrCJmbm0v/1tDQwPjx4xVWEBEREdG7Uq45QsDLCdMTJ05EQEAAkpOTAQC7du3C5cuXFVacKoWHh8PZ2RkNGzZUdSlERESkJOUKQgcPHoSbmxtOnDiBLVu2ICMjAwBw/vx5TJkyRaEFqgqvI0RERFTxlSsIjR8/HjNnzsTevXuhra0tLW/Tpg2OHz+usOKIiIiIlKlcQejixYvo1q1boeWVK1dGSkrKWxdFRERE9C6UKwiZmpri3r17hZafO3cOVapUeeuiiIiIiN6FcgWh3r17Y9y4cbh//z5kMhny8/Nx5MgRjBkzBv3791d0jURERERKUe57jdWrVw/29vbIyMiAs7MzWrVqhWbNmvE6QkRERPTBKNd1hLS1tbFixQpMmjQJly5dQkZGBjw9PeHo6Kjo+oiIiIiUplxBqEC1atV4t3kiovKaaqK4vmrwu5ioPMoVhPLy8hAREYGoqCgkJycjPz9fbn10dLRCilOl8PBwhIeHIy8vT9WlEBERkZKUKwiNGjUKERER6NSpE1xdXSGTyRRdl8oFBwcjODgY6enpMDFR4F9tRPRBcxi/Q2F9JekqrCsiKqdyBaH169fjzz//RMeOHRVdDxEREdE7U66zxrS1tVG7dm1F10JERET0TpVrROjrr7/GwoULsXjx4gp5WIyI3q3woYqbVxi8rI3C+iKiiq/UQejTTz+VexwdHY1du3bBxcUFWlpacuu2bNmimOqIiIiIlKjUQej1CcNF3WuMiIiI6ENS6iC0atUqAEBubi7WrVuH9u3bw8bGRmmFERERESlbmSdLV6pUCUOHDkVWVpYy6iEiIiJ6Z8p11lijRo1w7tw5RddCRERE9E6V66yx4cOH4+uvv8adO3fg5eUFAwMDufX169dXSHFEREREylSuINS7d28AwMiRI6VlMpkMQgjIZDLeloKIiIg+COUKQomJiYqu473De40RERFVfOUKQtWrV1d0He8d3muM6M3i6jkprjPfcMX1RURUBuWaLA0Aa9asQfPmzWFnZ4ebN28CAMLCwvDXX38prDgiIiIiZSpXEFq6dClCQ0PRsWNHpKWlSYePTE1NERYWpsj6iIiIiJSmXEFo0aJFWLFiBb799ltoampKy729vXHx4kWFFUdERESkTOUKQomJifD09Cy0XEdHB8+ePXvrooiIiIjehXIFoRo1aiAmJqbQ8sjISDg5KXACJREREZESleussdDQUAQHB+PFixcQQuDkyZP4448/MHv2bPzyyy+KrpGIiIhIKcoVhAYPHgw9PT1MnDgRmZmZ6NOnD+zs7LBw4ULpYotERERE77tyBSEA6Nu3L/r27YvMzExkZGSgcuXKiqyLiIiISOnKNUdo5syZ0tWl9fX1GYKIiIjog1SuILRx40bUrl0bzZo1w5IlS5CSkqLouoiIiIiUrlxB6Pz587hw4QJ8fX0xb9482NnZoVOnTli3bh0yMzMVXSMRERGRUpT7FhsuLi747rvvcOPGDezfvx8ODg4YPXo0bGxsFFkfERERkdKUOwi9ysDAAHp6etDW1kZOTo4iuiQiIiJSunIHocTERMyaNQsuLi7w9vbGuXPnMG3aNNy/f1+R9REREREpTblOn2/SpAlOnTqF+vXrY8CAAQgICECVKlUUXRsRERGRUpUrCLVt2xYrV66Es7Ozout5b4SHhyM8PBx5eXmqLoWIiIiUpFxBaNasWQAgnTZvaWmpuIreE8HBwQgODkZ6ejpMTExUXQ4REREpQZnnCKWlpSE4OBiWlpawtraGtbU1LC0tERISgrS0NCWUSERERKQcZRoRSk1NRdOmTfHff/+hb9++0p3mY2NjERERgaioKBw9ehRmZmZKKZaIiIhIkcoUhKZPnw5tbW0kJCTA2tq60Lr27dtj+vTp+PHHHxVaJBEREZEylOnQ2LZt2zBv3rxCIQgAbGxsMGfOHGzdulVhxREREREpU5mC0L179+Di4lLseldXV15HiIiIiD4YZQpClpaWSEpKKnZ9YmIizM3N37YmIiIioneiTHOE/Pz88O2332Lv3r3Q1taWW5eVlYVJkybh448/VmiBRGpvqgIv3zD1ieL6IiKqAMo8Wdrb2xuOjo4IDg5GvXr1IIRAXFwclixZgqysLKxZs0ZZtRIREREpVJmCUNWqVXHs2DEMHz4cEyZMgBACACCTyfDRRx9h8eLFsLe3V0qhRERERIpW5itL16hRA7t27cLjx48RHx8PAKhduzbnBhEREdEHp1y32AAAMzMzNGrUSJG1EBEREb1TZb7FBhEREVFFwSBEREREaotBiIiIiNQWgxARERGpLQYhIiIiUlsMQkRERKS2GISIiIhIbTEIERERkdpiECIiIiK1xSBEREREaotBiIiIiNRWhQ9CaWlp8Pb2hoeHB1xdXbFixQpVl0RERETviXLfdPVDYWRkhEOHDkFfXx/Pnj2Dq6srPv30U1hYWKi6NCIiIlKxCj8ipKmpCX19fQBAVlYWhBAQQqi4KiIiInofqDwIHTp0CP7+/rCzs4NMJsO2bdsKtQkPD4eDgwN0dXXRuHFjnDx5skzbSEtLg7u7O6pWrYqxY8fC0tJSQdUTERHRh0zlQejZs2dwd3dHeHh4kes3bNiA0NBQTJkyBWfPnoW7uzv8/PyQnJwstSmY//P6z927dwEApqamOH/+PBITE7Fu3To8ePDgnewbERERvd9UPkeoQ4cO6NChQ7HrFyxYgCFDhmDAgAEAgGXLlmHHjh1YuXIlxo8fDwCIiYkp1basra3h7u6Ow4cP47PPPiuyTVZWFrKysqTH6enppdwTIiIi+tCofEToTbKzs3HmzBm0a9dOWqahoYF27drh2LFjperjwYMHePr0KQDgyZMnOHToEOrWrVts+9mzZ8PExET6sbe3f7udICIiovfWex2EUlJSkJeXB2tra7nl1tbWuH//fqn6uHnzJlq2bAl3d3e0bNkSI0aMgJubW7HtJ0yYgCdPnkg/t2/ffqt9ICIioveXyg+NKVujRo1KfegMAHR0dKCjo6O8goiIiOi98V4HIUtLS2hqahaa3PzgwQPY2NioqCqiD5fb6uJHQ8vqT4X1RESkOu/1oTFtbW14eXkhKipKWpafn4+oqCg0bdpUqdsODw+Hs7MzGjZsqNTtEBERkeqofEQoIyMD169flx4nJiYiJiYG5ubmqFatGkJDQxEYGAhvb280atQIYWFhePbsmXQWmbIEBwcjODgY6enpMDExUeq2iIiISDVUHoROnz6N1q1bS49DQ0MBAIGBgYiIiECvXr3w8OFDTJ48Gffv34eHhwciIyMLTaAmIiIiKiuVByFfX98Sb3kREhKCkJCQd1QRERERqYv3eo4QERERkTIxCBWDk6WJiIgqPgahYgQHByM2NhanTp1SdSlERESkJAxCREREpLYYhIiIiEhtqfysMaKKyGH8DoX1laSrsK6IiOg1HBEiIiIitcUgVAyeNUZERFTxMQgVg2eNERERVXwMQkRERKS2GISIiIhIbTEIERERkdpiECIiIiK1xSBUDJ41RkREVPExCBWDZ40RERFVfAxCREREpLYYhIiIiEhtMQgRERGR2mIQIiIiIrXFIERERERqi0GIiIiI1BaDUDF4HSEiIqKKj0GoGLyOEBERUcXHIERERERqi0GIiIiI1BaDEBEREaktBiEiIiJSWwxCREREpLYYhIiIiEhtMQgRERGR2mIQKgYvqEhERFTxMQgVgxdUJCIiqvgYhIiIiEhtMQgRERGR2mIQIiIiIrXFIERERERqi0GIiIiI1BaDEBEREaktBiEiIiJSWwxCREREpLYYhIiIiEhtMQgRERGR2mIQKgbvNUZERFTxMQgVg/caIyIiqvgYhIiIiEhtMQgRERGR2mIQIiIiIrXFIERERERqi0GIiIiI1BaDEBEREaktBiEiIiJSWwxCREREpLYYhIiIiEhtMQgRERGR2mIQIiIiIrXFIERERERqi0GIiIiI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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "# read benchmark results from CSV\n", + "import pandas as pd\n", + "import numpy as np\n", + "from tabulate import tabulate\n", + "\n", + "df = pd.read_csv(\"benchmark_results.csv\")\n", + "\n", + "# plot benchmark results\n", + "\n", + "modes = df['check mode'].unique()\n", + "libs = df['library'].unique()\n", + "n_libs = len(libs)\n", + "\n", + "# find a small positive epsilon to replace non-positive values (log scale can't handle <= 0)\n", + "pos_min = df[df['overhead (ms)'] > 0]['overhead (ms)'].min()\n", + "epsilon = 1e-4\n", + "\n", + "for add_args in df['additional args'].unique():\n", + " x = np.arange(len(modes))\n", + " width = 0.8 / n_libs\n", + "\n", + " for i, lib in enumerate(libs):\n", + " lib_df = df[(df['library'] == lib) & (df['additional args'] == add_args)]\n", + " vals = lib_df.set_index('check mode').reindex(modes)['overhead (ms)']\n", + " vals_plot = vals.copy().astype(float).where(vals > 0, epsilon) # replace <=0 with epsilon\n", + " plt.bar(x + i * width, vals_plot, width=width, label=lib)\n", + "\n", + " plt.xticks(x + width * (n_libs - 1) / 2, modes)\n", + " plt.xlabel(\"Check Mode\")\n", + " plt.ylabel(\"Overhead (ms)\")\n", + " plt.yscale('log')\n", + " plt.title(f\"Decorator Overhead (additional args = {add_args})\")\n", + " plt.legend()\n", + " plt.savefig(f\"assets/benchmark_overhead_additional_args_{add_args}.png\")\n", + " plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "8c99f086", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "| id | lib | num_args | method | s/it | rel | overhead |\n", + "|------|--------|------------|---------------|---------|---------|------------|\n", + "| 8 | torch | 100 | not_annotated | 0.02975 | 100.00% | 0.0e+00 |\n", + "| 9 | torch | 100 | never | 0.03106 | 104.39% | 1.3e-06 |\n", + "| 10 | torch | 100 | once | 0.03391 | 113.96% | 4.2e-06 |\n", + "| 11 | torch | 100 | always | 0.28250 | 949.49% | 2.5e-04 |\n", + "| 20 | numpy | 100 | not_annotated | 0.15465 | 100.00% | 0.0e+00 |\n", + "| 21 | numpy | 100 | never | 0.15588 | 100.79% | 1.2e-06 |\n", + "| 22 | numpy | 100 | once | 0.15839 | 102.42% | 3.7e-06 |\n", + "| 23 | numpy | 100 | always | 0.38753 | 250.59% | 2.3e-04 |\n", + "| 32 | dask | 100 | not_annotated | 1.50759 | 100.00% | 0.0e+00 |\n", + "| 33 | dask | 100 | never | 1.50902 | 100.09% | 1.4e-06 |\n", + "| 34 | dask | 100 | once | 1.51694 | 100.62% | 9.4e-06 |\n", + "| 35 | dask | 100 | always | 1.79947 | 119.36% | 2.9e-04 |\n", + "| 44 | jax | 100 | not_annotated | 0.32844 | 100.00% | 0.0e+00 |\n", + "| 45 | jax | 100 | never | 0.33278 | 101.32% | 4.3e-06 |\n", + "| 46 | jax | 100 | once | 0.33702 | 102.61% | 8.6e-06 |\n", + "| 47 | jax | 100 | always | 0.60768 | 185.02% | 2.8e-04 |\n", + "| 56 | sparse | 100 | not_annotated | 0.57341 | 100.00% | 0.0e+00 |\n", + "| 57 | sparse | 100 | never | 0.57755 | 100.72% | 4.1e-06 |\n", + "| 58 | sparse | 100 | once | 0.58779 | 102.51% | 1.4e-05 |\n", + "| 59 | sparse | 100 | always | 0.84208 | 146.86% | 2.7e-04 |\n" + ] + } + ], + "source": [ + "df = pd.read_csv(\"benchmark_results.csv\")\n", + "print(tabulate(df[df[\"additional args\"] == 100], tablefmt=\"github\", floatfmt=(None, None, None, None, \".5f\", \".2%\", \".1e\"), headers=[\"id\", \"lib\", \"num_args\", \"method\", \"s/it\", \"rel\", \"overhead\"])) # type: ignore" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tsa-test-p312", + "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.12.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/src/tensor_shape_assert/__init__.py b/src/tensor_shape_assert/__init__.py index 8af932d..434d47c 100644 --- a/src/tensor_shape_assert/__init__.py +++ b/src/tensor_shape_assert/__init__.py @@ -2,5 +2,5 @@ check_tensor_shapes, get_shape_variables, assert_shape_here, set_global_check_mode ) -from .types import ShapedTensor, ShapedTorchLiteral, ShapedNumpyLiteral +from .types import ShapedTensor, ShapedTorchLiteral, ShapedNumpyLiteral, ShapedLiteral from .types import ScalarTensor # type: ignore \ No newline at end of file diff --git a/src/tensor_shape_assert/types.py b/src/tensor_shape_assert/types.py index 3126a27..6ce315f 100644 --- a/src/tensor_shape_assert/types.py +++ b/src/tensor_shape_assert/types.py @@ -118,6 +118,15 @@ def __init__(self, *args, **kwargs): ) def __class_getitem__(cls, key): + # check if it is a tuple annotation + if isinstance(key, tuple): + if len(key) != 2: + raise TypeError( + "ShapedTensor can only be parameterized with a single " + "shape descriptor string or a tuple of (type, shape)." + ) + key = key[1] + # check if it is a literal if get_origin(key) is Literal: key = " ".join(get_args(key)) @@ -138,7 +147,7 @@ def __class_getitem__(cls, key): # torch try: - from array_api_compat import torch + import torch ShapedTorchLiteral = TypeAliasType( 'ShapedTorchLiteral', ShapedLiteral[torch.Tensor, S], @@ -150,7 +159,7 @@ def __class_getitem__(cls, key): # numpy try: - from array_api_compat import numpy + import numpy ShapedNumpyLiteral = TypeAliasType( 'ShapedNumpyLiteral', ShapedLiteral[numpy.ndarray, S], diff --git a/src/tensor_shape_assert_test.py b/src/tensor_shape_assert_test.py index 4938214..a74a59d 100644 --- a/src/tensor_shape_assert_test.py +++ b/src/tensor_shape_assert_test.py @@ -2,6 +2,7 @@ # pyright: reportReturnType=false # pyright: reportAttributeAccessIssue=false # pyright: reportOperatorIssue=false +# pyright: reportPossiblyUnboundVariable=false # mypy: ignore-errors import sys @@ -31,27 +32,10 @@ NoVariableContextExistsError, VariableConstraintError ) +from test_utils import get_library_by_name # read library to be used from env -lib = os.environ["TSA_TEST_LIBRARY"] - - -if lib == "torch" or TYPE_CHECKING: - import array_api_compat.torch as xp -elif lib == "numpy": - import array_api_compat.numpy as xp -elif lib == "cupy": - import array_api_compat.cupy as xp -elif lib == "dask": - import array_api_compat.dask.array as xp -elif lib == "jax": - import jax.numpy as xp -elif lib == "ndonnx": - import ndonnx as xp -elif lib == "sparse": - import sparse as xp -else: - raise ValueError(f"Unsupported library: {lib}") +xp = get_library_by_name(os.environ["TSA_TEST_LIBRARY"]) def library_has_types(type_names: list[str]) -> bool: diff --git a/src/tensor_shape_assert_typesafe_test.py b/src/tensor_shape_assert_typesafe_test.py index 9a4c272..22aacea 100644 --- a/src/tensor_shape_assert_typesafe_test.py +++ b/src/tensor_shape_assert_typesafe_test.py @@ -5,50 +5,35 @@ ShapedLiteral, ShapedNumpyLiteral, ShapedTorchLiteral, + ShapedTensor ) from tensor_shape_assert.wrapper import ( check_tensor_shapes, ) from tensor_shape_assert.utils import TensorShapeAssertError - +from typing_extensions import Literal as L # read library to be used from env lib = os.environ["TSA_TEST_LIBRARY"] -if lib == "numpy" or TYPE_CHECKING: - import array_api_compat.numpy as xp -elif lib == "torch": - import array_api_compat.torch as xp -elif lib == "cupy": - import array_api_compat.cupy as xp -elif lib == "dask": - import array_api_compat.dask.array as xp -elif lib == "jax": - import jax.numpy as xp -elif lib == "ndonnx": - import ndonnx as xp -elif lib == "sparse": - import sparse as xp -else: - raise ValueError(f"Unsupported library: {lib}") - - class TestAnnotationWithLiterals(unittest.TestCase): - # def test_literal_annotation_torch(self): - # if lib != "torch": - # self.skipTest("Skipping torch-specific test") + def test_literal_annotation_torch(self): + if lib != "torch": + self.skipTest("Skipping torch-specific test") - # from typing_extensions import Literal as L - # import torch + from typing_extensions import Literal as L + import torch - # @check_tensor_shapes() - # def test(x: ShapedLiteral[torch.Tensor, L["3 a 5"]]) -> ShapedLiteral[torch.Tensor, L["a 5"]]: - # return torch.sum(x, dim=0) + @check_tensor_shapes() + def test( + x: ShapedLiteral[torch.Tensor, L["3 a 5"]] + ) -> ShapedLiteral[torch.Tensor, L["a 5"]]: + return torch.sum(x, dim=0) - # test(x=torch.zeros((3, 4, 5))) - # with self.assertRaises(TensorShapeAssertError): - # test(x=torch.zeros((2, 4, 5))) + test(x=torch.zeros((3, 4, 5))) + with self.assertRaises(TensorShapeAssertError): + test(x=torch.zeros((2, 4, 5))) def test_literal_annotation_torch_alias(self): if lib != "torch": @@ -58,27 +43,32 @@ def test_literal_annotation_torch_alias(self): import torch @check_tensor_shapes() - def test(x: ShapedTorchLiteral[L["3 a 5"]]) -> ShapedTorchLiteral[L["a 5"]]: + def test( + x: ShapedTorchLiteral[L["3 a 5"]] + ) -> ShapedTorchLiteral[L["a 5"]]: return torch.sum(x, dim=0) test(x=torch.zeros((3, 4, 5))) with self.assertRaises(TensorShapeAssertError): test(x=torch.zeros((2, 4, 5))) - # def test_literal_annotation_numpy(self): - # if lib != "numpy": - # self.skipTest("Skipping numpy-specific test") + def test_literal_annotation_numpy(self): + if lib != "numpy": + self.skipTest("Skipping numpy-specific test") - # from typing_extensions import Literal as L - # import numpy as np + from typing_extensions import Literal as L + import numpy as np - # @check_tensor_shapes() - # def test(x: ShapedLiteral[np.ndarray, L["3 a 5"]]) -> ShapedLiteral[np.ndarray, L["a 5"]]: - # return np.sum(x, axis=0) + @check_tensor_shapes() + def test( + x: ShapedLiteral[np.ndarray, L["3 a 5"]] + ) -> ShapedLiteral[np.ndarray, L["a 5"]]: + return np.sum(x, axis=0) + + test(x=np.zeros((3, 4, 5))) + with self.assertRaises(TensorShapeAssertError): + test(x=np.zeros((2, 4, 5))) - # test(x=np.zeros((3, 4, 5))) - # with self.assertRaises(TensorShapeAssertError): - # test(x=np.zeros((2, 4, 5))) def test_literal_annotation_numpy_alias(self): if lib != "numpy": @@ -88,7 +78,9 @@ def test_literal_annotation_numpy_alias(self): import numpy as np @check_tensor_shapes() - def test(x: ShapedNumpyLiteral[L["3 a 5"]]) -> ShapedNumpyLiteral[L["a 5"]]: + def test( + x: ShapedNumpyLiteral[L["3 a 5"]] + ) -> ShapedNumpyLiteral[L["a 5"]]: return np.sum(x, axis=0) test(x=np.zeros((3, 4, 5))) diff --git a/src/test_utils.py b/src/test_utils.py new file mode 100644 index 0000000..a474da8 --- /dev/null +++ b/src/test_utils.py @@ -0,0 +1,19 @@ +from types import ModuleType +from importlib import import_module + +NAME_LIBRARY_MAP = { + "torch": "array_api_compat.torch", + "numpy": "array_api_compat.numpy", + "cupy": "array_api_compat.cupy", + "dask": "array_api_compat.dask.array", + "jax": "jax.numpy", + "ndonnx": "ndonnx", + "sparse": "sparse", +} + +def get_library_by_name(name: str) -> ModuleType: + try: + module_name = NAME_LIBRARY_MAP[name] + return import_module(module_name) + except KeyError: + raise ValueError(f"Unsupported library: {name}") \ No newline at end of file